about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/default.nix3
-rw-r--r--nixos/doc/manual/administration/imperative-containers.xml13
-rw-r--r--nixos/doc/manual/configuration/configuration.xml1
-rw-r--r--nixos/doc/manual/configuration/file-systems.xml6
-rw-r--r--nixos/doc/manual/configuration/ipv6-config.xml20
-rw-r--r--nixos/doc/manual/configuration/luks-file-systems.xml4
-rw-r--r--nixos/doc/manual/configuration/modularity.xml5
-rw-r--r--nixos/doc/manual/configuration/network-manager.xml6
-rw-r--r--nixos/doc/manual/configuration/user-mgmt.xml5
-rw-r--r--nixos/doc/manual/configuration/x-windows.xml10
-rw-r--r--nixos/doc/manual/configuration/xfce.xml105
-rw-r--r--nixos/doc/manual/development/option-declarations.xml48
-rw-r--r--nixos/doc/manual/development/replace-modules.xml75
-rw-r--r--nixos/doc/manual/development/sources.xml22
-rw-r--r--nixos/doc/manual/development/writing-modules.xml1
-rw-r--r--nixos/doc/manual/installation/changing-config.xml2
-rw-r--r--nixos/doc/manual/installation/installing-uefi.xml3
-rw-r--r--nixos/doc/manual/installation/installing-usb.xml9
-rw-r--r--nixos/doc/manual/installation/installing.xml5
-rw-r--r--nixos/doc/manual/installation/upgrading.xml22
-rw-r--r--nixos/doc/manual/release-notes/release-notes.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-1703.xml432
-rw-r--r--nixos/doc/manual/release-notes/rl-1709.xml80
-rw-r--r--nixos/lib/make-disk-image.nix183
-rw-r--r--nixos/lib/test-driver/Machine.pm23
-rw-r--r--nixos/lib/testing.nix11
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix28
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh17
-rwxr-xr-xnixos/maintainers/scripts/gce/create-gce.sh28
-rw-r--r--nixos/maintainers/scripts/openstack/nova-image.nix24
-rw-r--r--nixos/modules/config/fonts/fontconfig-penultimate.nix270
-rw-r--r--nixos/modules/config/fonts/fontconfig-ultimate.nix102
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix171
-rw-r--r--nixos/modules/config/fonts/fonts.nix1
-rw-r--r--nixos/modules/config/i18n.nix28
-rw-r--r--nixos/modules/config/networking.nix11
-rw-r--r--nixos/modules/config/pulseaudio.nix4
-rw-r--r--nixos/modules/config/shells-environment.nix3
-rw-r--r--nixos/modules/config/sysctl.nix4
-rw-r--r--nixos/modules/config/update-users-groups.pl72
-rw-r--r--nixos/modules/hardware/all-firmware.nix1
-rw-r--r--nixos/modules/hardware/cpu/amd-microcode.nix3
-rw-r--r--nixos/modules/hardware/cpu/intel-microcode.nix3
-rw-r--r--nixos/modules/hardware/mcelog.nix37
-rw-r--r--nixos/modules/hardware/opengl.nix13
-rw-r--r--nixos/modules/hardware/sensor/iio.nix30
-rw-r--r--nixos/modules/hardware/usb-wwan.nix26
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix5
-rw-r--r--nixos/modules/hardware/video/ati.nix2
-rw-r--r--nixos/modules/hardware/video/bumblebee.nix8
-rw-r--r--nixos/modules/hardware/video/capture/mwprocapture.nix61
-rw-r--r--nixos/modules/hardware/video/nvidia.nix23
-rw-r--r--nixos/modules/i18n/input-method/ibus.nix13
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix13
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-minimal.nix5
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-aarch64.nix61
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix10
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix6
-rw-r--r--nixos/modules/installer/scan/detected.nix2
-rw-r--r--nixos/modules/installer/scan/not-detected.nix2
-rw-r--r--nixos/modules/installer/tools/auto-upgrade.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix6
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl21
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh157
-rw-r--r--nixos/modules/installer/tools/nixos-prepare-root.sh105
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh40
-rw-r--r--nixos/modules/installer/tools/tools.nix13
-rw-r--r--nixos/modules/misc/extra-arguments.nix10
-rw-r--r--nixos/modules/misc/ids.nix31
-rw-r--r--nixos/modules/misc/locate.nix95
-rw-r--r--nixos/modules/misc/nixpkgs.nix65
-rw-r--r--nixos/modules/misc/version.nix7
-rw-r--r--nixos/modules/module-list.nix63
-rw-r--r--nixos/modules/profiles/all-hardware.nix3
-rw-r--r--nixos/modules/profiles/graphical.nix8
-rw-r--r--nixos/modules/profiles/hardened.nix62
-rw-r--r--nixos/modules/profiles/installation-device.nix15
-rw-r--r--nixos/modules/programs/adb.nix1
-rw-r--r--nixos/modules/programs/browserpass.nix26
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.nix93
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.pl2
-rw-r--r--nixos/modules/programs/environment.nix5
-rw-r--r--nixos/modules/programs/gphoto2.nix1
-rw-r--r--nixos/modules/programs/kbdlight.nix2
-rw-r--r--nixos/modules/programs/light.nix2
-rw-r--r--nixos/modules/programs/man.nix1
-rw-r--r--nixos/modules/programs/mosh.nix1
-rw-r--r--nixos/modules/programs/mtr.nix27
-rw-r--r--nixos/modules/programs/nano.nix12
-rw-r--r--nixos/modules/programs/qt5ct.nix31
-rw-r--r--nixos/modules/programs/shadow.nix16
-rw-r--r--nixos/modules/programs/slock.nix26
-rw-r--r--nixos/modules/programs/ssmtp.nix48
-rw-r--r--nixos/modules/programs/tmux.nix5
-rw-r--r--nixos/modules/programs/venus.nix2
-rw-r--r--nixos/modules/programs/vim.nix1
-rw-r--r--nixos/modules/programs/wireshark.nix42
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix66
-rw-r--r--nixos/modules/programs/zsh/zsh-syntax-highlighting.nix53
-rw-r--r--nixos/modules/programs/zsh/zsh.nix16
-rw-r--r--nixos/modules/rename.nix43
-rw-r--r--nixos/modules/security/acme.nix17
-rw-r--r--nixos/modules/security/apparmor-suid.nix3
-rw-r--r--nixos/modules/security/chromium-suid-sandbox.nix2
-rw-r--r--nixos/modules/security/dhparams.nix107
-rw-r--r--nixos/modules/security/duosec.nix3
-rw-r--r--nixos/modules/security/grsecurity.nix5
-rw-r--r--nixos/modules/security/grsecurity.xml52
-rw-r--r--nixos/modules/security/lock-kernel-modules.nix36
-rw-r--r--nixos/modules/security/pam.nix42
-rw-r--r--nixos/modules/security/pam_usb.nix8
-rw-r--r--nixos/modules/security/polkit.nix16
-rw-r--r--nixos/modules/security/setuid-wrapper.c81
-rw-r--r--nixos/modules/security/setuid-wrappers.nix146
-rw-r--r--nixos/modules/security/sudo.nix5
-rw-r--r--nixos/modules/security/wrappers/default.nix228
-rw-r--r--nixos/modules/security/wrappers/wrapper.c239
-rw-r--r--nixos/modules/services/audio/mpd.nix19
-rw-r--r--nixos/modules/services/audio/slimserver.nix69
-rw-r--r--nixos/modules/services/backup/rsnapshot.nix1
-rw-r--r--nixos/modules/services/backup/sitecopy-backup.nix106
-rw-r--r--nixos/modules/services/backup/tarsnap.nix13
-rw-r--r--nixos/modules/services/backup/znapzend.nix13
-rw-r--r--nixos/modules/services/cluster/kubernetes.nix42
-rw-r--r--nixos/modules/services/computing/boinc/client.nix2
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix51
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/worker.nix126
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix13
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix3
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/job-builder.nix26
-rw-r--r--nixos/modules/services/databases/cassandra.nix9
-rw-r--r--nixos/modules/services/databases/clickhouse.nix75
-rw-r--r--nixos/modules/services/databases/couchdb.nix30
-rw-r--r--nixos/modules/services/databases/mongodb.nix2
-rw-r--r--nixos/modules/services/databases/neo4j.nix17
-rw-r--r--nixos/modules/services/databases/openldap.nix9
-rw-r--r--nixos/modules/services/databases/stanchion.nix10
-rw-r--r--nixos/modules/services/editors/emacs.nix3
-rw-r--r--nixos/modules/services/editors/emacs.xml9
-rw-r--r--nixos/modules/services/games/factorio.nix87
-rw-r--r--nixos/modules/services/hardware/amd-hybrid-graphics.nix15
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix94
-rw-r--r--nixos/modules/services/hardware/illum.nix35
-rw-r--r--nixos/modules/services/hardware/sane.nix2
-rw-r--r--nixos/modules/services/hardware/tlp.nix3
-rw-r--r--nixos/modules/services/hardware/trezord.nix54
-rw-r--r--nixos/modules/services/hardware/udev.nix11
-rw-r--r--nixos/modules/services/logging/awstats.nix2
-rw-r--r--nixos/modules/services/logging/fluentd.nix9
-rw-r--r--nixos/modules/services/logging/graylog.nix28
-rw-r--r--nixos/modules/services/logging/journalbeat.nix76
-rw-r--r--nixos/modules/services/logging/logcheck.nix8
-rw-r--r--nixos/modules/services/logging/logstash.nix14
-rw-r--r--nixos/modules/services/mail/dovecot.nix11
-rw-r--r--nixos/modules/services/mail/exim.nix4
-rw-r--r--nixos/modules/services/mail/mail.nix2
-rw-r--r--nixos/modules/services/mail/mlmmj.nix2
-rw-r--r--nixos/modules/services/mail/offlineimap.nix1
-rw-r--r--nixos/modules/services/mail/postfix.nix2
-rw-r--r--nixos/modules/services/mail/rmilter.nix191
-rw-r--r--nixos/modules/services/mail/rspamd.nix5
-rw-r--r--nixos/modules/services/misc/apache-kafka.nix16
-rw-r--r--nixos/modules/services/misc/autorandr.nix43
-rw-r--r--nixos/modules/services/misc/bepasty.nix36
-rw-r--r--nixos/modules/services/misc/cgminer.nix2
-rwxr-xr-xnixos/modules/services/misc/confd.nix2
-rw-r--r--nixos/modules/services/misc/couchpotato.nix50
-rw-r--r--nixos/modules/services/misc/etcd.nix2
-rw-r--r--nixos/modules/services/misc/geoip-updater.nix306
-rw-r--r--nixos/modules/services/misc/gitlab.nix5
-rw-r--r--nixos/modules/services/misc/gogs.nix266
-rw-r--r--nixos/modules/services/misc/ihaskell.nix13
-rw-r--r--nixos/modules/services/misc/irkerd.nix67
-rw-r--r--nixos/modules/services/misc/jackett.nix47
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix25
-rw-r--r--nixos/modules/services/misc/mesos-master.nix21
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix98
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix60
-rw-r--r--nixos/modules/services/misc/nix-gc.nix2
-rw-r--r--nixos/modules/services/misc/nix-optimise.nix2
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix14
-rw-r--r--nixos/modules/services/misc/octoprint.nix4
-rw-r--r--nixos/modules/services/misc/plex.nix7
-rw-r--r--nixos/modules/services/misc/radarr.nix47
-rw-r--r--nixos/modules/services/misc/sonarr.nix7
-rw-r--r--nixos/modules/services/misc/ssm-agent.nix46
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix136
-rw-r--r--nixos/modules/services/misc/taskserver/doc.xml6
-rw-r--r--nixos/modules/services/misc/taskserver/helper-tool.py43
-rw-r--r--nixos/modules/services/monitoring/arbtt.nix3
-rw-r--r--nixos/modules/services/monitoring/cadvisor.nix91
-rw-r--r--nixos/modules/services/monitoring/collectd.nix2
-rw-r--r--nixos/modules/services/monitoring/das_watchdog.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana.nix21
-rw-r--r--nixos/modules/services/monitoring/graphite.nix18
-rw-r--r--nixos/modules/services/monitoring/longview.nix112
-rw-r--r--nixos/modules/services/monitoring/munin.nix25
-rw-r--r--nixos/modules/services/monitoring/netdata.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix8
-rw-r--r--nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix76
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix104
-rw-r--r--nixos/modules/services/monitoring/smartd.nix2
-rw-r--r--nixos/modules/services/monitoring/ups.nix2
-rw-r--r--nixos/modules/services/monitoring/vnstat.nix43
-rw-r--r--nixos/modules/services/network-filesystems/glusterfs.nix84
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix77
-rw-r--r--nixos/modules/services/network-filesystems/netatalk.nix2
-rw-r--r--nixos/modules/services/network-filesystems/nfsd.nix97
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix1
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix37
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix12
-rw-r--r--nixos/modules/services/networking/aiccu.nix24
-rw-r--r--nixos/modules/services/networking/aria2.nix135
-rw-r--r--nixos/modules/services/networking/asterisk.nix20
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix42
-rw-r--r--nixos/modules/services/networking/btsync.nix10
-rw-r--r--nixos/modules/services/networking/chrony.nix47
-rw-r--r--nixos/modules/services/networking/cjdns.nix5
-rw-r--r--nixos/modules/services/networking/ddclient.nix23
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix7
-rw-r--r--nixos/modules/services/networking/dhcpd.nix249
-rw-r--r--nixos/modules/services/networking/dnschain.nix177
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix273
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.xml12
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix187
-rw-r--r--nixos/modules/services/networking/ferm.nix1
-rw-r--r--nixos/modules/services/networking/firefox/sync-server.nix4
-rw-r--r--nixos/modules/services/networking/firewall.nix127
-rw-r--r--nixos/modules/services/networking/flannel.nix2
-rw-r--r--nixos/modules/services/networking/gale.nix2
-rw-r--r--nixos/modules/services/networking/i2pd.nix25
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/default.nix2
-rw-r--r--nixos/modules/services/networking/keepalived/default.nix245
-rw-r--r--nixos/modules/services/networking/keepalived/virtual-ip-options.nix50
-rw-r--r--nixos/modules/services/networking/keepalived/vrrp-options.nix121
-rw-r--r--nixos/modules/services/networking/kresd.nix119
-rw-r--r--nixos/modules/services/networking/libreswan.nix2
-rw-r--r--nixos/modules/services/networking/mosquitto.nix3
-rw-r--r--nixos/modules/services/networking/murmur.nix10
-rw-r--r--nixos/modules/services/networking/namecoind.nix211
-rw-r--r--nixos/modules/services/networking/networkmanager.nix43
-rw-r--r--nixos/modules/services/networking/nftables.nix136
-rw-r--r--nixos/modules/services/networking/nylon.nix74
-rw-r--r--nixos/modules/services/networking/openntpd.nix9
-rw-r--r--nixos/modules/services/networking/pdns-recursor.nix168
-rw-r--r--nixos/modules/services/networking/prayer.nix2
-rw-r--r--nixos/modules/services/networking/prosody.nix7
-rw-r--r--nixos/modules/services/networking/quagga.nix1
-rw-r--r--nixos/modules/services/networking/quassel.nix4
-rw-r--r--nixos/modules/services/networking/radicale.nix2
-rw-r--r--nixos/modules/services/networking/redsocks.nix270
-rw-r--r--nixos/modules/services/networking/rpcbind.nix52
-rw-r--r--nixos/modules/services/networking/searx.nix25
-rw-r--r--nixos/modules/services/networking/smokeping.nix14
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix47
-rw-r--r--nixos/modules/services/networking/sslh.nix2
-rw-r--r--nixos/modules/services/networking/supplicant.nix3
-rw-r--r--nixos/modules/services/networking/tinc.nix4
-rw-r--r--nixos/modules/services/networking/vsftpd.nix3
-rw-r--r--nixos/modules/services/networking/xrdp.nix153
-rw-r--r--nixos/modules/services/networking/znc.nix7
-rw-r--r--nixos/modules/services/printing/cupsd.nix1
-rw-r--r--nixos/modules/services/scheduling/atd.nix7
-rw-r--r--nixos/modules/services/scheduling/cron.nix12
-rw-r--r--nixos/modules/services/scheduling/fcron.nix55
-rw-r--r--nixos/modules/services/security/clamav.nix1
-rw-r--r--nixos/modules/services/security/haka.nix1
-rw-r--r--nixos/modules/services/security/hologram-agent.nix57
-rw-r--r--nixos/modules/services/security/hologram-server.nix (renamed from nixos/modules/services/security/hologram.nix)0
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix17
-rw-r--r--nixos/modules/services/security/physlock.nix14
-rw-r--r--nixos/modules/services/security/sshguard.nix140
-rw-r--r--nixos/modules/services/security/tor.nix17
-rw-r--r--nixos/modules/services/security/torify.nix16
-rw-r--r--nixos/modules/services/system/dbus-session-local.conf.in5
-rw-r--r--nixos/modules/services/system/dbus-system-local.conf.in6
-rw-r--r--nixos/modules/services/system/dbus.nix65
-rw-r--r--nixos/modules/services/system/earlyoom.nix96
-rw-r--r--nixos/modules/services/torrent/deluge.nix2
-rw-r--r--nixos/modules/services/torrent/transmission.nix3
-rw-r--r--nixos/modules/services/ttys/kmscon.nix2
-rw-r--r--nixos/modules/services/web-apps/atlassian/confluence.nix2
-rw-r--r--nixos/modules/services/web-apps/atlassian/crowd.nix3
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix1
-rw-r--r--nixos/modules/services/web-apps/frab.nix224
-rw-r--r--nixos/modules/services/web-apps/mattermost.nix1
-rw-r--r--nixos/modules/services/web-apps/pump.io-configure.js23
-rw-r--r--nixos/modules/services/web-apps/pump.io.nix220
-rw-r--r--nixos/modules/services/web-apps/quassel-webserver.nix4
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix2
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix2
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/moodle.nix198
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/wordpress.nix51
-rw-r--r--nixos/modules/services/web-servers/caddy.nix2
-rw-r--r--nixos/modules/services/web-servers/jboss/default.nix2
-rw-r--r--nixos/modules/services/web-servers/lighttpd/gitweb.nix3
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix84
-rw-r--r--nixos/modules/services/web-servers/nginx/location-options.nix9
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix9
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix65
-rw-r--r--nixos/modules/services/web-servers/zope2.nix2
-rw-r--r--nixos/modules/services/x11/compton.nix7
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix16
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix5
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde4.nix199
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde5.nix253
-rw-r--r--nixos/modules/services/x11/desktop-managers/kodi.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/lumina.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix228
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix15
-rw-r--r--nixos/modules/services/x11/desktop-managers/xterm.nix1
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix54
-rw-r--r--nixos/modules/services/x11/display-managers/kdm.nix158
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix10
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix6
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix15
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix20
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix249
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix8
-rw-r--r--nixos/modules/services/x11/hardware/multitouch.nix6
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix4
-rw-r--r--nixos/modules/services/x11/redshift.nix1
-rw-r--r--nixos/modules/services/x11/terminal-server.nix14
-rw-r--r--nixos/modules/services/x11/unclutter-xfixes.nix1
-rw-r--r--nixos/modules/services/x11/unclutter.nix6
-rw-r--r--nixos/modules/services/x11/urxvtd.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/2bwm.nix37
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/exwm.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/fvwm.nix41
-rw-r--r--nixos/modules/services/x11/window-managers/herbstluftwm.nix23
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix1
-rw-r--r--nixos/modules/services/x11/xserver.nix47
-rw-r--r--nixos/modules/system/activation/activation-script.nix5
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl6
-rw-r--r--nixos/modules/system/activation/top-level.nix5
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix33
-rw-r--r--nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix36
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl85
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/builder.sh12
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix2
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py37
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix18
-rw-r--r--nixos/modules/system/boot/luksroot.nix55
-rw-r--r--nixos/modules/system/boot/networkd.nix8
-rw-r--r--nixos/modules/system/boot/readonly-mountpoint.c20
-rw-r--r--nixos/modules/system/boot/resolved.nix4
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh27
-rw-r--r--nixos/modules/system/boot/stage-1.nix93
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh68
-rw-r--r--nixos/modules/system/boot/stage-2.nix19
-rw-r--r--nixos/modules/system/boot/systemd-lib.nix10
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix12
-rw-r--r--nixos/modules/system/boot/systemd.nix47
-rw-r--r--nixos/modules/tasks/filesystems.nix19
-rw-r--r--nixos/modules/tasks/filesystems/f2fs.nix2
-rw-r--r--nixos/modules/tasks/filesystems/glusterfs.nix11
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix97
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix65
-rw-r--r--nixos/modules/tasks/kbd.nix21
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix149
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix93
-rw-r--r--nixos/modules/tasks/network-interfaces.nix220
-rw-r--r--nixos/modules/tasks/powertop.nix27
-rw-r--r--nixos/modules/tasks/trackpoint.nix2
-rw-r--r--nixos/modules/testing/test-instrumentation.nix9
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix6
-rw-r--r--nixos/modules/virtualisation/amazon-init.nix28
-rw-r--r--nixos/modules/virtualisation/amazon-options.nix4
-rw-r--r--nixos/modules/virtualisation/azure-image.nix96
-rw-r--r--nixos/modules/virtualisation/containers.nix48
-rw-r--r--nixos/modules/virtualisation/docker.nix18
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix134
-rw-r--r--nixos/modules/virtualisation/ecs-agent.nix46
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix109
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix48
-rw-r--r--nixos/modules/virtualisation/lxc.nix1
-rw-r--r--nixos/modules/virtualisation/nova-config.nix57
-rw-r--r--nixos/modules/virtualisation/nova-image.nix65
-rw-r--r--nixos/modules/virtualisation/openstack/glance.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix44
-rw-r--r--nixos/modules/virtualisation/rkt.nix2
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix43
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix10
-rw-r--r--nixos/modules/virtualisation/vmware-guest.nix13
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix66
-rw-r--r--nixos/modules/virtualisation/xen-domU.nix5
-rw-r--r--nixos/release-combined.nix8
-rw-r--r--nixos/release-small.nix8
-rw-r--r--nixos/release.nix22
-rw-r--r--nixos/tests/bittorrent.nix2
-rw-r--r--nixos/tests/buildbot.nix46
-rw-r--r--nixos/tests/chromium.nix39
-rw-r--r--nixos/tests/cjdns.nix12
-rw-r--r--nixos/tests/containers-bridge.nix2
-rw-r--r--nixos/tests/containers-extra_veth.nix2
-rw-r--r--nixos/tests/containers-ipv6.nix2
-rw-r--r--nixos/tests/containers-portforward.nix63
-rw-r--r--nixos/tests/containers-restart_networking.nix114
-rw-r--r--nixos/tests/dnscrypt-proxy.nix1
-rw-r--r--nixos/tests/docker.nix17
-rw-r--r--nixos/tests/ec2.nix21
-rw-r--r--nixos/tests/emacs-daemon.nix2
-rw-r--r--nixos/tests/firefox.nix10
-rw-r--r--nixos/tests/gnome3-gdm.nix1
-rw-r--r--nixos/tests/grsecurity.nix46
-rw-r--r--nixos/tests/hardened.nix36
-rw-r--r--nixos/tests/hibernate.nix3
-rw-r--r--nixos/tests/installer.nix22
-rw-r--r--nixos/tests/ipv6.nix12
-rw-r--r--nixos/tests/kde4.nix70
-rw-r--r--nixos/tests/leaps.nix3
-rw-r--r--nixos/tests/mesos.nix97
-rw-r--r--nixos/tests/mesos_test.py72
-rw-r--r--nixos/tests/misc.nix9
-rw-r--r--nixos/tests/munin.nix1
-rw-r--r--nixos/tests/mysql-replication.nix10
-rw-r--r--nixos/tests/nat.nix46
-rw-r--r--nixos/tests/networking.nix72
-rw-r--r--nixos/tests/nfs.nix6
-rw-r--r--nixos/tests/nginx.nix42
-rw-r--r--nixos/tests/pam-oath-login.nix126
-rw-r--r--nixos/tests/pgjwt.nix42
-rw-r--r--nixos/tests/phabricator.nix2
-rw-r--r--nixos/tests/plasma5.nix (renamed from nixos/tests/kde5.nix)31
-rw-r--r--nixos/tests/postgresql.nix62
-rw-r--r--nixos/tests/pump.io.nix19
-rw-r--r--nixos/tests/radicale.nix80
-rw-r--r--nixos/tests/samba.nix5
-rw-r--r--nixos/tests/sddm.nix86
-rw-r--r--nixos/tests/slim.nix66
-rw-r--r--nixos/tests/smokeping.nix2
-rw-r--r--nixos/tests/taskserver.nix129
-rw-r--r--nixos/tests/trac.nix2
-rw-r--r--nixos/tests/virtualbox.nix2
-rw-r--r--nixos/tests/wordpress.nix12
-rw-r--r--nixos/tests/xrdp.nix45
442 files changed, 13143 insertions, 4657 deletions
diff --git a/nixos/default.nix b/nixos/default.nix
index 5f3e2ae081cc..0e45a1cd75e2 100644
--- a/nixos/default.nix
+++ b/nixos/default.nix
@@ -37,7 +37,4 @@ in
   vm = vmConfig.system.build.vm;
 
   vmWithBootLoader = vmWithBootLoaderConfig.system.build.vm;
-
-  # The following are used by nixos-rebuild.
-  nixFallback = pkgs.nixUnstable.out;
 }
diff --git a/nixos/doc/manual/administration/imperative-containers.xml b/nixos/doc/manual/administration/imperative-containers.xml
index 258e1ea948da..9851eb08afb5 100644
--- a/nixos/doc/manual/administration/imperative-containers.xml
+++ b/nixos/doc/manual/administration/imperative-containers.xml
@@ -29,8 +29,10 @@ line. For instance, to create a container that has
 <literal>root</literal>:
 
 <screen>
-# nixos-container create foo --config 'services.openssh.enable = true; \
-  users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-dss AAAAB3N…"];'
+# nixos-container create foo --config '
+  services.openssh.enable = true;
+  users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-dss AAAAB3N…"];
+'
 </screen>
 
 </para>
@@ -97,8 +99,11 @@ This will build and activate the new configuration. You can also
 specify a new configuration on the command line:
 
 <screen>
-# nixos-container update foo --config 'services.httpd.enable = true; \
-  services.httpd.adminAddr = "foo@example.org";'
+# nixos-container update foo --config '
+  services.httpd.enable = true;
+  services.httpd.adminAddr = "foo@example.org";
+  networking.firewall.allowedTCPPorts = [ 80 ];
+'
 
 # curl http://$(nixos-container show-ip foo)/
 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">…
diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml
index 448e2a932e91..8677c13db40f 100644
--- a/nixos/doc/manual/configuration/configuration.xml
+++ b/nixos/doc/manual/configuration/configuration.xml
@@ -21,6 +21,7 @@ effect after you run <command>nixos-rebuild</command>.</para>
 <xi:include href="user-mgmt.xml" />
 <xi:include href="file-systems.xml" />
 <xi:include href="x-windows.xml" />
+<xi:include href="xfce.xml" />
 <xi:include href="networking.xml" />
 <xi:include href="linux-kernel.xml" />
 
diff --git a/nixos/doc/manual/configuration/file-systems.xml b/nixos/doc/manual/configuration/file-systems.xml
index d1b324af3f12..ae3d124cd6bb 100644
--- a/nixos/doc/manual/configuration/file-systems.xml
+++ b/nixos/doc/manual/configuration/file-systems.xml
@@ -35,6 +35,12 @@ or <literal>ext4</literal>, then it’s best to specify
 <option>fsType</option> to ensure that the kernel module is
 available.</para>
 
+<note><para>System startup will fail if any of the filesystems fails to mount,
+dropping you to the emergency shell.
+You can make a mount asynchronous and non-critical by adding
+<literal>options = [ "nofail" ];</literal>.
+</para></note>
+
 <xi:include href="luks-file-systems.xml" />
 
 </chapter>
diff --git a/nixos/doc/manual/configuration/ipv6-config.xml b/nixos/doc/manual/configuration/ipv6-config.xml
index bf86926f9bf5..6d9e0a164e9e 100644
--- a/nixos/doc/manual/configuration/ipv6-config.xml
+++ b/nixos/doc/manual/configuration/ipv6-config.xml
@@ -22,5 +22,25 @@ boot.kernel.sysctl."net.ipv6.conf.eth0.disable_ipv6" = true;
 </programlisting>
 </para>
 
+<para>As with IPv4 networking interfaces are automatically configured via
+DHCPv6. You can configure an interface manually:
+
+<programlisting>
+networking.interfaces.eth0.ip6 = [ { address = "fe00:aa:bb:cc::2"; prefixLength = 64; } ];
+</programlisting>
+</para>
+
+<para>For configuring a gateway, optionally with explicitly specified interface:
+
+<programlisting>
+networking.defaultGateway6 = {
+  address = "fe00::1";
+  interface = "enp0s3";
+}
+</programlisting>
+</para>
+
+<para>See <xref linkend='sec-ipv4' /> for similar examples and additional information.
+</para>
 
 </section>
diff --git a/nixos/doc/manual/configuration/luks-file-systems.xml b/nixos/doc/manual/configuration/luks-file-systems.xml
index 2062456703f7..00c795cd0898 100644
--- a/nixos/doc/manual/configuration/luks-file-systems.xml
+++ b/nixos/doc/manual/configuration/luks-file-systems.xml
@@ -37,6 +37,10 @@ boot.initrd.luks.devices.crypted.device = "/dev/disk/by-uuid/3f6b0024-3a44-4fde-
 fileSystems."/".device = "/dev/mapper/crypted";
 </programlisting>
 
+Should grub be used as bootloader, and <filename>/boot</filename> is located
+on an encrypted partition, it is necessary to add the following grub option:
+<programlisting>boot.loader.grub.enableCryptodisk = true;</programlisting>
+
 </para>
 
 </section>
diff --git a/nixos/doc/manual/configuration/modularity.xml b/nixos/doc/manual/configuration/modularity.xml
index 59a4e3b33ba1..5420c7f88385 100644
--- a/nixos/doc/manual/configuration/modularity.xml
+++ b/nixos/doc/manual/configuration/modularity.xml
@@ -36,9 +36,8 @@ latter might look like this:
 { config, pkgs, ... }:
 
 { services.xserver.enable = true;
-  services.xserver.displayManager.kdm.enable = true;
-  services.xserver.desktopManager.kde4.enable = true;
-  environment.systemPackages = [ pkgs.kde4.kscreensaver ];
+  services.xserver.displayManager.sddm.enable = true;
+  services.xserver.desktopManager.plasma5.enable = true;
 }
 </programlisting>
 
diff --git a/nixos/doc/manual/configuration/network-manager.xml b/nixos/doc/manual/configuration/network-manager.xml
index dafbcfcb1e5b..b4808e74ff9d 100644
--- a/nixos/doc/manual/configuration/network-manager.xml
+++ b/nixos/doc/manual/configuration/network-manager.xml
@@ -27,7 +27,11 @@ users.extraUsers.youruser.extraGroups = [ "networkmanager" ];
 <para>NetworkManager is controlled using either <command>nmcli</command> or
 <command>nmtui</command> (curses-based terminal user interface). See their
 manual pages for details on their usage. Some desktop environments (GNOME, KDE)
-have their own configuration tools for NetworkManager.</para>
+have their own configuration tools for NetworkManager. On XFCE, there is no
+configuration tool for NetworkManager by default: by adding
+<code>networkmanagerapplet</code> to the list of system packages, the graphical
+applet will be installed and will launch automatically when XFCE is starting
+(and will show in the status tray).</para>
 
 <note><para><code>networking.networkmanager</code> and
 <code>networking.wireless</code> (WPA Supplicant) cannot be enabled at the same
diff --git a/nixos/doc/manual/configuration/user-mgmt.xml b/nixos/doc/manual/configuration/user-mgmt.xml
index 829e5b9ea842..2bd9cca5622f 100644
--- a/nixos/doc/manual/configuration/user-mgmt.xml
+++ b/nixos/doc/manual/configuration/user-mgmt.xml
@@ -36,7 +36,10 @@ to set a password, which is retained across invocations of
 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
 account will cease to exist. Also, imperative commands for managing users
-and groups, such as useradd, are no longer available.</para>
+and groups, such as useradd, are no longer available. Passwords may still be
+assigned by setting the user's <literal>hashedPassword</literal> option. A
+hashed password can be generated using <command>mkpasswd -m sha-512</command>
+after installing the <literal>mkpasswd</literal> package.</para>
 
 <para>A user ID (uid) is assigned automatically.  You can also specify
 a uid manually by adding
diff --git a/nixos/doc/manual/configuration/x-windows.xml b/nixos/doc/manual/configuration/x-windows.xml
index 3040839861c1..4a73695e0942 100644
--- a/nixos/doc/manual/configuration/x-windows.xml
+++ b/nixos/doc/manual/configuration/x-windows.xml
@@ -25,19 +25,23 @@ Otherwise, you can only log into a plain undecorated
 <command>xterm</command> window.  Thus you should pick one or more of
 the following lines:
 <programlisting>
-services.xserver.desktopManager.kde4.enable = true;
+services.xserver.desktopManager.plasma5.enable = true;
 services.xserver.desktopManager.xfce.enable = true;
+services.xserver.desktopManager.gnome3.enable = true;
 services.xserver.windowManager.xmonad.enable = true;
 services.xserver.windowManager.twm.enable = true;
 services.xserver.windowManager.icewm.enable = true;
+services.xserver.windowManager.i3.enable = true;
 </programlisting>
 </para>
 
 <para>NixOS’s default <emphasis>display manager</emphasis> (the
 program that provides a graphical login prompt and manages the X
-server) is SLiM.  You can select KDE’s <command>kdm</command> instead:
+server) is SLiM. You can select an alternative one by picking one
+of the following lines:
 <programlisting>
-services.xserver.displayManager.kdm.enable = true;
+services.xserver.displayManager.sddm.enable = true;
+services.xserver.displayManager.lightdm.enable = true;
 </programlisting>
 </para>
 
diff --git a/nixos/doc/manual/configuration/xfce.xml b/nixos/doc/manual/configuration/xfce.xml
new file mode 100644
index 000000000000..21c7a85e19cc
--- /dev/null
+++ b/nixos/doc/manual/configuration/xfce.xml
@@ -0,0 +1,105 @@
+<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="sec-xfce">
+
+    <title>Xfce Desktop Environment</title>
+
+    <para>
+        To enable the Xfce Desktop Environment, set
+        <programlisting>
+services.xserver.desktopManager = {
+    xfce.enable = true;
+    default = "xfce";
+};
+        </programlisting>
+    </para>
+
+    <para>
+        Optionally, <emphasis>compton</emphasis>
+        can be enabled for nice graphical effects, some example settings:
+        <programlisting>
+services.compton = {
+  enable          = true;
+  fade            = true;
+  inactiveOpacity = "0.9";
+  shadow          = true;
+  fadeDelta       = 4;
+};
+        </programlisting>
+    </para>
+
+    <para>
+        Some Xfce programs are not installed automatically.
+        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>
+
+        <para>
+            To enable
+            <emphasis>Thunar</emphasis>
+            volume support, put
+            <programlisting>
+services.xserver.desktopManager.xfce.enable = true;
+            </programlisting>
+            into your <emphasis>configuration.nix</emphasis>.
+        </para>
+
+    </simplesect>
+
+    <simplesect>
+        <title>Polkit Authentication Agent</title>
+
+        <para>
+            There is no authentication agent automatically installed alongside
+            Xfce. To allow mounting of local (non-removable) filesystems, you
+            will need to install one.
+
+            Installing <emphasis>polkit_gnome</emphasis>, a rebuild, logout and
+            login did the trick.
+        </para>
+
+    </simplesect>
+
+    <simplesect>
+        <title>Troubleshooting</title>
+
+        <para>
+            Even after enabling udisks2, volume management might not work.
+            Thunar and/or the desktop takes time to show up.
+
+            Thunar will spit out this kind of message on start
+            (look at <command>journalctl --user -b</command>).
+
+            <programlisting>
+Thunar:2410): GVFS-RemoteVolumeMonitor-WARNING **: remote volume monitor with dbus name org.gtk.Private.UDisks2VolumeMonitor is not supported
+            </programlisting>
+
+            This is caused by some needed GNOME services not running.
+            This is all fixed by enabling "Launch GNOME services on startup" in
+            the Advanced tab of the Session and Startup settings panel.
+            Alternatively, you can run this command to do the same thing.
+            <programlisting>
+$ xfconf-query -c xfce4-session -p /compat/LaunchGNOME -s true
+            </programlisting>
+            A log-out and re-log will be needed for this to take effect.
+        </para>
+
+    </simplesect>
+
+</chapter>
diff --git a/nixos/doc/manual/development/option-declarations.xml b/nixos/doc/manual/development/option-declarations.xml
index ce432a7fa6ca..d62d0896bb7c 100644
--- a/nixos/doc/manual/development/option-declarations.xml
+++ b/nixos/doc/manual/development/option-declarations.xml
@@ -65,22 +65,22 @@ options = {
 
 </para>
 
-<section xml:id="sec-option-declarations-eot"><title>Extensible Option 
+<section xml:id="sec-option-declarations-eot"><title>Extensible Option
     Types</title>
 
-  <para>Extensible option types is a feature that allow to extend certain types 
+  <para>Extensible option types is a feature that allow to extend certain types
     declaration through multiple module files.
-    This feature only work with a restricted set of types, namely 
+    This feature only work with a restricted set of types, namely
     <literal>enum</literal> and <literal>submodules</literal> and any composed
     forms of them.</para>
 
-  <para>Extensible option types can be used for <literal>enum</literal> options 
-    that affects multiple modules, or as an alternative to related 
+  <para>Extensible option types can be used for <literal>enum</literal> options
+    that affects multiple modules, or as an alternative to related
     <literal>enable</literal> options.</para>
 
   <para>As an example, we will take the case of display managers. There is a
     central display manager module for generic display manager options and a
-    module file per display manager backend (slim, kdm, gdm ...).
+    module file per display manager backend (slim, sddm, gdm ...).
   </para>
 
   <para>There are two approach to this module structure:
@@ -96,7 +96,7 @@ options = {
   </para>
 
   <para>Both approachs have problems.</para>
-    
+
   <para>Making backends independent can quickly become hard to manage. For
     display managers, there can be only one enabled at a time, but the type
     system can not enforce this restriction as there is no relation between
@@ -108,18 +108,18 @@ options = {
     central module will require to change the central module option every time
     a new backend is added or removed.</para>
 
-  <para>By using extensible option types, it is possible to create a placeholder 
-    option in the central module (<xref linkend='ex-option-declaration-eot-service' 
-      />), and to extend it in each backend module (<xref 
-      linkend='ex-option-declaration-eot-backend-slim' />, <xref 
-      linkend='ex-option-declaration-eot-backend-kdm' />).</para>
- 
+  <para>By using extensible option types, it is possible to create a placeholder
+    option in the central module (<xref linkend='ex-option-declaration-eot-service'
+      />), and to extend it in each backend module (<xref
+      linkend='ex-option-declaration-eot-backend-slim' />, <xref
+      linkend='ex-option-declaration-eot-backend-sddm' />).</para>
+
   <para>As a result, <literal>displayManager.enable</literal> option values can
   be added without changing the main service module file and the type system
   automatically enforce that there can only be a single display manager
   enabled.</para>
 
-<example xml:id='ex-option-declaration-eot-service'><title>Extensible type 
+<example xml:id='ex-option-declaration-eot-service'><title>Extensible type
     placeholder in the service module</title>
 <screen>
 services.xserver.displayManager.enable = mkOption {
@@ -127,29 +127,29 @@ services.xserver.displayManager.enable = mkOption {
   type = with types; nullOr (enum [ ]);
 };</screen></example>
 
-<example xml:id='ex-option-declaration-eot-backend-slim'><title>Extending 
-    <literal>services.xserver.displayManager.enable</literal> in the 
+<example xml:id='ex-option-declaration-eot-backend-slim'><title>Extending
+    <literal>services.xserver.displayManager.enable</literal> in the
     <literal>slim</literal> module</title>
 <screen>
 services.xserver.displayManager.enable = mkOption {
   type = with types; nullOr (enum [ "slim" ]);
 };</screen></example>
 
-<example xml:id='ex-option-declaration-eot-backend-kdm'><title>Extending 
-    <literal>services.foo.backend</literal> in the <literal>kdm</literal> 
+<example xml:id='ex-option-declaration-eot-backend-sddm'><title>Extending
+    <literal>services.foo.backend</literal> in the <literal>sddm</literal>
     module</title>
 <screen>
 services.xserver.displayManager.enable = mkOption {
-  type = with types; nullOr (enum [ "kdm" ]);
+  type = with types; nullOr (enum [ "sddm" ]);
 };</screen></example>
 
-<para>The placeholder declaration is a standard <literal>mkOption</literal> 
-  declaration, but it is important that extensible option declarations only use 
+<para>The placeholder declaration is a standard <literal>mkOption</literal>
+  declaration, but it is important that extensible option declarations only use
   the <literal>type</literal> argument.</para>
 
-<para>Extensible option types work with any of the composed variants of 
-  <literal>enum</literal> such as 
-  <literal>with types; nullOr (enum [ "foo" "bar" ])</literal> 
+<para>Extensible option types work with any of the composed variants of
+  <literal>enum</literal> such as
+  <literal>with types; nullOr (enum [ "foo" "bar" ])</literal>
   or <literal>with types; listOf (enum [ "foo" "bar" ])</literal>.</para>
 
 </section>
diff --git a/nixos/doc/manual/development/replace-modules.xml b/nixos/doc/manual/development/replace-modules.xml
new file mode 100644
index 000000000000..cc0539ec5109
--- /dev/null
+++ b/nixos/doc/manual/development/replace-modules.xml
@@ -0,0 +1,75 @@
+<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-replace-modules">
+
+<title>Replace Modules</title>
+
+<para>Modules that are imported can also be disabled.  The option
+  declarations and config implementation of a disabled module will be
+  ignored, allowing another to take it's place.  This can be used to
+  import a set of modules from another channel while keeping the rest
+  of the system on a stable release.</para>
+<para><literal>disabledModules</literal> is a top level attribute like
+  <literal>imports</literal>, <literal>options</literal> and
+  <literal>config</literal>.  It contains a list of modules that will
+  be disabled. This can either be the full path to the module or a
+  string with the filename relative to the modules path
+  (eg. &lt;nixpkgs/nixos/modules&gt; for nixos).
+  </para>
+
+<para>This example will replace the existing postgresql module with
+  the version defined in the nixos-unstable channel while keeping the
+  rest of the modules and packages from the original nixos channel.
+  This only overrides the module definition, this won't use postgresql
+  from nixos-unstable unless explicitly configured to do so.</para>
+
+<programlisting>
+{ config, lib, pkgs, ... }:
+
+{
+  disabledModules = [ "services/databases/postgresql.nix" ];
+
+  imports =
+    [ # Use postgresql service from nixos-unstable channel.
+      # sudo nix-channel --add http://nixos.org/channels/nixos-unstable nixos-unstable
+      &lt;nixos-unstable/nixos/modules/services/databases/postgresql.nix&gt;
+    ];
+
+  services.postgresql.enable = true;
+}
+</programlisting>
+
+<para>This example shows how to define a custom module as a
+  replacement for an existing module. Importing this module will
+  disable the original module without having to know it's
+  implementation details.</para>
+
+<programlisting>
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.man;
+in
+
+{
+  disabledModules = [ "services/programs/man.nix" ];
+
+  options = {
+    programs.man.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable manual pages.";
+    };
+  };
+
+  config = mkIf cfg.enabled {
+    warnings = [ "disabled manpages for production deployments." ];
+  };
+}
+</programlisting>
+
+</section>
diff --git a/nixos/doc/manual/development/sources.xml b/nixos/doc/manual/development/sources.xml
index 7cd5ce0002c2..a2896cd7a135 100644
--- a/nixos/doc/manual/development/sources.xml
+++ b/nixos/doc/manual/development/sources.xml
@@ -8,7 +8,7 @@
 
 <para>By default, NixOS’s <command>nixos-rebuild</command> command
 uses the NixOS and Nixpkgs sources provided by the
-<literal>nixos-unstable</literal> channel (kept in
+<literal>nixos</literal> channel (kept in
 <filename>/nix/var/nix/profiles/per-user/root/channels/nixos</filename>).
 To modify NixOS, however, you should check out the latest sources from
 Git.  This is as follows:
@@ -27,8 +27,8 @@ a subdirectory of the Nixpkgs repository.) The remote
 <literal>channels</literal> refers to a read-only repository that
 tracks the Nixpkgs/NixOS channels (see <xref linkend="sec-upgrading"/>
 for more information about channels). Thus, the Git branch
-<literal>channels/nixos-14.12</literal> will contain the latest built
-and tested version available in the <literal>nixos-14.12</literal>
+<literal>channels/nixos-17.03</literal> will contain the latest built
+and tested version available in the <literal>nixos-17.03</literal>
 channel.</para>
 
 <para>It’s often inconvenient to develop directly on the master
@@ -39,9 +39,9 @@ branch based on your current NixOS version:
 
 <screen>
 $ nixos-version
-14.04.273.ea1952b (Baboon)
+17.09pre104379.6e0b727 (Hummingbird)
 
-$ git checkout -b local ea1952b
+$ git checkout -b local 6e0b727
 </screen>
 
 Or, to base your local branch on the latest version available in a
@@ -49,17 +49,17 @@ NixOS channel:
 
 <screen>
 $ git remote update channels
-$ git checkout -b local channels/nixos-14.12
+$ git checkout -b local channels/nixos-17.03
 </screen>
 
-(Replace <literal>nixos-14.12</literal> with the name of the channel
+(Replace <literal>nixos-17.03</literal> with the name of the channel
 you want to use.) You can use <command>git merge</command> or
 <command>git rebase</command> to keep your local branch in sync with
 the channel, e.g.
 
 <screen>
 $ git remote update channels
-$ git merge channels/nixos-14.12
+$ git merge channels/nixos-17.03
 </screen>
 
 You can use <command>git cherry-pick</command> to copy commits from
@@ -87,7 +87,11 @@ $ ln -s <replaceable>/my/sources</replaceable>/nixpkgs ~/.nix-defexpr/nixpkgs
 
 You may want to delete the symlink
 <filename>~/.nix-defexpr/channels_root</filename> to prevent root’s
-NixOS channel from clashing with your own tree.</para>
+NixOS channel from clashing with your own tree (this may break the
+command-not-found utility though). If you want to go back to the default
+state, you may just remove the <filename>~/.nix-defexpr</filename>
+directory completely, log out and log in again and it should have been
+recreated with a link to the root channels.</para>
 
 <!-- FIXME: not sure what this means.
 <para>You should not pass the base directory
diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml
index ef6920160e6d..5bdcad5ceb57 100644
--- a/nixos/doc/manual/development/writing-modules.xml
+++ b/nixos/doc/manual/development/writing-modules.xml
@@ -179,5 +179,6 @@ in {
 <xi:include href="option-types.xml" />
 <xi:include href="option-def.xml" />
 <xi:include href="meta-attributes.xml" />
+<xi:include href="replace-modules.xml" />
 
 </chapter>
diff --git a/nixos/doc/manual/installation/changing-config.xml b/nixos/doc/manual/installation/changing-config.xml
index 43b591a1cae9..75df307a1b7c 100644
--- a/nixos/doc/manual/installation/changing-config.xml
+++ b/nixos/doc/manual/installation/changing-config.xml
@@ -7,7 +7,7 @@
 
 <para>The file <filename>/etc/nixos/configuration.nix</filename>
 contains the current configuration of your machine.  Whenever you’ve
-changed something to that file, you should do
+<link linkend="ch-configuration">changed something</link> in that file, you should do
 
 <screen>
 # nixos-rebuild switch</screen>
diff --git a/nixos/doc/manual/installation/installing-uefi.xml b/nixos/doc/manual/installation/installing-uefi.xml
index 927648febc50..0d3eaa8bb1fb 100644
--- a/nixos/doc/manual/installation/installing-uefi.xml
+++ b/nixos/doc/manual/installation/installing-uefi.xml
@@ -26,7 +26,8 @@ changes:
     <literal>vfat</literal> filesystem.</para>
   </listitem>
   <listitem>
-    <para>You must set <option>boot.loader.systemd-boot.enable</option> to
+    <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>
diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml
index a4b5dafbed16..4a74e406b14c 100644
--- a/nixos/doc/manual/installation/installing-usb.xml
+++ b/nixos/doc/manual/installation/installing-usb.xml
@@ -11,7 +11,9 @@ 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.</para>
+block devices. If you're on OS X 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>
 
 <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
@@ -32,6 +34,11 @@ ISO, copy its contents verbatim to your drive, then either:
     in <link xlink:href="https://www.kernel.org/doc/Documentation/kernel-parameters.txt">
     the kernel documentation</link> for more details).</para>
   </listitem>
+  <listitem>
+    <para>If you want to load the contents of the ISO to ram after bootin
+    (So you can remove the stick after bootup) you can append the parameter
+    <literal>copytoram</literal>to the <literal>options</literal> field.</para>
+  </listitem>
 </itemizedlist>
 </para>
 
diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml
index 04a186a1bca6..8c37643c08f5 100644
--- a/nixos/doc/manual/installation/installing.xml
+++ b/nixos/doc/manual/installation/installing.xml
@@ -37,6 +37,11 @@
   first disable network-manager with
   <command>systemctl stop network-manager</command>.</para></listitem>
 
+  <listitem><para>If you would like to continue the installation from a different
+  machine you need to activate the SSH daemon via <literal>systemctl start sshd</literal>.
+  In order to be able to login you also need to set a password for
+  <literal>root</literal> using <literal>passwd</literal>.</para></listitem>
+
   <listitem><para>The NixOS installer doesn’t do any partitioning or
   formatting yet, so you need to do that yourself.  Use the following
   commands:
diff --git a/nixos/doc/manual/installation/upgrading.xml b/nixos/doc/manual/installation/upgrading.xml
index c974523f886c..aee6523345c4 100644
--- a/nixos/doc/manual/installation/upgrading.xml
+++ b/nixos/doc/manual/installation/upgrading.xml
@@ -15,12 +15,12 @@ been built.  These channels are:
 <itemizedlist>
   <listitem>
     <para><emphasis>Stable channels</emphasis>, such as <literal
-    xlink:href="https://nixos.org/channels/nixos-14.12">nixos-14.12</literal>.
+    xlink:href="https://nixos.org/channels/nixos-17.03">nixos-17.03</literal>.
     These only get conservative bug fixes and package upgrades.  For
     instance, a channel update may cause the Linux kernel on your
-    system to be upgraded from 3.4.66 to 3.4.67 (a minor bug fix), but
-    not from 3.4.<replaceable>x</replaceable> to
-    3.11.<replaceable>x</replaceable> (a major change that has the
+    system to be upgraded from 4.9.16 to 4.9.17 (a minor bug fix), but
+    not from 4.9.<replaceable>x</replaceable> to
+    4.11.<replaceable>x</replaceable> (a major change that has the
     potential to break things).  Stable channels are generally
     maintained until the next stable branch is created.</para>
     <para></para>
@@ -34,7 +34,7 @@ been built.  These channels are:
   </listitem>
   <listitem>
     <para><emphasis>Small channels</emphasis>, such as <literal
-    xlink:href="https://nixos.org/channels/nixos-14.12-small">nixos-14.12-small</literal>
+    xlink:href="https://nixos.org/channels/nixos-17.03-small">nixos-17.03-small</literal>
     or <literal
     xlink:href="https://nixos.org/channels/nixos-unstable-small">nixos-unstable-small</literal>. These
     are identical to the stable and unstable channels described above,
@@ -55,8 +55,8 @@ appliances.)</para>
 
 <para>When you first install NixOS, you’re automatically subscribed to
 the NixOS channel that corresponds to your installation source.   For
-instance, if you installed from a 14.12 ISO, you will be subscribed to
-the <literal>nixos-14.12</literal> channel.  To see which NixOS
+instance, if you installed from a 17.03 ISO, you will be subscribed to
+the <literal>nixos-17.03</literal> channel.  To see which NixOS
 channel you’re subscribed to, run the following as root:
 
 <screen>
@@ -71,16 +71,16 @@ To switch to a different NixOS channel, do
 </screen>
 
 (Be sure to include the <literal>nixos</literal> parameter at the
-end.)  For instance, to use the NixOS 14.12 stable channel:
+end.)  For instance, to use the NixOS 17.03 stable channel:
 
 <screen>
-# nix-channel --add https://nixos.org/channels/nixos-14.12 nixos
+# nix-channel --add https://nixos.org/channels/nixos-17.03 nixos
 </screen>
 
 If you have a server, you may want to use the “small” channel instead:
 
 <screen>
-# nix-channel --add https://nixos.org/channels/nixos-14.12-small nixos
+# nix-channel --add https://nixos.org/channels/nixos-17.03-small nixos
 </screen>
 
 And if you want to live on the bleeding edge:
@@ -130,7 +130,7 @@ runs, see <command>systemctl list-timers</command>.)  You can also
 specify a channel explicitly, e.g.
 
 <programlisting>
-system.autoUpgrade.channel = https://nixos.org/channels/nixos-15.09;
+system.autoUpgrade.channel = https://nixos.org/channels/nixos-17.03;
 </programlisting>
 
 </para>
diff --git a/nixos/doc/manual/release-notes/release-notes.xml b/nixos/doc/manual/release-notes/release-notes.xml
index 20d3f74f94b4..6065a86f60d8 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-1709.xml" />
 <xi:include href="rl-1703.xml" />
 <xi:include href="rl-1609.xml" />
 <xi:include href="rl-1603.xml" />
diff --git a/nixos/doc/manual/release-notes/rl-1703.xml b/nixos/doc/manual/release-notes/rl-1703.xml
index 6997155d94d0..28c573e6d231 100644
--- a/nixos/doc/manual/release-notes/rl-1703.xml
+++ b/nixos/doc/manual/release-notes/rl-1703.xml
@@ -4,25 +4,178 @@
          version="5.0"
          xml:id="sec-release-17.03">
 
-<title>Release 17.03 (“XXX”, 2017/03/??)</title>
+<title>Release 17.03 (“Gorilla”, 2017/03/31)</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-17.03-highlights">
+
+<title>Highlights</title>
 
 <para>In addition to numerous new and upgraded packages, this release
 has the following highlights: </para>
 
 <itemizedlist>
   <listitem>
-    <para></para>
+    <para>Nixpkgs is now extensible through overlays. See the <link
+    xlink:href="https://nixos.org/nixpkgs/manual/#sec-overlays-install">Nixpkgs
+    manual</link> for more information.</para>
+  </listitem>
+
+  <listitem>
+    <para>This release is based on Glibc 2.25, GCC 5.4.0 and systemd
+    232. The default Linux kernel is 4.9 and Nix is at 1.11.8.</para>
+  </listitem>
+
+  <listitem>
+    <para>The default desktop environment now is KDE's Plasma 5. KDE 4 has been removed</para>
+  </listitem>
+
+  <listitem>
+    <para>The setuid wrapper functionality now supports setting
+    capabilities.</para>
+  </listitem>
+
+  <listitem>
+    <para>X.org server uses branch 1.19. Due to ABI incompatibilities,
+      <literal>ati_unfree</literal> keeps forcing 1.17
+      and <literal>amdgpu-pro</literal> starts forcing 1.18.</para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Cross compilation has been rewritten. See the nixpkgs manual for
+      details. The most obvious breaking change is that in derivations there is no
+      <literal>.nativeDrv</literal> nor <literal>.crossDrv</literal> are now
+      cross by default, not native.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>The <literal>overridePackages</literal> function has been rewritten
+    to be replaced by <link
+    xlink:href="https://nixos.org/nixpkgs/manual/#sec-overlays-install">
+    overlays</link></para>
+  </listitem>
+
+  <listitem>
+    <para>Packages in nixpkgs can be marked as insecure through listed
+    vulnerabilities. See the <link
+    xlink:href="https://nixos.org/nixpkgs/manual/#sec-allow-insecure">Nixpkgs
+    manual</link> for more information.</para>
+  </listitem>
+
+  <listitem>
+    <para>PHP now defaults to PHP 7.1</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-17.03-new-services">
+
+<title>New Services</title>
+
 <para>The following new services were added since the last release:</para>
 
 <itemizedlist>
-  <listitem>
-    <para></para>
-  </listitem>
+  <listitem><para><literal>hardware/ckb.nix</literal></para></listitem>
+  <listitem><para><literal>hardware/mcelog.nix</literal></para></listitem>
+  <listitem><para><literal>hardware/usb-wwan.nix</literal></para></listitem>
+  <listitem><para><literal>hardware/video/capture/mwprocapture.nix</literal></para></listitem>
+  <listitem><para><literal>programs/adb.nix</literal></para></listitem>
+  <listitem><para><literal>programs/chromium.nix</literal></para></listitem>
+  <listitem><para><literal>programs/gphoto2.nix</literal></para></listitem>
+  <listitem><para><literal>programs/java.nix</literal></para></listitem>
+  <listitem><para><literal>programs/mtr.nix</literal></para></listitem>
+  <listitem><para><literal>programs/oblogout.nix</literal></para></listitem>
+  <listitem><para><literal>programs/vim.nix</literal></para></listitem>
+  <listitem><para><literal>programs/wireshark.nix</literal></para></listitem>
+  <listitem><para><literal>security/dhparams.nix</literal></para></listitem>
+  <listitem><para><literal>services/audio/ympd.nix</literal></para></listitem>
+  <listitem><para><literal>services/computing/boinc/client.nix</literal></para></listitem>
+  <listitem><para><literal>services/continuous-integration/buildbot/master.nix</literal></para></listitem>
+  <listitem><para><literal>services/continuous-integration/buildbot/worker.nix</literal></para></listitem>
+  <listitem><para><literal>services/continuous-integration/gitlab-runner.nix</literal></para></listitem>
+  <listitem><para><literal>services/databases/riak-cs.nix</literal></para></listitem>
+  <listitem><para><literal>services/databases/stanchion.nix</literal></para></listitem>
+  <listitem><para><literal>services/desktops/gnome3/gnome-terminal-server.nix</literal></para></listitem>
+  <listitem><para><literal>services/editors/infinoted.nix</literal></para></listitem>
+  <listitem><para><literal>services/hardware/illum.nix</literal></para></listitem>
+  <listitem><para><literal>services/hardware/trezord.nix</literal></para></listitem>
+  <listitem><para><literal>services/logging/journalbeat.nix</literal></para></listitem>
+  <listitem><para><literal>services/mail/offlineimap.nix</literal></para></listitem>
+  <listitem><para><literal>services/mail/postgrey.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/couchpotato.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/docker-registry.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/errbot.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/geoip-updater.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/gogs.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/leaps.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/nix-optimise.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/ssm-agent.nix</literal></para></listitem>
+  <listitem><para><literal>services/misc/sssd.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/arbtt.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/netdata.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/default.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/alertmanager.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/blackbox-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/json-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/nginx-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/node-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/snmp-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/unifi-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/prometheus/varnish-exporter.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/sysstat.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/telegraf.nix</literal></para></listitem>
+  <listitem><para><literal>services/monitoring/vnstat.nix</literal></para></listitem>
+  <listitem><para><literal>services/network-filesystems/cachefilesd.nix</literal></para></listitem>
+  <listitem><para><literal>services/network-filesystems/glusterfs.nix</literal></para></listitem>
+  <listitem><para><literal>services/network-filesystems/ipfs.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/dante.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/dnscrypt-wrapper.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/fakeroute.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/flannel.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/htpdate.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/miredo.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/nftables.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/powerdns.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/pdns-recursor.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/quagga.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/redsocks.nix</literal></para></listitem>
+  <listitem><para><literal>services/networking/wireguard.nix</literal></para></listitem>
+  <listitem><para><literal>services/system/cgmanager.nix</literal></para></listitem>
+  <listitem><para><literal>services/torrent/opentracker.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/atlassian/confluence.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/atlassian/crowd.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/atlassian/jira.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/frab.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/nixbot.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/selfoss.nix</literal></para></listitem>
+  <listitem><para><literal>services/web-apps/quassel-webserver.nix</literal></para></listitem>
+  <listitem><para><literal>services/x11/unclutter-xfixes.nix</literal></para></listitem>
+  <listitem><para><literal>services/x11/urxvtd.nix</literal></para></listitem>
+  <listitem><para><literal>system/boot/systemd-nspawn.nix</literal></para></listitem>
+  <listitem><para><literal>virtualisation/ecs-agent.nix</literal></para></listitem>
+  <listitem><para><literal>virtualisation/lxcfs.nix</literal></para></listitem>
+  <listitem><para><literal>virtualisation/openstack/keystone.nix</literal></para></listitem>
+  <listitem><para><literal>virtualisation/openstack/glance.nix</literal></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-17.03-incompatibilities">
+
+<title>Backward Incompatibilities</title>
 
 <para>When upgrading from a previous release, please be aware of the
 following incompatible changes:</para>
@@ -30,6 +183,13 @@ following incompatible changes:</para>
 <itemizedlist>
   <listitem>
     <para>
+      Derivations have no <literal>.nativeDrv</literal> nor <literal>.crossDrv</literal> 
+      and are now cross by default, not native.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
       <literal>stdenv.overrides</literal> is now expected to take <literal>self</literal>
       and <literal>super</literal> arguments. See <literal>lib.trivial.extends</literal>
       for what those parameters represent.
@@ -38,6 +198,15 @@ following incompatible changes:</para>
 
   <listitem>
     <para>
+      <literal>ansible</literal> now defaults to ansible version 2 as version 1
+      has been removed due to a serious <link
+      xlink:href="https://www.computest.nl/advisories/CT-2017-0109_Ansible.txt">
+      vulnerability</link> unpatched by upstream.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
       <literal>gnome</literal> alias has been removed along with
       <literal>gtk</literal>, <literal>gtkmm</literal> and several others.
       Now you need to use versioned attributes, like <literal>gnome3</literal>.
@@ -54,15 +223,6 @@ following incompatible changes:</para>
 
   <listitem>
     <para>
-      The Yama LSM is now enabled by default in the kernel,
-      which prevents ptracing non-child processes.
-      This means you will not be able to attach gdb to an existing process,
-      but will need to start that process from gdb (so it is a child).
-    </para>
-  </listitem>
-
-  <listitem>
-    <para>
       The <literal>stripHash</literal> bash function in <literal>stdenv</literal>
       changed according to its documentation; it now outputs the stripped name to
       <literal>stdout</literal> instead of putting it in the variable
@@ -79,6 +239,15 @@ following incompatible changes:</para>
 
   <listitem>
     <para>
+      Two lone top-level dict dbs moved into <literal>dictdDBs</literal>. This
+      affects: <literal>dictdWordnet</literal> which is now at
+      <literal>dictdDBs.wordnet</literal> and <literal>dictdWiktionary</literal>
+      which is now at <literal>dictdDBs.wiktionary</literal>
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
       Parsoid service now uses YAML configuration format.
      <literal>service.parsoid.interwikis</literal> is now called
      <literal>service.parsoid.wikis</literal> and is a list of either API URLs
@@ -96,19 +265,250 @@ following incompatible changes:</para>
      <literal>networking.timeServers</literal>.
    </para>
   </listitem>
+
+  <listitem>
+    <para>
+      <literal>service.nylon</literal> is now declared using named instances.
+      As an example:
+
+<programlisting>
+  services.nylon = {
+    enable = true;
+    acceptInterface = "br0";
+    bindInterface = "tun1";
+    port = 5912;
+  };
+</programlisting>
+
+      should be replaced with:
+
+<programlisting>
+  services.nylon.myvpn = {
+    enable = true;
+    acceptInterface = "br0";
+    bindInterface = "tun1";
+    port = 5912;
+  };
+</programlisting>
+
+      this enables you to declare a SOCKS proxy for each uplink.
+
+    </para>
+  </listitem>
+
+  <listitem>
+    <para><literal>overridePackages</literal> function no longer exists.
+    It is replaced by <link
+    xlink:href="https://nixos.org/nixpkgs/manual/#sec-overlays-install">
+    overlays</link>. For example, the following code:
+
+<programlisting>
+  let
+    pkgs = import &lt;nixpkgs&gt; {};
+  in
+    pkgs.overridePackages (self: super: ...)
+</programlisting>
+
+    should be replaced by:
+
+<programlisting>
+  let
+    pkgs = import &lt;nixpkgs&gt; {};
+  in
+    import pkgs.path { overlays = [(self: super: ...)] }
+</programlisting>
+
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Autoloading connection tracking helpers is now disabled by default.
+      This default was also changed in the Linux kernel and is considered
+      insecure if not configured properly in your firewall. If you need
+      connection tracking helpers (i.e. for active FTP) please enable
+      <literal>networking.firewall.autoLoadConntrackHelpers</literal> and
+      tune <literal>networking.firewall.connectionTrackingModules</literal>
+      to suit your needs.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      <literal>local_recipient_maps</literal> is not set to empty value by
+      Postfix service. It's an insecure default as stated by Postfix
+      documentation. Those who want to retain this setting need to set it via
+      <literal>services.postfix.extraConfig</literal>.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+    Iputils no longer provide ping6 and traceroute6. The functionality of
+    these tools has been integrated into ping and traceroute respectively. To
+    enforce an address family the new flags <literal>-4</literal> and
+    <literal>-6</literal> have been added. One notable incompatibility is that
+    specifying an interface (for link-local IPv6 for instance) is no longer done
+    with the <literal>-I</literal> flag, but by encoding the interface into the
+    address (<literal>ping fe80::1%eth0</literal>).
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The socket handling of the <literal>services.rmilter</literal> module
+      has been fixed and refactored. As rmilter doesn't support binding to
+      more than one socket, the options <literal>bindUnixSockets</literal>
+      and <literal>bindInetSockets</literal> have been replaced by
+      <literal>services.rmilter.bindSocket.*</literal>. The default is still
+      a unix socket in <literal>/run/rmilter/rmilter.sock</literal>. Refer to
+      the options documentation for more information.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The <literal>fetch*</literal> functions no longer support md5,
+      please use sha256 instead.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The dnscrypt-proxy module interface has been streamlined around the
+      <option>extraArgs</option> option. Where possible, legacy option
+      declarations are mapped to <option>extraArgs</option> but will emit
+      warnings. The <option>resolverList</option> has been outright
+      removed: to use an unlisted resolver, use the
+      <option>customResolver</option> option.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      torbrowser now stores local state under
+      <filename>~/.local/share/tor-browser</filename> by default. Any
+      browser profile data from the old location,
+      <filename>~/.torbrowser4</filename>, must be migrated manually.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The ihaskell, monetdb, offlineimap and sitecopy services have been removed.
+    </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-17.03-notable-changes">
 
-<para>Other notable improvements:</para>
+<title>Other Notable Changes</title>
 
 <itemizedlist>
+
   <listitem>
     <para>Module type system have a new extensible option types feature that
       allow to extend certain types, such as enum, through multiple option
       declarations of the same option across multiple modules.
     </para>
   </listitem>
-</itemizedlist>
 
+  <listitem>
+    <para>
+      <literal>jre</literal> now defaults to GTK+ UI by default. This
+      improves visual consistency and makes Java follow system font style,
+      improving the situation on HighDPI displays. This has a cost of increased
+      closure size; for server and other headless workloads it's recommended to
+      use <literal>jre_headless</literal>.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>Python 2.6 interpreter and package set have been removed.</para>
+  </listitem>
 
+  <listitem>
+    <para>
+      The Python 2.7 interpreter does not use modules anymore. Instead, all
+      CPython interpreters now include the whole standard library except for `tkinter`,
+      which is available in the Python package set.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Python 2.7, 3.5 and 3.6 are now built deterministically and 3.4 mostly.
+      Minor modifications had to be made to the interpreters in order to generate
+      deterministic bytecode. This has security implications and is relevant for
+      those using Python in a <literal>nix-shell</literal>. See the Nixpkgs manual
+      for details.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The Python package sets now use a fixed-point combinator and the sets are
+      available as attributes of the interpreters.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The Python function <literal>buildPythonPackage</literal> has been improved and can be
+      used to build from Setuptools source, Flit source, and precompiled Wheels.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      When adding new or updating current Python libraries, the expressions should be put
+      in separate files in <literal>pkgs/development/python-modules</literal> and
+      called from <literal>python-packages.nix</literal>.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The dnscrypt-proxy service supports synchronizing the list of public
+      resolvers without working DNS resolution. This fixes issues caused by the
+      resolver list becoming outdated. It also improves the viability of
+      DNSCrypt only configurations.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Containers using bridged networking no longer lose their connection after
+      changes to the host networking.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      ZFS supports pool auto scrubbing.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The bind DNS utilities (e.g. dig) have been split into their own output and
+      are now also available in <literal>pkgs.dnsutils</literal> and it is no longer
+      necessary to pull in all of <literal>bind</literal> to use them.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      Per-user configuration was moved from <filename>~/.nixpkgs</filename> to
+      <filename>~/.config/nixpkgs</filename>. The former is still valid for
+      <filename>config.nix</filename> for backwards compatibility.
+    </para>
+  </listitem>
+</itemizedlist>
+</section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-1709.xml b/nixos/doc/manual/release-notes/rl-1709.xml
new file mode 100644
index 000000000000..7a92f2e8a638
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-1709.xml
@@ -0,0 +1,80 @@
+<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-17.09">
+
+<title>Release 17.09 (“Hummingbird”, 2017/09/??)</title>
+
+<para>In addition to numerous new and upgraded packages, this release
+has the following highlights: </para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+      The user handling now keeps track of deallocated UIDs/GIDs. When a user
+      or group is revived, this allows it to be allocated the UID/GID it had before.
+      A consequence is that UIDs and GIDs are no longer reused.
+    </para>
+  </listitem>
+
+</itemizedlist>
+
+<para>The following new services were added since the last release:</para>
+
+<itemizedlist>
+  <listitem>
+    <para></para>
+  </listitem>
+</itemizedlist>
+
+
+<para>When upgrading from a previous release, please be aware of the
+following incompatible changes:</para>
+
+<itemizedlist>
+  <listitem>
+    <para>
+      Top-level <literal>idea</literal> package collection was renamed.
+      All JetBrains IDEs are now at <literal>jetbrains</literal>.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      <literal>flexget</literal>'s state database cannot be upgraded to its
+      new internal format, requiring removal of any existing
+      <literal>db-config.sqlite</literal> which will be automatically recreated.
+    </para>
+  </listitem>
+</itemizedlist>
+
+
+<para>Other notable improvements:</para>
+
+<itemizedlist>
+
+  <listitem>
+    <para>
+      Modules can now be disabled by using <link
+      xlink:href="https://nixos.org/nixpkgs/manual/#sec-replace-modules">
+      disabledModules</link>, allowing another to take it's place.  This can be
+      used to import a set of modules from another channel while keeping the
+      rest of the system on a stable release.
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      Updated to FreeType 2.7.1, including a new TrueType engine.
+      The new engine replaces the Infinality engine which was the default in
+      NixOS. The default font rendering settings are now provided by
+      fontconfig-penultimate, replacing fontconfig-ultimate; the new defaults
+      are less invasive and provide rendering that is more consistent with
+      other systems and hopefully with each font designer's intent. Some
+      system-wide configuration has been removed from the Fontconfig NixOS
+      module where user Fontconfig settings are available.
+    </para>
+  </listitem>
+
+</itemizedlist>
+
+</section>
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index e279803f2ea0..56766ec9047f 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -7,6 +7,12 @@
 , # The size of the disk, in megabytes.
   diskSize
 
+  # The files and directories to be placed in the target file system.
+  # This is a list of attribute sets {source, target} where `source'
+  # is the file system object (regular file or directory) to be
+  # 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.
@@ -27,35 +33,124 @@
 
 , name ? "nixos-disk-image"
 
-  # This prevents errors while checking nix-store validity, see
-  # https://github.com/NixOS/nix/issues/1134
-, fixValidity ? true
-
 , format ? "raw"
 }:
 
 with lib;
 
-pkgs.vmTools.runInLinuxVM (
+let
+  # Copied from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/cd-dvd/channel.nix
+  # TODO: factor out more cleanly
+
+  # Do not include these things:
+  #   - The '.git' directory
+  #   - Result symlinks from nix-build ('result', 'result-2', 'result-bin', ...)
+  #   - VIM/Emacs swap/backup files ('.swp', '.swo', '.foo.swp', 'foo~', ...)
+  filterFn = path: type: let basename = baseNameOf (toString path); in
+    if type == "directory" then basename != ".git"
+    else if type == "symlink" then builtins.match "^result(|-.*)$" basename == null
+    else builtins.match "^((|\..*)\.sw[a-z]|.*~)$" basename == null;
+
+  nixpkgs = builtins.filterSource filterFn pkgs.path;
+
+  channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} ''
+    mkdir -p $out
+    cp -prd ${nixpkgs} $out/nixos
+    chmod -R u+w $out/nixos
+    if [ ! -e $out/nixos/nixpkgs ]; then
+      ln -s . $out/nixos/nixpkgs
+    fi
+    rm -rf $out/nixos/.git
+    echo -n ${config.system.nixosVersionSuffix} > $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;
+
+  # 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 :)
+  # !!! should use XML.
+  sources = map (x: x.source) contents;
+  targets = map (x: x.target) contents;
+
+  prepareImage = ''
+    export PATH=${pkgs.lib.makeSearchPathOutput "bin" "bin" prepareImageInputs}
+
+    mkdir $out
+    diskImage=nixos.raw
+    truncate -s ${toString diskSize}M $diskImage
+
+    ${if partitioned then ''
+      parted $diskImage -- mklabel msdos mkpart primary ext4 1M -1s
+      offset=$((2048*512))
+    '' else ''
+      offset=0
+    ''}
+
+    mkfs.${fsType} -F -L nixos -E offset=$offset $diskImage
+  
+    root="$PWD/root"
+    mkdir -p $root
+
+    # Copy arbitrary other files into the image
+    # Semi-shamelessly copied from make-etc.sh. I (@copumpkin) shall factor this stuff out as part of
+    # https://github.com/NixOS/nixpkgs/issues/23052.
+    set -f
+    sources_=(${concatStringsSep " " sources})
+    targets_=(${concatStringsSep " " targets})
+    set +f
+
+    for ((i = 0; i < ''${#targets_[@]}; i++)); do
+      source="''${sources_[$i]}"
+      target="''${targets_[$i]}"
+
+      if [[ "$source" =~ '*' ]]; then
+        # If the source name contains '*', perform globbing.
+        mkdir -p $root/$target
+        for fn in $source; do
+          rsync -a --no-o --no-g "$fn" $root/$target/
+        done
+      else
+        mkdir -p $root/$(dirname $target)
+        if ! [ -e $root/$target ]; then
+          rsync -a --no-o --no-g $source $root/$target
+        else
+          echo "duplicate entry $target -> $source"
+          exit 1
+        fi
+      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
+
+    echo "copying staging root to image..."
+    cptofs ${pkgs.lib.optionalString partitioned "-P 1"} -t ${fsType} -i $diskImage $root/* /
+  '';
+in pkgs.vmTools.runInLinuxVM (
   pkgs.runCommand name
-    { preVM =
-        ''
-          mkdir $out
-          diskImage=$out/nixos.${if format == "qcow2" then "qcow2" else "img"}
-          ${pkgs.vmTools.qemu}/bin/qemu-img create -f ${format} $diskImage "${toString diskSize}M"
-          mv closure xchg/
-        '';
-      buildInputs = [ pkgs.utillinux pkgs.perl pkgs.e2fsprogs pkgs.parted ];
-      exportReferencesGraph =
-        [ "closure" config.system.build.toplevel ];
-      inherit postVM;
+    { preVM = prepareImage;
+      buildInputs = with pkgs; [ utillinux e2fsprogs ];
+      exportReferencesGraph = [ "closure" metaClosure ];
+      postVM = ''
+        ${if format == "raw" then ''
+          mv $diskImage $out/nixos.img
+          diskImage=$out/nixos.img
+        '' else ''
+          ${pkgs.qemu}/bin/qemu-img convert -f raw -O qcow2 $diskImage $out/nixos.qcow2
+          diskImage=$out/nixos.qcow2
+        ''}
+        ${postVM}
+      '';
       memSize = 1024;
     }
     ''
       ${if partitioned then ''
-        # Create a single / partition.
-        parted /dev/vda mklabel msdos
-        parted /dev/vda -- mkpart primary ext2 1M -1s
         . /sys/class/block/vda1/uevent
         mknod /dev/vda1 b $MAJOR $MINOR
         rootDisk=/dev/vda1
@@ -63,46 +158,40 @@ pkgs.vmTools.runInLinuxVM (
         rootDisk=/dev/vda
       ''}
 
-      # Create an empty filesystem and mount it.
-      mkfs.${fsType} -L nixos $rootDisk
-      mkdir /mnt
-      mount $rootDisk /mnt
-
-      # Register the paths in the Nix database.
-      printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
-          ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group ""
-
-      ${if fixValidity then ''
-        # Add missing size/hash fields to the database. FIXME:
-        # exportReferencesGraph should provide these directly.
-        ${config.nix.package.out}/bin/nix-store --verify --check-contents --option build-users-group ""
-      '' else ""}
-
-      # In case the bootloader tries to write to /dev/sda…
+      # Some tools assume these exist
       ln -s vda /dev/xvda
       ln -s vda /dev/sda
 
-      # Install the closure onto the image
-      USER=root ${config.system.build.nixos-install}/bin/nixos-install \
-        --closure ${config.system.build.toplevel} \
-        --no-channel-copy \
-        --no-root-passwd \
-        ${optionalString (!installBootLoader) "--no-bootloader"}
+      mountPoint=/mnt
+      mkdir $mountPoint
+      mount $rootDisk $mountPoint
 
-      # Install a configuration.nix.
+      # Install a configuration.nix
       mkdir -p /mnt/etc/nixos
       ${optionalString (configFile != null) ''
         cp ${configFile} /mnt/etc/nixos/configuration.nix
       ''}
 
-      # Remove /etc/machine-id so that each machine cloning this image will get its own id
-      rm -f /mnt/etc/machine-id
+      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
+
+      # 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
 
-      umount /mnt
+      umount -R /mnt
 
-      # Make sure resize2fs works
+      # Make sure resize2fs works. Note that resize2fs has stricter criteria for resizing than a normal
+      # mount, so the `-c 0` and `-i 0` don't affect it. Setting it to `now` doesn't produce deterministic
+      # output, of course, but we can fix that when/if we start making images deterministic.
       ${optionalString (fsType == "ext4") ''
-        tune2fs -c 0 -i 0 $rootDisk
+        tune2fs -T now -c 0 -i 0 $rootDisk
       ''}
     ''
 )
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index 14c39e859bc1..c619264eb949 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -508,7 +508,7 @@ sub screenshot {
 sub getTTYText {
     my ($self, $tty) = @_;
 
-    my ($status, $out) = $self->execute("fold -w 80 /dev/vcs${tty}");
+    my ($status, $out) = $self->execute("fold -w\$(stty -F /dev/tty${tty} size | awk '{print \$2}') /dev/vcs${tty}");
     return $out;
 }
 
@@ -542,16 +542,20 @@ sub getScreenText {
     $self->nest("performing optical character recognition", sub {
         my $tmpbase = Cwd::abs_path(".")."/ocr";
         my $tmpin = $tmpbase."in.ppm";
-        my $tmpout = "$tmpbase.ppm";
 
         $self->sendMonitorCommand("screendump $tmpin");
-        system("ppmtopgm $tmpin | pamscale 4 -filter=lanczos > $tmpout") == 0
-            or die "cannot scale screenshot";
+
+        my $magickArgs = "-filter Catrom -density 72 -resample 300 "
+                       . "-contrast -normalize -despeckle -type grayscale "
+                       . "-sharpen 1 -posterize 3 -negate -gamma 100 "
+                       . "-blur 1x65535";
+        my $tessArgs = "-c debug_file=/dev/null --psm 11 --oem 2";
+
+        $text = `convert $magickArgs $tmpin tiff:- | tesseract - - $tessArgs`;
+        my $status = $? >> 8;
         unlink $tmpin;
-        system("tesseract $tmpout $tmpbase") == 0 or die "OCR failed";
-        unlink $tmpout;
-        $text = read_file("$tmpbase.txt");
-        unlink "$tmpbase.txt";
+
+        die "OCR failed with exit code $status" if $status != 0;
     });
     return $text;
 }
@@ -607,7 +611,8 @@ sub waitForWindow {
 sub copyFileFromHost {
     my ($self, $from, $to) = @_;
     my $s = `cat $from` or die;
-    $self->mustSucceed("echo '$s' > $to"); # !!! escaping
+    $s =~ s/'/'\\''/g;
+    $self->mustSucceed("echo '$s' > $to");
 }
 
 
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index c1cb5072acad..58c447c76db6 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -93,7 +93,7 @@ rec {
 
       vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 
-      ocrProg = tesseract;
+      ocrProg = tesseract_4.override { enableLanguages = [ "eng" ]; };
 
       # Generate onvenience wrappers for running the test driver
       # interactively with the specified network, and for starting the
@@ -108,16 +108,17 @@ rec {
           mkdir -p $out/bin
           echo "$testScript" > $out/test-script
           ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
-          vms="$(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)"
+          vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
           wrapProgram $out/bin/nixos-test-driver \
-            --add-flags "$vms" \
-            ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
+            --add-flags "''${vms[*]}" \
+            ${lib.optionalString enableOCR
+              "--prefix PATH : '${ocrProg}/bin:${imagemagick}/bin'"} \
             --run "testScript=\"\$(cat $out/test-script)\"" \
             --set testScript '$testScript' \
             --set VLANS '${toString vlans}'
           ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
           wrapProgram $out/bin/nixos-run-vms \
-            --add-flags "$vms" \
+            --add-flags "''${vms[*]}" \
             ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
             --set tests 'startAll; joinAll;' \
             --set VLANS '${toString vlans}' \
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index bfa4f4b3ca59..cdfac71634d4 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -2,15 +2,31 @@
 
 with lib;
 
-{
+let
+  cfg = config.amazonImage;
+in {
 
-  imports =
-    [ ../../../modules/installer/cd-dvd/channel.nix
-      ../../../modules/virtualisation/amazon-image.nix
-    ];
+  imports = [ ../../../modules/virtualisation/amazon-image.nix ];
 
-  system.build.amazonImage = import ../../../lib/make-disk-image.nix {
+  options.amazonImage = {
+    contents = mkOption {
+      example = literalExample ''
+        [ { source = pkgs.memtest86 + "/memtest.bin";
+            target = "boot/memtest.bin";
+          }
+        ]
+      '';
+      default = [];
+      description = ''
+        This option lists files to be copied to fixed locations in the
+        generated image. Glob patterns work.
+      '';
+    };
+  };
+
+  config.system.build.amazonImage = import ../../../lib/make-disk-image.nix {
     inherit lib config;
+    inherit (cfg) contents;
     pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
     partitioned = config.ec2.hvm;
     diskSize = if config.ec2.hvm then 2048 else 8192;
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index 0750a1b18c99..24ced8da531a 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -3,23 +3,22 @@
 
 # To start with do: nix-shell -p awscli --run "aws configure"
 
-
+set -e
 set -o pipefail
-#set -x
-
-stateDir=${TMPDIR:-/tmp}/ec2-image
-echo "keeping state in $stateDir"
-mkdir -p $stateDir
 
 version=$(nix-instantiate --eval --strict '<nixpkgs>' -A lib.nixpkgsVersion | sed s/'"'//g)
 major=${version:0:5}
 echo "NixOS version is $version ($major)"
 
+stateDir=/var/tmp/ec2-image-$version
+echo "keeping state in $stateDir"
+mkdir -p $stateDir
+
 rm -f ec2-amis.nix
 
-types="hvm pv"
+types="hvm"
 stores="ebs s3"
-regions="eu-west-1 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2 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-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
@@ -206,7 +205,7 @@ for type in $types; do
 
                     # Register the AMI.
                     if [ $type = pv ]; then
-                        kernel=$(aws ec2 describe-images --owner amazon --filters "Name=name,Values=pv-grub-hd0_1.04-$arch.gz" | jq -r .Images[0].ImageId)
+                        kernel=$(aws ec2 describe-images --owner amazon --filters "Name=name,Values=pv-grub-hd0_1.05-$arch.gz" | jq -r .Images[0].ImageId)
                         if [ "$kernel" = null ]; then break; fi
                         echo "using PV-GRUB kernel $kernel"
                         extraFlags+=" --virtualization-type paravirtual --kernel $kernel"
diff --git a/nixos/maintainers/scripts/gce/create-gce.sh b/nixos/maintainers/scripts/gce/create-gce.sh
index 7f8a0d23027a..ef1801fe54be 100755
--- a/nixos/maintainers/scripts/gce/create-gce.sh
+++ b/nixos/maintainers/scripts/gce/create-gce.sh
@@ -1,15 +1,23 @@
-#! /bin/sh -e
+#!/usr/bin/env nix-shell
+#! nix-shell -i bash -p google-cloud-sdk
 
-BUCKET_NAME=${BUCKET_NAME:-nixos-images}
-export NIX_PATH=nixpkgs=../../../..
-export NIXOS_CONFIG=$(dirname $(readlink -f $0))/../../../modules/virtualisation/google-compute-image.nix
-export TIMESTAMP=$(date +%Y%m%d%H%M)
+set -euo pipefail
+
+BUCKET_NAME="${BUCKET_NAME:-nixos-images}"
+TIMESTAMP="$(date +%Y%m%d%H%M)"
+export TIMESTAMP
 
 nix-build '<nixpkgs/nixos>' \
-   -A config.system.build.googleComputeImage --argstr system x86_64-linux -o gce --option extra-binary-caches http://hydra.nixos.org -j 10
+   -A config.system.build.googleComputeImage \
+   --arg configuration "{ imports = [ <nixpkgs/nixos/modules/virtualisation/google-compute-image.nix> ]; }" \
+   --argstr system x86_64-linux \
+   -o gce \
+   -j 10
 
-img=$(echo gce/*.tar.gz)
-if ! gsutil ls gs://${BUCKET_NAME}/$(basename $img); then
-  gsutil cp $img gs://${BUCKET_NAME}/$(basename $img)
+img_path=$(echo gce/*.tar.gz)
+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"
 fi
-gcloud compute images create $(basename $img .raw.tar.gz | sed 's|\.|-|' | sed 's|_|-|') --source-uri gs://${BUCKET_NAME}/$(basename $img)
+gcloud compute images create "$img_id" --source-uri "gs://${BUCKET_NAME}/$img_name"
diff --git a/nixos/maintainers/scripts/openstack/nova-image.nix b/nixos/maintainers/scripts/openstack/nova-image.nix
new file mode 100644
index 000000000000..fa9cfb74bd6c
--- /dev/null
+++ b/nixos/maintainers/scripts/openstack/nova-image.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  imports =
+    [ ../../../modules/installer/cd-dvd/channel.nix
+      ../../../modules/virtualisation/nova-config.nix
+    ];
+
+  system.build.novaImage = import ../../../lib/make-disk-image.nix {
+    inherit lib config;
+    pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
+    diskSize = 8192;
+    format = "qcow2";
+    configFile = pkgs.writeText "configuration.nix"
+      ''
+        {
+          imports = [ <nixpkgs/nixos/modules/virtualisation/nova-config.nix> ];
+        }
+      '';
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontconfig-penultimate.nix b/nixos/modules/config/fonts/fontconfig-penultimate.nix
new file mode 100644
index 000000000000..3e163b8ec51e
--- /dev/null
+++ b/nixos/modules/config/fonts/fontconfig-penultimate.nix
@@ -0,0 +1,270 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.fonts.fontconfig;
+
+  fcBool = x: "<bool>" + (boolToString x) + "</bool>";
+
+  # back-supported fontconfig version and package
+  # version is used for font cache generation
+  supportVersion = "210";
+  supportPkg     = pkgs."fontconfig_${supportVersion}";
+
+  # latest fontconfig version and package
+  # version is used for configuration folder name, /etc/fonts/VERSION/
+  # note: format differs from supportVersion and can not be used with makeCacheConf
+  latestVersion  = pkgs.fontconfig.configVersion;
+  latestPkg      = pkgs.fontconfig;
+
+  # supported version fonts.conf
+  supportFontsConf = pkgs.makeFontsConf { fontconfig = supportPkg; fontDirectories = config.fonts.fonts; };
+
+  # configuration file to read fontconfig cache
+  # version dependent
+  # priority 0
+  cacheConfSupport = makeCacheConf { version = supportVersion; };
+  cacheConfLatest  = makeCacheConf {};
+
+  # generate the font cache setting file for a fontconfig version
+  # use latest when no version is passed
+  makeCacheConf = { version ? null }:
+    let
+      fcPackage = if builtins.isNull version
+                  then "fontconfig"
+                  else "fontconfig_${version}";
+      makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
+      cache     = makeCache pkgs."${fcPackage}";
+      cache32   = makeCache pkgs.pkgsi686Linux."${fcPackage}";
+    in
+    pkgs.writeText "fc-00-nixos-cache.conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+      <fontconfig>
+        <!-- Font directories -->
+        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
+        <!-- Pre-generated font caches -->
+        <cachedir>${cache}</cachedir>
+        ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
+          <cachedir>${cache32}</cachedir>
+        ''}
+      </fontconfig>
+    '';
+
+  # The configuration to be included in /etc/font/
+  penultimateConf = pkgs.runCommand "font-penultimate-conf" {} ''
+    support_folder=$out/etc/fonts/conf.d
+    latest_folder=$out/etc/fonts/${latestVersion}/conf.d
+
+    mkdir -p $support_folder
+    mkdir -p $latest_folder
+
+    ln -s ${supportFontsConf} $support_folder/../fonts.conf
+    ln -s ${latestPkg.out}/etc/fonts/fonts.conf \
+          $latest_folder/../fonts.conf
+
+    # fontconfig-penultimate various configuration files
+    ln -s ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/*.conf \
+          $support_folder
+    ln -s ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/*.conf \
+          $latest_folder
+
+    ln -s ${cacheConfSupport} $support_folder/00-nixos-cache.conf
+    ln -s ${cacheConfLatest}  $latest_folder/00-nixos-cache.conf
+
+    rm $support_folder/10-antialias.conf $latest_folder/10-antialias.conf
+    ln -s ${antialiasConf} $support_folder/10-antialias.conf
+    ln -s ${antialiasConf} $latest_folder/10-antialias.conf
+
+    rm $support_folder/10-hinting.conf $latest_folder/10-hinting.conf
+    ln -s ${hintingConf} $support_folder/10-hinting.conf
+    ln -s ${hintingConf} $latest_folder/10-hinting.conf
+
+    ${optionalString cfg.useEmbeddedBitmaps ''
+    rm $support_folder/10-no-embedded-bitmaps.conf
+    rm $latest_folder/10-no-embedded-bitmaps.conf
+    ''}
+
+    rm $support_folder/10-subpixel.conf $latest_folder/10-subpixel.conf
+    ln -s ${subpixelConf} $support_folder/10-subpixel.conf
+    ln -s ${subpixelConf} $latest_folder/10-subpixel.conf
+
+    ${optionalString (cfg.dpi != 0) ''
+    ln -s ${dpiConf} $support_folder/11-dpi.conf
+    ln -s ${dpiConf} $latest_folder/11-dpi.conf
+    ''}
+
+    ${optionalString (!cfg.includeUserConf) ''
+    rm $support_folder/50-user.conf
+    rm $latest_folder/50-user.conf
+    ''}
+
+    # 51-local.conf
+    rm $latest_folder/51-local.conf
+    substitute \
+      ${pkgs.fontconfig-penultimate}/etc/fonts/conf.d/51-local.conf \
+      $latest_folder/51-local.conf \
+      --replace local.conf /etc/fonts/${latestVersion}/local.conf
+
+    ln -s ${defaultFontsConf} $support_folder/52-default-fonts.conf
+    ln -s ${defaultFontsConf} $latest_folder/52-default-fonts.conf
+
+    ${optionalString cfg.allowBitmaps ''
+    rm $support_folder/53-no-bitmaps.conf
+    rm $latest_folder/53-no-bitmaps.conf
+    ''}
+
+    ${optionalString (!cfg.allowType1) ''
+    ln -s ${rejectType1} $support_folder/53-no-type1.conf
+    ln -s ${rejectType1} $latest_folder/53-no-type1.conf
+    ''}
+  '';
+
+  hintingConf = pkgs.writeText "fc-10-hinting.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="hinting">
+          ${fcBool cfg.hinting.enable}
+        </edit>
+        <edit mode="append" name="autohint">
+          ${fcBool cfg.hinting.autohint}
+        </edit>
+        <edit mode="append" name="hintstyle">
+          <const>hintslight</const>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  antialiasConf = pkgs.writeText "fc-10-antialias.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="antialias">
+          ${fcBool cfg.antialias}
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  subpixelConf = pkgs.writeText "fc-10-subpixel.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="rgba">
+          <const>${cfg.subpixel.rgba}</const>
+        </edit>
+        <edit mode="append" name="lcdfilter">
+          <const>lcd${cfg.subpixel.lcdfilter}</const>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  dpiConf = pkgs.writeText "fc-11-dpi.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <match target="pattern">
+        <edit name="dpi" mode="assign">
+          <double>${toString cfg.dpi}</double>
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  defaultFontsConf =
+    let genDefault = fonts: name:
+      optionalString (fonts != []) ''
+        <alias>
+          <family>${name}</family>
+          <prefer>
+          ${concatStringsSep ""
+          (map (font: ''
+            <family>${font}</family>
+          '') fonts)}
+          </prefer>
+        </alias>
+      '';
+    in
+    pkgs.writeText "fc-52-nixos-default-fonts.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default fonts -->
+      ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
+
+      ${genDefault cfg.defaultFonts.serif     "serif"}
+
+      ${genDefault cfg.defaultFonts.monospace "monospace"}
+
+    </fontconfig>
+  '';
+
+  rejectType1 = pkgs.writeText "fc-53-no-type1.conf" ''
+    <?xml version="1.0"?>
+    <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+    <fontconfig>
+
+    <!-- Reject Type 1 fonts -->
+    <selectfont>
+      <rejectfont>
+        <pattern>
+          <patelt name="fontformat"><string>Type 1</string></patelt>
+        </pattern>
+      </rejectfont>
+    </selectfont>
+
+    </fontconfig>
+  '';
+
+in
+{
+
+  options = {
+
+    fonts = {
+
+      fontconfig = {
+
+        penultimate = {
+          enable = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Enable fontconfig-penultimate settings to supplement the
+              NixOS defaults by providing per-font rendering defaults and
+              metric aliases.
+            '';
+          };
+        };
+
+      };
+    };
+
+  };
+
+  config = mkIf (config.fonts.fontconfig.enable && cfg.enable) {
+
+    fonts.fontconfig.confPackages = [ penultimateConf ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontconfig-ultimate.nix b/nixos/modules/config/fonts/fontconfig-ultimate.nix
index a3f52fbd9199..1bf9fdb19845 100644
--- a/nixos/modules/config/fonts/fontconfig-ultimate.nix
+++ b/nixos/modules/config/fonts/fontconfig-ultimate.nix
@@ -8,61 +8,6 @@ let fcBool = x: if x then "<bool>true</bool>" else "<bool>false</bool>";
 
     latestVersion  = pkgs.fontconfig.configVersion;
 
-    # fontconfig ultimate main configuration file
-    # priority 52
-    fontconfigUltimateConf = pkgs.writeText "fc-52-fontconfig-ultimate.conf" ''
-      <?xml version="1.0"?>
-      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
-      <fontconfig>
-
-        ${optionalString (!cfg.allowBitmaps) ''
-        <!-- Reject bitmap fonts -->
-        <selectfont>
-          <rejectfont>
-            <pattern>
-              <patelt name="scalable"><bool>false</bool></patelt>
-            </pattern>
-          </rejectfont>
-        </selectfont>
-        ''}
-
-        ${optionalString cfg.allowType1 ''
-        <!-- Reject Type 1 fonts -->
-        <selectfont>
-          <rejectfont>
-            <pattern>
-              <patelt name="fontformat">
-                <string>Type 1</string>
-              </patelt>
-            </pattern>
-          </rejectfont>
-        </selectfont>
-        ''}
-
-        <!-- Use embedded bitmaps in fonts like Calibri? -->
-        <match target="font">
-          <edit name="embeddedbitmap" mode="assign">
-            ${fcBool cfg.useEmbeddedBitmaps}
-          </edit>
-        </match>
-
-        <!-- Force autohint always -->
-        <match target="font">
-          <edit name="force_autohint" mode="assign">
-            ${fcBool cfg.forceAutohint}
-          </edit>
-        </match>
-
-        <!-- Render some monospace TTF fonts as bitmaps -->
-        <match target="pattern">
-          <edit name="bitmap_monospace" mode="assign">
-            ${fcBool cfg.renderMonoTTFAsBitmap}
-          </edit>
-        </match>
-
-      </fontconfig>
-    '';
-
     # The configuration to be included in /etc/font/
     confPkg = pkgs.runCommand "font-ultimate-conf" {} ''
       support_folder=$out/etc/fonts/conf.d
@@ -71,12 +16,6 @@ let fcBool = x: if x then "<bool>true</bool>" else "<bool>false</bool>";
       mkdir -p $support_folder
       mkdir -p $latest_folder
 
-      # 52-fontconfig-ultimate.conf
-      ln -s ${fontconfigUltimateConf} \
-            $support_folder/52-fontconfig-ultimate.conf
-      ln -s ${fontconfigUltimateConf} \
-            $latest_folder/52-fontconfig-ultimate.conf
-
       # fontconfig ultimate substitutions
       ${optionalString (cfg.substitutions != "none") ''
       ln -s ${pkgs.fontconfig-ultimate}/etc/fonts/presets/${cfg.substitutions}/*.conf \
@@ -104,7 +43,7 @@ in
         ultimate = {
           enable = mkOption {
             type = types.bool;
-            default = true;
+            default = false;
             description = ''
               Enable fontconfig-ultimate settings (formerly known as
               Infinality). Besides the customizable settings in this NixOS
@@ -113,45 +52,6 @@ in
             '';
           };
 
-          allowBitmaps = mkOption {
-            type = types.bool;
-            default = true;
-            description = ''
-              Allow bitmap fonts. Set to <literal>false</literal> to ban all
-              bitmap fonts.
-            '';
-          };
-
-          allowType1 = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Allow Type-1 fonts. Default is <literal>false</literal> because of
-              poor rendering.
-            '';
-          };
-
-          useEmbeddedBitmaps = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''Use embedded bitmaps in fonts like Calibri.'';
-          };
-
-          forceAutohint = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Force use of the TrueType Autohinter. Useful for debugging or
-              free-software purists.
-            '';
-          };
-
-          renderMonoTTFAsBitmap = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''Render some monospace TTF fonts as bitmaps.'';
-          };
-
           substitutions = mkOption {
             type = types.nullOr (types.enum ["free" "combi" "ms"]);
             default = "free";
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 52ad1e714fb9..12f5ca2e7993 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -20,7 +20,7 @@ with lib;
 
 let cfg = config.fonts.fontconfig;
 
-    fcBool = x: "<bool>" + (if x then "true" else "false") + "</bool>";
+    fcBool = x: "<bool>" + (boolToString x) + "</bool>";
 
     # back-supported fontconfig version and package
     # version is used for font cache generation
@@ -41,11 +41,11 @@ let cfg = config.fonts.fontconfig;
     # priority 0
     cacheConfSupport = makeCacheConf { version = supportVersion; };
     cacheConfLatest  = makeCacheConf {};
-    
+
     # generate the font cache setting file for a fontconfig version
     # use latest when no version is passed
     makeCacheConf = { version ? null }:
-      let 
+      let
         fcPackage = if builtins.isNull version
                     then "fontconfig"
                     else "fontconfig_${version}";
@@ -75,23 +75,23 @@ let cfg = config.fonts.fontconfig;
       <fontconfig>
 
         <!-- Default rendering settings -->
-        <match target="font">
-          <edit mode="assign" name="hinting">
+        <match target="pattern">
+          <edit mode="append" name="hinting">
             ${fcBool cfg.hinting.enable}
           </edit>
-          <edit mode="assign" name="autohint">
+          <edit mode="append" name="autohint">
             ${fcBool cfg.hinting.autohint}
           </edit>
-          <edit mode="assign" name="hintstyle">
-            <const>hint${cfg.hinting.style}</const>
+          <edit mode="append" name="hintstyle">
+            <const>hintslight</const>
           </edit>
-          <edit mode="assign" name="antialias">
+          <edit mode="append" name="antialias">
             ${fcBool cfg.antialias}
           </edit>
-          <edit mode="assign" name="rgba">
+          <edit mode="append" name="rgba">
             <const>${cfg.subpixel.rgba}</const>
           </edit>
-          <edit mode="assign" name="lcdfilter">
+          <edit mode="append" name="lcdfilter">
             <const>lcd${cfg.subpixel.lcdfilter}</const>
           </edit>
         </match>
@@ -113,7 +113,7 @@ let cfg = config.fonts.fontconfig;
 
     # default fonts configuration file
     # priority 52
-    defaultFontsConf = 
+    defaultFontsConf =
       let genDefault = fonts: name:
         optionalString (fonts != []) ''
           <alias>
@@ -142,7 +142,54 @@ let cfg = config.fonts.fontconfig;
       </fontconfig>
     '';
 
-    # fontconfig configuration package 
+    # bitmap font options
+    # priority 53
+    rejectBitmaps = pkgs.writeText "fc-53-nixos-bitmaps.conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+
+      ${optionalString (!cfg.allowBitmaps) ''
+      <!-- Reject bitmap fonts -->
+      <selectfont>
+        <rejectfont>
+          <pattern>
+            <patelt name="scalable"><bool>false</bool></patelt>
+          </pattern>
+        </rejectfont>
+      </selectfont>
+      ''}
+
+      <!-- Use embedded bitmaps in fonts like Calibri? -->
+      <match target="font">
+        <edit name="embeddedbitmap" mode="assign">
+          ${fcBool cfg.useEmbeddedBitmaps}
+        </edit>
+      </match>
+
+      </fontconfig>
+    '';
+
+    # reject Type 1 fonts
+    # priority 53
+    rejectType1 = pkgs.writeText "fc-53-nixos-reject-type1.conf" ''
+      <?xml version="1.0"?>
+      <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+      <fontconfig>
+
+      <!-- Reject Type 1 fonts -->
+      <selectfont>
+        <rejectfont>
+          <pattern>
+            <patelt name="fontformat"><string>Type 1</string></patelt>
+          </pattern>
+        </rejectfont>
+      </selectfont>
+
+      </fontconfig>
+    '';
+
+    # fontconfig configuration package
     confPkg = pkgs.runCommand "fontconfig-conf" {} ''
       support_folder=$out/etc/fonts
       latest_folder=$out/etc/fonts/${latestVersion}
@@ -166,7 +213,7 @@ let cfg = config.fonts.fontconfig;
 
       substitute ${latestPkg.out}/etc/fonts/conf.d/51-local.conf \
                  $latest_folder/conf.d/51-local.conf \
-                 --replace local.conf /etc/fonts/${latestVersion}/local.conf 
+                 --replace local.conf /etc/fonts/${latestVersion}/local.conf
 
       # 00-nixos-cache.conf
       ln -s ${cacheConfSupport} \
@@ -192,6 +239,16 @@ let cfg = config.fonts.fontconfig;
       # 52-nixos-default-fonts.conf
       ln -s ${defaultFontsConf} $support_folder/conf.d/52-nixos-default-fonts.conf
       ln -s ${defaultFontsConf} $latest_folder/conf.d/52-nixos-default-fonts.conf
+
+      # 53-nixos-bitmaps.conf
+      ln -s ${rejectBitmaps} $support_folder/conf.d/53-nixos-bitmaps.conf
+      ln -s ${rejectBitmaps} $latest_folder/conf.d/53-nixos-bitmaps.conf
+
+      ${optionalString (! cfg.allowType1) ''
+      # 53-nixos-reject-type1.conf
+      ln -s ${rejectType1} $support_folder/conf.d/53-nixos-reject-type1.conf
+      ln -s ${rejectType1} $latest_folder/conf.d/53-nixos-reject-type1.conf
+      ''}
     '';
 
     # Package with configuration files
@@ -233,7 +290,11 @@ in
         antialias = mkOption {
           type = types.bool;
           default = true;
-          description = "Enable font antialiasing.";
+          description = ''
+            Enable font antialiasing. At high resolution (> 200 DPI),
+            antialiasing has no visible effect; users of such displays may want
+            to disable this option.
+          '';
         };
 
         dpi = mkOption {
@@ -249,7 +310,7 @@ in
           type = types.lines;
           default = "";
           description = ''
-            System-wide customization file contents, has higher priority than 
+            System-wide customization file contents, has higher priority than
             <literal>defaultFonts</literal> settings.
           '';
         };
@@ -287,26 +348,21 @@ in
           enable = mkOption {
             type = types.bool;
             default = true;
-            description = "Enable TrueType hinting.";
-          };
-
-          autohint = mkOption {
-            type = types.bool;
-            default = true;
             description = ''
-              Enable the autohinter, which provides hinting for otherwise
-              un-hinted fonts. The results are usually lower quality than
-              correctly-hinted fonts.
+              Enable font hinting. Hinting aligns glyphs to pixel boundaries to
+              improve rendering sharpness at low resolution. At high resolution
+              (> 200 dpi) hinting will do nothing (at best); users of such
+              displays may want to disable this option.
             '';
           };
 
-          style = mkOption {
-            type = types.enum ["none" "slight" "medium" "full"];
-            default = "full";
+          autohint = mkOption {
+            type = types.bool;
+            default = false;
             description = ''
-              TrueType hinting style, one of <literal>none</literal>,
-              <literal>slight</literal>, <literal>medium</literal>, or
-              <literal>full</literal>.
+              Enable the autohinter in place of the default interpreter.
+              The results are usually lower quality than correctly-hinted
+              fonts, but better than unhinted fonts.
             '';
           };
         };
@@ -327,7 +383,15 @@ in
             default = "rgb";
             type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
             description = ''
-              Subpixel order.
+              Subpixel order. The overwhelming majority of displays are
+              <literal>rgb</literal> in their normal orientation. Select
+              <literal>vrgb</literal> for mounting such a display 90 degrees
+              clockwise from its normal orientation or <literal>vbgr</literal>
+              for mounting 90 degrees counter-clockwise. Select
+              <literal>bgr</literal> in the unlikely event of mounting 180
+              degrees from the normal orientation. Reverse these directions in
+              the improbable event that the display's native subpixel order is
+              <literal>bgr</literal>.
             '';
           };
 
@@ -335,7 +399,9 @@ in
             default = "default";
             type = types.enum ["none" "default" "light" "legacy"];
             description = ''
-              FreeType LCD filter.
+              FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering
+              has no visible effect; users of such displays may want to select
+              <literal>none</literal>.
             '';
           };
 
@@ -349,16 +415,43 @@ in
           '';
         };
 
+        allowBitmaps = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Allow bitmap fonts. Set to <literal>false</literal> to ban all
+            bitmap fonts.
+          '';
+        };
+
+        allowType1 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Allow Type-1 fonts. Default is <literal>false</literal> because of
+            poor rendering.
+          '';
+        };
+
+        useEmbeddedBitmaps = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''Use embedded bitmaps in fonts like Calibri.'';
+        };
+
       };
 
     };
 
   };
-  config = mkIf cfg.enable {
-    fonts.fontconfig.confPackages = [ confPkg ];
-
-    environment.systemPackages    = [ pkgs.fontconfig ];
-    environment.etc.fonts.source  = "${fontconfigEtc}/etc/fonts/";
-  };
+  config = mkMerge [
+    (mkIf cfg.enable {
+      environment.systemPackages    = [ pkgs.fontconfig ];
+      environment.etc.fonts.source  = "${fontconfigEtc}/etc/fonts/";
+    })
+    (mkIf (cfg.enable && !cfg.penultimate.enable) {
+      fonts.fontconfig.confPackages = [ confPkg ];
+    })
+  ];
 
 }
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index af3d93fc1bc4..0dd01df9da74 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -37,6 +37,7 @@ with lib;
         pkgs.xorg.fontbhlucidatypewriter75dpi
         pkgs.dejavu_fonts
         pkgs.freefont_ttf
+        pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
         pkgs.liberation_ttf
         pkgs.xorg.fontbh100dpi
         pkgs.xorg.fontmiscmisc
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 799f0793c74f..65ef95127805 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -2,21 +2,27 @@
 
 with lib;
 
-let
-
-  glibcLocales = pkgs.glibcLocales.override {
-    allLocales = any (x: x == "all") config.i18n.supportedLocales;
-    locales = config.i18n.supportedLocales;
-  };
-
-in
-
 {
   ###### interface
 
   options = {
 
     i18n = {
+      glibcLocales = mkOption {
+        type = types.path;
+        default = pkgs.glibcLocales.override {
+          allLocales = any (x: x == "all") config.i18n.supportedLocales;
+          locales = config.i18n.supportedLocales;
+        };
+        example = literalExample "pkgs.glibcLocales";
+        description = ''
+          Customized pkg.glibcLocales package.
+
+          Changing this option can disable handling of i18n.defaultLocale
+          and supportedLocale.
+        '';
+      };
+
       defaultLocale = mkOption {
         type = types.str;
         default = "en_US.UTF-8";
@@ -118,7 +124,7 @@ in
         '');
 
     environment.systemPackages =
-      optional (config.i18n.supportedLocales != []) glibcLocales;
+      optional (config.i18n.supportedLocales != []) config.i18n.glibcLocales;
 
     environment.sessionVariables =
       { LANG = config.i18n.defaultLocale;
@@ -126,7 +132,7 @@ in
       };
 
     systemd.globalEnvironment = mkIf (config.i18n.supportedLocales != []) {
-      LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";
+      LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
     };
 
     # ‘/etc/locale.conf’ is used by systemd.
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 9e7cfbd686cc..ae30a710bf6f 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -13,7 +13,7 @@ let
 
   resolvconfOptions = cfg.resolvconfOptions
     ++ optional cfg.dnsSingleRequest "single-request"
-    ++ optional cfg.dnsExtensionMechanism "ends0";
+    ++ optional cfg.dnsExtensionMechanism "edns0";
 in
 
 {
@@ -178,10 +178,10 @@ in
 
     environment.etc =
       { # /etc/services: TCP/UDP port assignments.
-        "services".source = pkgs.iana_etc + "/etc/services";
+        "services".source = pkgs.iana-etc + "/etc/services";
 
         # /etc/protocols: IP protocol numbers.
-        "protocols".source  = pkgs.iana_etc + "/etc/protocols";
+        "protocols".source  = pkgs.iana-etc + "/etc/protocols";
 
         # /etc/rpc: RPC program numbers.
         "rpc".source = pkgs.glibc.out + "/etc/rpc";
@@ -251,11 +251,6 @@ in
     # Install the proxy environment variables
     environment.sessionVariables = cfg.proxy.envVars;
 
-    # The ‘ip-up’ target is kept for backwards compatibility.
-    # New services should use systemd upstream targets:
-    # See https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/
-    systemd.targets.ip-up.description = "Services Requiring IP Connectivity (deprecated)";
-
     # This is needed when /etc/resolv.conf is being overriden by networkd
     # and other configurations. If the file is destroyed by an environment
     # activation then it must be rebuilt so that applications which interface
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index d5cb4fce0f9d..bf66994b5022 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -108,7 +108,7 @@ in {
         type = types.bool;
         default = false;
         description = ''
-          Whether to include the 32-bit pulseaudio libraries in the systemn or not.
+          Whether to include the 32-bit pulseaudio libraries in the system or not.
           This is only useful on 64-bit systems and currently limited to x86_64-linux.
         '';
       };
@@ -274,6 +274,8 @@ in {
           RestartSec = "500ms";
         };
       };
+
+      environment.variables.PULSE_COOKIE = "${stateDir}/.config/pulse/cookie";
     })
   ];
 
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 8147fed39d09..152493151fd3 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -168,9 +168,6 @@ in
 
          ${cfg.extraInit}
 
-         # The setuid wrappers override other bin directories.
-         export PATH="${config.security.wrapperDir}:$PATH"
-
          # ~/bin if it exists overrides other bin directories.
          export PATH="$HOME/bin:$PATH"
        '';
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
index 61b02c5ffa6a..a3f7e8f722f0 100644
--- a/nixos/modules/config/sysctl.nix
+++ b/nixos/modules/config/sysctl.nix
@@ -64,5 +64,9 @@ in
     # Removed under grsecurity.
     boot.kernel.sysctl."kernel.kptr_restrict" =
       if (config.boot.kernelPackages.kernel.features.grsecurity or false) then null else 1;
+
+    # Disable YAMA by default to allow easy debugging.
+    boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkDefault 0;
+
   };
 }
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index cbbe216e5a17..ef5e6346f02e 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -6,6 +6,21 @@ use JSON;
 make_path("/var/lib/nixos", { mode => 0755 });
 
 
+# Keep track of deleted uids and gids.
+my $uidMapFile = "/var/lib/nixos/uid-map";
+my $uidMap = -e $uidMapFile ? decode_json(read_file($uidMapFile)) : {};
+
+my $gidMapFile = "/var/lib/nixos/gid-map";
+my $gidMap = -e $gidMapFile ? decode_json(read_file($gidMapFile)) : {};
+
+
+sub updateFile {
+    my ($path, $contents, $perms) = @_;
+    write_file("$path.tmp", { binmode => ':utf8', perms => $perms // 0644 }, $contents);
+    rename("$path.tmp", $path) or die;
+}
+
+
 sub hashPassword {
     my ($password) = @_;
     my $salt = "";
@@ -18,10 +33,10 @@ sub hashPassword {
 # Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
 # /etc/login.defs.
 sub allocId {
-    my ($used, $idMin, $idMax, $up, $getid) = @_;
+    my ($used, $prevUsed, $idMin, $idMax, $up, $getid) = @_;
     my $id = $up ? $idMin : $idMax;
     while ($id >= $idMin && $id <= $idMax) {
-        if (!$used->{$id} && !defined &$getid($id)) {
+        if (!$used->{$id} && !$prevUsed->{$id} && !defined &$getid($id)) {
             $used->{$id} = 1;
             return $id;
         }
@@ -31,23 +46,36 @@ sub allocId {
     die "$0: out of free UIDs or GIDs\n";
 }
 
-my (%gidsUsed, %uidsUsed);
+my (%gidsUsed, %uidsUsed, %gidsPrevUsed, %uidsPrevUsed);
 
 sub allocGid {
-    return allocId(\%gidsUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
+    my ($name) = @_;
+    my $prevGid = $gidMap->{$name};
+    if (defined $prevGid && !defined $gidsUsed{$prevGid}) {
+        print STDERR "reviving group '$name' with GID $prevGid\n";
+        $gidsUsed{$prevGid} = 1;
+        return $prevGid;
+    }
+    return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 499, 0, sub { my ($gid) = @_; getgrgid($gid) });
 }
 
 sub allocUid {
-    my ($isSystemUser) = @_;
+    my ($name, $isSystemUser) = @_;
     my ($min, $max, $up) = $isSystemUser ? (400, 499, 0) : (1000, 29999, 1);
-    return allocId(\%uidsUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+    my $prevUid = $uidMap->{$name};
+    if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
+        print STDERR "reviving user '$name' with UID $prevUid\n";
+        $uidsUsed{$prevUid} = 1;
+        return $prevUid;
+    }
+    return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
 }
 
 
 # Read the declared users/groups.
 my $spec = decode_json(read_file($ARGV[0]));
 
-# Don't allocate UIDs/GIDs that are already in use.
+# Don't allocate UIDs/GIDs that are manually assigned.
 foreach my $g (@{$spec->{groups}}) {
     $gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
 }
@@ -56,6 +84,11 @@ foreach my $u (@{$spec->{users}}) {
     $uidsUsed{$u->{uid}} = 1 if defined $u->{uid};
 }
 
+# Likewise for previously used but deleted UIDs/GIDs.
+$uidsPrevUsed{$_} = 1 foreach values %{$uidMap};
+$gidsPrevUsed{$_} = 1 foreach values %{$gidMap};
+
+
 # Read the current /etc/group.
 sub parseGroup {
     chomp;
@@ -114,16 +147,18 @@ foreach my $g (@{$spec->{groups}}) {
             }
         }
     } else {
-        $g->{gid} = allocGid if !defined $g->{gid};
+        $g->{gid} = allocGid($name) if !defined $g->{gid};
         $g->{password} = "x";
     }
 
     $g->{members} = join ",", sort(keys(%members));
     $groupsOut{$name} = $g;
+
+    $gidMap->{$name} = $g->{gid};
 }
 
 # Update the persistent list of declarative groups.
-write_file($declGroupsFile, { binmode => ':utf8' }, join(" ", sort(keys %groupsOut)));
+updateFile($declGroupsFile, join(" ", sort(keys %groupsOut)));
 
 # Merge in the existing /etc/group.
 foreach my $name (keys %groupsCur) {
@@ -140,8 +175,8 @@ foreach my $name (keys %groupsCur) {
 # Rewrite /etc/group. FIXME: acquire lock.
 my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members}) . "\n" }
     (sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
-write_file("/etc/group.tmp", { binmode => ':utf8' }, @lines);
-rename("/etc/group.tmp", "/etc/group") or die;
+updateFile($gidMapFile, encode_json($gidMap));
+updateFile("/etc/group", \@lines);
 system("nscd --invalidate group");
 
 # Generate a new /etc/passwd containing the declared users.
@@ -167,7 +202,7 @@ foreach my $u (@{$spec->{users}}) {
             $u->{uid} = $existing->{uid};
         }
     } else {
-        $u->{uid} = allocUid($u->{isSystemUser}) if !defined $u->{uid};
+        $u->{uid} = allocUid($name, $u->{isSystemUser}) if !defined $u->{uid};
 
         if (defined $u->{initialPassword}) {
             $u->{hashedPassword} = hashPassword($u->{initialPassword});
@@ -177,7 +212,7 @@ foreach my $u (@{$spec->{users}}) {
     }
 
     # Create a home directory.
-    if ($u->{createHome} && ! -e $u->{home}) {
+    if ($u->{createHome}) {
         make_path($u->{home}, { mode => 0700 }) if ! -e $u->{home};
         chown $u->{uid}, $u->{gid}, $u->{home};
     }
@@ -195,10 +230,12 @@ foreach my $u (@{$spec->{users}}) {
 
     $u->{fakePassword} = $existing->{fakePassword} // "x";
     $usersOut{$name} = $u;
+
+    $uidMap->{$name} = $u->{uid};
 }
 
 # Update the persistent list of declarative users.
-write_file($declUsersFile, { binmode => ':utf8' }, join(" ", sort(keys %usersOut)));
+updateFile($declUsersFile, join(" ", sort(keys %usersOut)));
 
 # Merge in the existing /etc/passwd.
 foreach my $name (keys %usersCur) {
@@ -214,8 +251,8 @@ foreach my $name (keys %usersCur) {
 # Rewrite /etc/passwd. FIXME: acquire lock.
 @lines = map { join(":", $_->{name}, $_->{fakePassword}, $_->{uid}, $_->{gid}, $_->{description}, $_->{home}, $_->{shell}) . "\n" }
     (sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
-write_file("/etc/passwd.tmp", { binmode => ':utf8' }, @lines);
-rename("/etc/passwd.tmp", "/etc/passwd") or die;
+updateFile($uidMapFile, encode_json($uidMap));
+updateFile("/etc/passwd", \@lines);
 system("nscd --invalidate passwd");
 
 
@@ -242,5 +279,4 @@ foreach my $u (values %usersOut) {
     push @shadowNew, join(":", $u->{name}, $hashedPassword, "1::::::") . "\n";
 }
 
-write_file("/etc/shadow.tmp", { binmode => ':utf8', perms => 0600 }, @shadowNew);
-rename("/etc/shadow.tmp", "/etc/shadow") or die;
+updateFile("/etc/shadow", \@shadowNew, 0600);
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index fb8e1ccab667..bc82bfd066c3 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -26,6 +26,7 @@ with lib;
       firmwareLinuxNonfree
       intel2200BGFirmware
       rtl8723bs-firmware
+      rtl8192su-firmware
     ];
   };
 
diff --git a/nixos/modules/hardware/cpu/amd-microcode.nix b/nixos/modules/hardware/cpu/amd-microcode.nix
index d44f01a49590..621c7066bfe1 100644
--- a/nixos/modules/hardware/cpu/amd-microcode.nix
+++ b/nixos/modules/hardware/cpu/amd-microcode.nix
@@ -22,7 +22,8 @@ with lib;
   ###### implementation
 
   config = mkIf config.hardware.cpu.amd.updateMicrocode {
-    boot.initrd.prepend = [ "${pkgs.microcodeAmd}/amd-ucode.img" ];
+    # Microcode updates must be the first item prepended in the initrd
+    boot.initrd.prepend = mkOrder 1 [ "${pkgs.microcodeAmd}/amd-ucode.img" ];
   };
 
 }
diff --git a/nixos/modules/hardware/cpu/intel-microcode.nix b/nixos/modules/hardware/cpu/intel-microcode.nix
index 89ae4f45806c..acce565fd808 100644
--- a/nixos/modules/hardware/cpu/intel-microcode.nix
+++ b/nixos/modules/hardware/cpu/intel-microcode.nix
@@ -22,7 +22,8 @@ with lib;
   ###### implementation
 
   config = mkIf config.hardware.cpu.intel.updateMicrocode {
-    boot.initrd.prepend = [ "${pkgs.microcodeIntel}/intel-ucode.img" ];
+    # Microcode updates must be the first item prepended in the initrd
+    boot.initrd.prepend = mkOrder 1 [ "${pkgs.microcodeIntel}/intel-ucode.img" ];
   };
 
 }
diff --git a/nixos/modules/hardware/mcelog.nix b/nixos/modules/hardware/mcelog.nix
new file mode 100644
index 000000000000..e4ac7d39053f
--- /dev/null
+++ b/nixos/modules/hardware/mcelog.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.grahamc ];
+  options = {
+
+    hardware.mcelog = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the Machine Check Exception logger.
+        '';
+      };
+    };
+
+  };
+
+  config = mkIf config.hardware.mcelog.enable {
+    systemd.services.mcelog = {
+      description = "Machine Check Exception Logging Daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.mcelog}/bin/mcelog --daemon --foreground";
+        SuccessExitStatus = [ 0 15 ];
+
+        ProtectHome = true;
+        PrivateNetwork = true;
+        PrivateTmp = true;
+      };
+    };
+  };
+
+}
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index c4fad9a66725..486fe7c1cd8f 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -96,7 +96,7 @@ in
       example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
       description = ''
         Additional packages to add to OpenGL drivers. This can be used
-        to add additional VA-API/VDPAU drivers.
+        to add OpenCL drivers, VA-API/VDPAU drivers etc.
       '';
     };
 
@@ -107,7 +107,7 @@ in
       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 additional VA-API/VDPAU drivers.
+        set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
       '';
     };
 
@@ -133,13 +133,10 @@ in
       '';
 
     environment.sessionVariables.LD_LIBRARY_PATH =
-      [ "/run/opengl-driver/lib" "/run/opengl-driver-32/lib" ];
+      [ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib";
 
-    environment.extraInit = ''
-      export XDG_DATA_DIRS=$XDG_DATA_DIRS:/run/opengl-driver/share
-    '' + optionalString cfg.driSupport32Bit ''
-      export XDG_DATA_DIRS=$XDG_DATA_DIRS:/run/opengl-driver-32/share
-    '';
+    environment.variables.XDG_DATA_DIRS =
+      [ "/run/opengl-driver/share" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/share";
 
     hardware.opengl.package = mkDefault (makePackage pkgs);
     hardware.opengl.package32 = mkDefault (makePackage pkgs_i686);
diff --git a/nixos/modules/hardware/sensor/iio.nix b/nixos/modules/hardware/sensor/iio.nix
new file mode 100644
index 000000000000..a8bc18800021
--- /dev/null
+++ b/nixos/modules/hardware/sensor/iio.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+    hardware.sensor.iio = {
+      enable = mkOption {
+        description = "Enable this option to support IIO sensors.";
+        type = types.bool;
+        default = false;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.sensor.iio.enable {
+
+    boot.initrd.availableKernelModules = [ "hid-sensor-hub" ];
+
+    environment.systemPackages = with pkgs; [ iio-sensor-proxy ];
+
+    services.dbus.packages = with pkgs; [ iio-sensor-proxy ];
+    services.udev.packages = with pkgs; [ iio-sensor-proxy ];
+    systemd.packages = with pkgs; [ iio-sensor-proxy ];
+  };
+}
diff --git a/nixos/modules/hardware/usb-wwan.nix b/nixos/modules/hardware/usb-wwan.nix
new file mode 100644
index 000000000000..2d20421586a7
--- /dev/null
+++ b/nixos/modules/hardware/usb-wwan.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+
+    hardware.usbWwan = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable this option to support USB WWAN adapters.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.usbWwan.enable {
+    services.udev.packages = with pkgs; [ usb-modeswitch-data ];
+  };
+}
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index 979810abf90a..5cc96d8bd074 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -21,6 +21,8 @@ in
 
   config = mkIf enabled {
 
+    nixpkgs.config.xorg.abiCompat = "1.18";
+
     services.xserver.drivers = singleton
       { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
 
@@ -44,9 +46,6 @@ in
       "amd/amdrc".source = package + "/etc/amd/amdrc";
       "amd/amdapfxx.blb".source = package + "/etc/amd/amdapfxx.blb";
       "gbm/gbm.conf".source = package + "/etc/gbm/gbm.conf";
-      "OpenCL/vendors/amdocl64.icd".source = package + "/etc/OpenCL/vendors/amdocl64.icd";
-    } // optionalAttrs opengl.driSupport32Bit {
-      "OpenCL/vendors/amdocl32.icd".source = package32 + "/etc/OpenCL/vendors/amdocl32.icd";
     };
 
   };
diff --git a/nixos/modules/hardware/video/ati.nix b/nixos/modules/hardware/video/ati.nix
index bf91bcf0776b..022fdea0a0a3 100644
--- a/nixos/modules/hardware/video/ati.nix
+++ b/nixos/modules/hardware/video/ati.nix
@@ -18,7 +18,7 @@ in
 
   config = mkIf enabled {
 
-    nixpkgs.config.xorg.fglrxCompat = true;
+    nixpkgs.config.xorg.abiCompat = "1.17";
 
     services.xserver.drivers = singleton
       { name = "fglrx"; modules = [ ati_x11 ]; libPath = [ "${ati_x11}/lib" ]; };
diff --git a/nixos/modules/hardware/video/bumblebee.nix b/nixos/modules/hardware/video/bumblebee.nix
index 3ce97ad31c22..2278c7b40611 100644
--- a/nixos/modules/hardware/video/bumblebee.nix
+++ b/nixos/modules/hardware/video/bumblebee.nix
@@ -13,7 +13,7 @@ let
     useDisplayDevice = cfg.connectDisplay;
   };
 
-  useBbswitch = cfg.pmMethod == "bbswitch";
+  useBbswitch = cfg.pmMethod == "bbswitch" || cfg.pmMethod == "auto" && useNvidia;
 
   primus = pkgs.primus.override {
     inherit useNvidia;
@@ -65,7 +65,7 @@ in
 
       pmMethod = mkOption {
         default = "auto";
-        type = types.enum [ "auto" "bbswitch" "nouveau" "switcheroo" "none" ];
+        type = types.enum [ "auto" "bbswitch" "switcheroo" "none" ];
         description = ''
           Set preferred power management method for unused card.
         '';
@@ -76,8 +76,8 @@ in
 
   config = mkIf cfg.enable {
     boot.blacklistedKernelModules = [ "nvidia-drm" "nvidia" "nouveau" ];
-    boot.kernelModules = optional useBbswitch [ "bbswitch" ];
-    boot.extraModulePackages = optional useBbswitch kernel.bbswitch ++ optional useNvidia kernel.nvidia_x11;
+    boot.kernelModules = optional useBbswitch "bbswitch";
+    boot.extraModulePackages = optional useBbswitch kernel.bbswitch ++ optional useNvidia kernel.nvidia_x11.bin;
 
     environment.systemPackages = [ bumblebee primus ];
 
diff --git a/nixos/modules/hardware/video/capture/mwprocapture.nix b/nixos/modules/hardware/video/capture/mwprocapture.nix
new file mode 100644
index 000000000000..aee15dcec6e5
--- /dev/null
+++ b/nixos/modules/hardware/video/capture/mwprocapture.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.mwProCapture;
+
+  kernelPackages = config.boot.kernelPackages;
+
+in
+
+{
+
+  options.hardware.mwProCapture.enable = mkEnableOption "Magewell Pro Capture family kernel module";
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = versionAtLeast kernelPackages.kernel.version "3.2";
+      message = "Magewell Pro Capture family module is not supported for kernels older than 3.2";
+    };
+
+    boot.kernelModules = [ "ProCapture" ];
+
+    environment.systemPackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModulePackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModprobeConfig = ''
+      # Set the png picture to be displayed when no input signal is detected.
+      options ProCapture nosignal_file=${kernelPackages.mwprocapture}/res/NoSignal.png
+
+      # Set the png picture to be displayed when an unsupported input signal is detected.
+      options ProCapture unsupported_file=${kernelPackages.mwprocapture}/res/Unsupported.png
+
+      # Set the png picture to be displayed when an loking input signal is detected.
+      options ProCapture locking_file=${kernelPackages.mwprocapture}/res/Locking.png
+
+      # Message signaled interrupts switch
+      #options ProCapture disable_msi=0
+
+      # Set the debug level
+      #options ProCapture debug_level=0
+
+      # Force init switch eeprom
+      #options ProCapture init_switch_eeprom=0
+
+      # Min frame interval for VIDIOC_ENUM_FRAMEINTERVALS (default: 166666(100ns))
+      #options ProCapture enum_frameinterval_min=166666
+
+      # VIDIOC_ENUM_FRAMESIZES type (1: DISCRETE; 2: STEPWISE; otherwise: CONTINUOUS )
+      #options ProCapture enum_framesizes_type=0
+
+      # Parameters for internal usage
+      #options ProCapture internal_params=""
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 8514f765e610..161ed9457af9 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -27,6 +27,13 @@ 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
+    else pkgs.buildEnv {
+      name = "nvidia-libs";
+      paths = [ pkgs.libglvnd nvidia.out ];
+    };
+
   enabled = nvidia_x11 != null;
 in
 
@@ -35,19 +42,23 @@ in
   config = mkIf enabled {
 
     services.xserver.drivers = singleton
-      { name = "nvidia"; modules = [ nvidia_x11 ]; libPath = [ nvidia_x11 ]; };
+      { name = "nvidia"; modules = [ nvidia_x11.bin ]; libPath = [ nvidia_x11 ]; };
 
     services.xserver.screenSection =
       ''
         Option "RandRRotation" "on"
       '';
 
-    hardware.opengl.package = nvidia_x11;
-    hardware.opengl.package32 = nvidia_libs32;
+    environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
+      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;
 
-    environment.systemPackages = [ nvidia_x11 ];
+    environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings nvidia_x11.persistenced ];
 
-    boot.extraModulePackages = [ nvidia_x11 ];
+    boot.extraModulePackages = [ nvidia_x11.bin ];
 
     # nvidia-uvm is required by CUDA applications.
     boot.kernelModules = [ "nvidia-uvm" ];
@@ -62,8 +73,6 @@ in
 
     services.acpid.enable = true;
 
-    environment.etc."OpenCL/vendors/nvidia.icd".source = "${nvidia_x11}/lib/vendors/nvidia.icd";
-
   };
 
 }
diff --git a/nixos/modules/i18n/input-method/ibus.nix b/nixos/modules/i18n/input-method/ibus.nix
index e23e28aa25ef..f8e021f551e8 100644
--- a/nixos/modules/i18n/input-method/ibus.nix
+++ b/nixos/modules/i18n/input-method/ibus.nix
@@ -10,6 +10,11 @@ let
     check = x: (lib.types.package.check x) && (attrByPath ["meta" "isIbusEngine"] false x);
   };
 
+  impanel =
+    if cfg.panel != null
+    then "--panel=${cfg.panel}"
+    else "";
+
   ibusAutostart = pkgs.writeTextFile {
     name = "autostart-ibus-daemon";
     destination = "/etc/xdg/autostart/ibus-daemon.desktop";
@@ -17,7 +22,7 @@ let
       [Desktop Entry]
       Name=IBus
       Type=Application
-      Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim
+      Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim ${impanel}
     '';
   };
 in
@@ -36,6 +41,12 @@ in
           in
             "Enabled IBus engines. Available engines are: ${engines}.";
       };
+      panel = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        example = literalExample "''${pkgs.plasma5.plasma-desktop}/lib/libexec/kimpanel-ibus-panel";
+        description = "Replace the IBus panel with another panel.";
+      };
     };
   };
 
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index cd6e72755dea..663ff24c81f1 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -6,6 +6,16 @@
 with lib;
 
 let
+  # Do not include these things:
+  #   - The '.git' directory
+  #   - Result symlinks from nix-build ('result', 'result-2', 'result-bin', ...)
+  #   - VIM/Emacs swap/backup files ('.swp', '.swo', '.foo.swp', 'foo~', ...)
+  filterFn = path: type: let basename = baseNameOf (toString path); in
+    if type == "directory" then basename != ".git"
+    else if type == "symlink" then builtins.match "^result(|-.*)$" basename == null
+    else builtins.match "^((|\..*)\.sw[a-z]|.*~)$" basename == null;
+
+  nixpkgs = builtins.filterSource filterFn pkgs.path;
 
   # We need a copy of the Nix expressions for Nixpkgs and NixOS on the
   # CD.  These are installed into the "nixos" channel of the root
@@ -15,12 +25,11 @@ let
     { }
     ''
       mkdir -p $out
-      cp -prd ${pkgs.path} $out/nixos
+      cp -prd ${nixpkgs} $out/nixos
       chmod -R u+w $out/nixos
       if [ ! -e $out/nixos/nixpkgs ]; then
         ln -s . $out/nixos/nixpkgs
       fi
-      rm -rf $out/nixos/.git
       echo -n ${config.system.nixosVersionSuffix} > $out/nixos/.version-suffix
     '';
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
index c44dff3bb60d..63227d573495 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
@@ -18,7 +18,7 @@ with lib;
       autoLogin = true;
     };
 
-    desktopManager.kde5 = {
+    desktopManager.plasma5 = {
       enable = true;
       enableQt4Support = false;
     };
@@ -66,7 +66,7 @@ with lib;
   in ''
     mkdir -p /root/Desktop
     ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
-    ln -sfT ${pkgs.kde5.konsole}/share/applications/org.kde.konsole.desktop /root/Desktop/org.kde.konsole.desktop
+    ln -sfT ${pkgs.konsole}/share/applications/org.kde.konsole.desktop /root/Desktop/org.kde.konsole.desktop
     ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop
   '';
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
index f4122ab0e518..7ec55f159d0e 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
@@ -7,9 +7,4 @@
   imports =
     [ ./installation-cd-base.nix
     ];
-
-  environment.systemPackages =
-    [
-      pkgs.vim
-    ];
 }
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 93dba0d882b8..d42174003526 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -172,7 +172,6 @@ in
 
     isoImage.includeSystemBuildDependencies = mkOption {
       default = false;
-      example = true;
       description = ''
         Set this option to include all the needed sources etc in the
         image. It significantly increases image size. Use that when
@@ -280,7 +279,7 @@ in
         options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ];
       };
 
-    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" ];
+    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" "uas" ];
 
     boot.blacklistedKernelModules = [ "nouveau" ];
 
diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
new file mode 100644
index 000000000000..c769bc80a481
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
@@ -0,0 +1,61 @@
+# To build, use:
+# nix-build nixos -I nixos-config=nixos/modules/installer/cd-dvd/sd-image-aarch64.nix -A config.system.build.sdImage
+{ config, lib, pkgs, ... }:
+
+let
+  extlinux-conf-builder =
+    import ../../system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.nix {
+      inherit pkgs;
+    };
+in
+{
+  imports = [
+    ../../profiles/minimal.nix
+    ../../profiles/installation-device.nix
+    ./sd-image.nix
+  ];
+
+  assertions = lib.singleton {
+    assertion = pkgs.stdenv.system == "aarch64-linux";
+    message = "sd-image-aarch64.nix can be only built natively on Aarch64 / ARM64; " +
+      "it cannot be cross compiled";
+  };
+
+  # Needed by RPi firmware
+  nixpkgs.config.allowUnfree = true;
+
+  boot.loader.grub.enable = false;
+  boot.loader.generic-extlinux-compatible.enable = true;
+
+  boot.kernelPackages = pkgs.linuxPackages_latest;
+  boot.kernelParams = ["console=ttyS0,115200n8" "console=tty0"];
+  boot.consoleLogLevel = 7;
+
+  # FIXME: this probably should be in installation-device.nix
+  users.extraUsers.root.initialHashedPassword = "";
+
+  sdImage = {
+    populateBootCommands = let
+      # Contains a couple of fixes for booting a Linux kernel, will hopefully appear upstream soon.
+      patchedUboot = pkgs.ubootRaspberryPi3_64bit.overrideAttrs (oldAttrs: {
+        src = pkgs.fetchFromGitHub {
+          owner = "dezgeg";
+          repo = "u-boot";
+          rev = "baab53ec244fe44def01948a0f10e67342d401e6";
+          sha256 = "0r5j2pc42ws3w3im0a9c6bh01czz5kapqrqp0ik9ra823cw73lxr";
+        };
+      });
+
+      configTxt = pkgs.writeText "config.txt" ''
+        kernel=u-boot-rpi3.bin
+        arm_control=0x200
+        enable_uart=1
+      '';
+      in ''
+        (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
+        cp ${patchedUboot}/u-boot.bin boot/u-boot-rpi3.bin
+        cp ${configTxt} boot/config.txt
+        ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
+      '';
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
index 456ef7c9f541..118ed20d47f5 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -1,3 +1,5 @@
+# To build, use:
+# nix-build nixos -I nixos-config=nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix -A config.system.build.sdImage
 { config, lib, pkgs, ... }:
 
 let
@@ -26,7 +28,7 @@ in
   boot.loader.generic-extlinux-compatible.enable = true;
 
   boot.kernelPackages = pkgs.linuxPackages_latest;
-  boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=ttyO0,115200n8" "console=tty0"];
+  boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=ttyO0,115200n8" "console=ttySAC2,115200n8" "console=tty0"];
 
   # FIXME: this probably should be in installation-device.nix
   users.extraUsers.root.initialHashedPassword = "";
@@ -42,11 +44,9 @@ in
         enable_uart=1
       '';
       in ''
-        for f in bootcode.bin fixup.dat start.elf; do
-          cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/$f boot/
-        done
+        (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
         cp ${pkgs.ubootRaspberryPi2}/u-boot.bin boot/u-boot-rpi2.bin
-        cp ${pkgs.ubootRaspberryPi3}/u-boot.bin boot/u-boot-rpi3.bin
+        cp ${pkgs.ubootRaspberryPi3_32bit}/u-boot.bin boot/u-boot-rpi3.bin
         cp ${configTxt} boot/config.txt
         ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
       '';
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index e7163f10a3c3..886ffd9a0923 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -1,3 +1,5 @@
+# To build, use:
+# nix-build nixos -I nixos-config=nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix -A config.system.build.sdImage
 { config, lib, pkgs, ... }:
 
 let
@@ -32,9 +34,7 @@ in
 
   sdImage = {
     populateBootCommands = ''
-      for f in bootcode.bin fixup.dat start.elf; do
-        cp ${pkgs.raspberrypifw}/share/raspberrypi/boot/$f boot/
-      done
+      (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
       cp ${pkgs.ubootRaspberryPi}/u-boot.bin boot/u-boot-rpi.bin
       echo 'kernel u-boot-rpi.bin' > boot/config.txt
       ${extlinux-conf-builder} -t 3 -c ${config.system.build.toplevel} -d ./boot
diff --git a/nixos/modules/installer/scan/detected.nix b/nixos/modules/installer/scan/detected.nix
index f350cd986afa..e72c78532943 100644
--- a/nixos/modules/installer/scan/detected.nix
+++ b/nixos/modules/installer/scan/detected.nix
@@ -1,4 +1,4 @@
-# List all devices which are detected by nixos-hardware-scan.
+# List all devices which are detected by nixos-generate-config.
 # Common devices are enabled by default.
 { config, lib, pkgs, ... }:
 
diff --git a/nixos/modules/installer/scan/not-detected.nix b/nixos/modules/installer/scan/not-detected.nix
index b30c569ed2a7..e1a3052ba957 100644
--- a/nixos/modules/installer/scan/not-detected.nix
+++ b/nixos/modules/installer/scan/not-detected.nix
@@ -1,4 +1,4 @@
-# List all devices which are _not_ detected by nixos-hardware-scan.
+# List all devices which are _not_ detected by nixos-generate-config.
 # Common devices are enabled by default.
 { config, lib, pkgs, ... }:
 
diff --git a/nixos/modules/installer/tools/auto-upgrade.nix b/nixos/modules/installer/tools/auto-upgrade.nix
index dfb43d1a1db9..a4d4f16d1d96 100644
--- a/nixos/modules/installer/tools/auto-upgrade.nix
+++ b/nixos/modules/installer/tools/auto-upgrade.nix
@@ -48,7 +48,7 @@ let cfg = config.system.autoUpgrade; in
         description = ''
           Specification (in the format described by
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
           which the update will occur.
         '';
       };
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index d73d67ef4728..833782477199 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,5 +1,5 @@
 {
-  x86_64-linux = "/nix/store/qdkzm17csr24snk247a1s0c47ikq5sl6-nix-1.11.6";
-  i686-linux = "/nix/store/hiwp53747lxlniqy5wpbql5izjrs8z0z-nix-1.11.6";
-  x86_64-darwin = "/nix/store/hca2hqcvwncf23hiqyqgwbsdy8vvl9xv-nix-1.11.6";
+  x86_64-linux = "/nix/store/71im965h634iy99zsmlncw6qhx5jcclx-nix-1.11.9";
+  i686-linux = "/nix/store/cgvavixkayc36l6kl92i8mxr6k0p2yhy-nix-1.11.9";
+  x86_64-darwin = "/nix/store/w1c96v5yxvdmq4nvqlxjvg6kp7xa2lag-nix-1.11.9";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index e17c02d13745..29b447912e70 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -208,9 +208,6 @@ foreach my $path (glob "/sys/bus/pci/devices/*") {
     pciCheck $path;
 }
 
-push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
-
-
 # Idem for USB devices.
 
 sub usbCheck {
@@ -277,6 +274,12 @@ if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
     push @imports, "<nixpkgs/nixos/modules/profiles/qemu-guest.nix>";
 }
 
+# Also for Hyper-V.
+if ($virt eq "microsoft") {
+    push @initrdAvailableKernelModules, "hv_storvsc";
+    $videoDriver = "fbdev";
+}
+
 
 # Pull in NixOS configuration for containers.
 if ($virt eq "systemd-nspawn") {
@@ -307,6 +310,7 @@ sub findStableDevPath {
     return $dev;
 }
 
+push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
 
 # Generate the swapDevices option from the currently activated swap
 # devices.
@@ -343,7 +347,6 @@ foreach my $fs (read_file("/proc/self/mountinfo")) {
 
     # Skip special filesystems.
     next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run") || $mountPoint eq "/var/lib/nfs/rpc_pipefs";
-    next if $mountPoint eq "/var/setuid-wrappers";
 
     # Skip the optional fields.
     my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
@@ -588,6 +591,12 @@ $bootLoaderConfig
   # Enable the OpenSSH daemon.
   # services.openssh.enable = true;
 
+  # Open ports in the firewall.
+  # networking.firewall.allowedTCPPorts = [ ... ];
+  # networking.firewall.allowedUDPPorts = [ ... ];
+  # Or disable the firewall altogether.
+  # networking.firewall.enable = false;
+
   # Enable CUPS to print documents.
   # services.printing.enable = true;
 
@@ -597,8 +606,8 @@ $bootLoaderConfig
   # services.xserver.xkbOptions = "eurosign:e";
 
   # Enable the KDE Desktop Environment.
-  # services.xserver.displayManager.kdm.enable = true;
-  # services.xserver.desktopManager.kde4.enable = true;
+  # services.xserver.displayManager.sddm.enable = true;
+  # services.xserver.desktopManager.plasma5.enable = true;
 
   # Define a user account. Don't forget to set a password with ‘passwd’.
   # users.extraUsers.guest = {
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index da28c027c563..e2ae2ee9fdf8 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -87,38 +87,6 @@ if ! test -e "$mountPoint"; then
     exit 1
 fi
 
-
-# Mount some stuff in the target root directory.
-mkdir -m 0755 -p $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home
-mkdir -m 01777 -p $mountPoint/tmp
-mkdir -m 0755 -p $mountPoint/tmp/root
-mkdir -m 0755 -p $mountPoint/var
-mkdir -m 0700 -p $mountPoint/root
-mount --rbind /dev $mountPoint/dev
-mount --rbind /proc $mountPoint/proc
-mount --rbind /sys $mountPoint/sys
-mount --rbind / $mountPoint/tmp/root
-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
-
-cp -Lf "@cacert@" "$mountPoint/tmp/ca-cert.crt"
-export SSL_CERT_FILE=/tmp/ca-cert.crt
-# For Nix 1.7
-export CURL_CA_BUNDLE=/tmp/ca-cert.crt
-
-if [ -n "$runChroot" ]; then
-    if ! [ -L $mountPoint/nix/var/nix/profiles/system ]; then
-        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[@]}"
-fi
-
-
 # Get the path of the NixOS configuration file.
 if test -z "$NIXOS_CONFIG"; then
     NIXOS_CONFIG=/etc/nixos/configuration.nix
@@ -130,121 +98,60 @@ if [ ! -e "$mountPoint/$NIXOS_CONFIG" ] && [ -z "$closure" ]; then
 fi
 
 
-# Create the necessary Nix directories on the target device, if they
-# don't already exist.
-mkdir -m 0755 -p \
-    $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
-
-mkdir -m 1775 -p $mountPoint/nix/store
-chown @root_uid@:@nixbld_gid@ $mountPoint/nix/store
-
-
-# There is no daemon in the chroot.
-unset NIX_REMOTE
-
-
-# We don't have locale-archive in the chroot, so clear $LANG.
-export LANG=
-export LC_ALL=
-export LC_TIME=
-
-
 # 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")
 
+nixpkgs="$(readlink -f "$(nix-instantiate --find-file nixpkgs)")"
+export NIX_PATH="nixpkgs=$nixpkgs:nixos-config=$mountPoint/$NIXOS_CONFIG"
+unset NIXOS_CONFIG
 
-# Copy Nix to the Nix store on the target device, unless it's already there.
-if ! NIX_DB_DIR=$mountPoint/nix/var/nix/db nix-store --check-validity @nix@ 2> /dev/null; then
-    echo "copying Nix to $mountPoint...."
-    for i in $(@perl@/bin/perl @pathsFromGraph@ @nixClosure@); do
-        echo "  $i"
-        chattr -R -i $mountPoint/$i 2> /dev/null || true # clear immutable bit
-        @rsync@/bin/rsync -a $i $mountPoint/nix/store/
-    done
-
-    # Register the paths in the Nix closure as valid.  This is necessary
-    # to prevent them from being deleted the first time we install
-    # something.  (I.e., Nix will see that, e.g., the glibc path is not
-    # valid, delete it to get it out of the way, but as a result nothing
-    # will work anymore.)
-    chroot $mountPoint @nix@/bin/nix-store --register-validity < @nixClosure@
-fi
-
-
-# Create the required /bin/sh symlink; otherwise lots of things
-# (notably the system() function) won't work.
-mkdir -m 0755 -p $mountPoint/bin
-# !!! assuming that @shell@ is in the closure
-ln -sf @shell@ $mountPoint/bin/sh
+# TODO: do I need to set NIX_SUBSTITUTERS here or is the --option binary-caches above enough?
 
 
-# Build hooks likely won't function correctly in the minimal chroot; just disable them.
-unset NIX_BUILD_HOOK
+# A place to drop temporary closures
+trap "rm -rf $tmpdir" EXIT
+tmpdir="$(mktemp -d)"
 
-# Make the build below copy paths from the CD if possible.  Note that
-# /tmp/root in the chroot is the root of the CD.
-export NIX_OTHER_STORES=/tmp/root/nix:$NIX_OTHER_STORES
-
-p=@nix@/libexec/nix/substituters
-export NIX_SUBSTITUTERS=$p/copy-from-other-stores.pl:$p/download-from-binary-cache.pl
+# 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"
 
 if [ -z "$closure" ]; then
-    # Get the absolute path to the NixOS/Nixpkgs sources.
-    nixpkgs="$(readlink -f $(nix-instantiate --find-file nixpkgs))"
-
-    nixEnvAction="-f <nixpkgs/nixos> --set -A system"
+    expr="(import <nixpkgs/nixos> {}).system"
+    system_root="$(nix-build -E "$expr")"
+    system_closure="$(closure "$expr")"
 else
-    nixpkgs=""
-    nixEnvAction="--set $closure"
+    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!
+    nix-store --export $(nix-store -qR $closure) > $system_closure
 fi
 
-# Build the specified Nix expression in the target store and install
-# it into the system configuration profile.
-echo "building the system configuration..."
-NIX_PATH="nixpkgs=/tmp/root/$nixpkgs:nixos-config=$NIXOS_CONFIG" NIXOS_CONFIG= \
-    chroot $mountPoint @nix@/bin/nix-env \
-    "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/system $nixEnvAction
+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
 
-# Copy the NixOS/Nixpkgs sources to the target as the initial contents
-# of the NixOS channel.
-mkdir -m 0755 -p $mountPoint/nix/var/nix/profiles
-mkdir -m 1777 -p $mountPoint/nix/var/nix/profiles/per-user
-mkdir -m 0755 -p $mountPoint/nix/var/nix/profiles/per-user/root
-srcs=$(nix-env "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")
-if [ -z "$noChannelCopy" ] && [ -n "$srcs" ]; then
-    echo "copying NixOS/Nixpkgs sources..."
-    chroot $mountPoint @nix@/bin/nix-env \
-        "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/per-user/root/channels -i "$srcs" --quiet
-fi
-mkdir -m 0700 -p $mountPoint/root/.nix-defexpr
-ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
-
-
-# Get rid of the /etc bind mounts.
-for f in /etc/passwd /etc/group; do [ -f "$f" ] && umount $mountPoint/$f; done
+# 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
 
+mount --rbind /dev $mountPoint/dev
+mount --rbind /proc $mountPoint/proc
+mount --rbind /sys $mountPoint/sys
 
 # 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.
-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.
@@ -259,9 +166,9 @@ chroot $mountPoint /nix/var/nix/profiles/system/activate
 
 
 # Ask the user to set a root password.
-if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /var/setuid-wrappers/passwd ] && [ -t 0 ]; then
+if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /run/wrappers/bin/passwd ] && [ -t 0 ]; then
     echo "setting root password..."
-    chroot $mountPoint /var/setuid-wrappers/passwd
+    chroot $mountPoint /run/wrappers/bin/passwd
 fi
 
 
diff --git a/nixos/modules/installer/tools/nixos-prepare-root.sh b/nixos/modules/installer/tools/nixos-prepare-root.sh
new file mode 100644
index 000000000000..0bd70d2d349c
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-prepare-root.sh
@@ -0,0 +1,105 @@
+#! @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.
+mkdir -m 0755 -p $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home
+mkdir -m 01777 -p $mountPoint/tmp
+mkdir -m 0755 -p $mountPoint/tmp/root
+mkdir -m 0755 -p $mountPoint/var
+mkdir -m 0700 -p $mountPoint/root
+
+ln -sf /run $mountPoint/var/run
+
+# Create the necessary Nix directories on the target device
+mkdir -m 0755 -p \
+    $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
+
+mkdir -m 1775 -p $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
+mkdir -m 0755 -p $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.
+mkdir -m 0755 -p $mountPoint/nix/var/nix/profiles
+mkdir -m 1777 -p $mountPoint/nix/var/nix/profiles/per-user
+mkdir -m 0755 -p $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
+mkdir -m 0700 -p $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 8e55a4f525f9..4b5e7b3230c8 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -15,6 +15,7 @@ origArgs=("$@")
 extraBuildFlags=()
 action=
 buildNix=1
+fast=
 rollback=
 upgrade=
 repair=
@@ -52,13 +53,13 @@ while [ "$#" -gt 0 ]; do
         repair=1
         extraBuildFlags+=("$i")
         ;;
-      --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q)
-        extraBuildFlags+=("$i")
-        ;;
       --max-jobs|-j|--cores|-I)
         j="$1"; shift 1
         extraBuildFlags+=("$i" "$j")
         ;;
+      --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q|-j*)
+        extraBuildFlags+=("$i")
+        ;;
       --option)
         j="$1"; shift 1
         k="$1"; shift 1
@@ -66,6 +67,7 @@ while [ "$#" -gt 0 ]; do
         ;;
       --fast)
         buildNix=
+        fast=1
         extraBuildFlags+=(--show-trace)
         ;;
       --profile-name|-p)
@@ -217,7 +219,7 @@ if [ -z "$_NIXOS_REBUILD_REEXEC" ]; then
 fi
 
 # Re-execute nixos-rebuild from the Nixpkgs tree.
-if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" ]; then
+if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" -a -z "$fast" ]; then
     if p=$(nix-build --no-out-link --expr 'with import <nixpkgs/nixos> {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then
         export _NIXOS_REBUILD_REEXEC=1
         exec $p/bin/nixos-rebuild "${origArgs[@]}"
@@ -276,24 +278,22 @@ if [ -n "$buildNix" ]; then
     echo "building Nix..." >&2
     nixDrv=
     if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A config.nix.package.out "${extraBuildFlags[@]}")"; then
-        if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A nixFallback "${extraBuildFlags[@]}")"; then
-            if ! nixDrv="$(nix-instantiate '<nixpkgs>' --add-root $tmpDir/nix.drv --indirect -A nix "${extraBuildFlags[@]}")"; then
-                nixStorePath="$(prebuiltNix "$(uname -m)")"
-                if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \
-                    --option extra-binary-caches https://cache.nixos.org/; then
+        if ! nixDrv="$(nix-instantiate '<nixpkgs>' --add-root $tmpDir/nix.drv --indirect -A nix "${extraBuildFlags[@]}")"; then
+            nixStorePath="$(prebuiltNix "$(uname -m)")"
+            if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \
+                --option extra-binary-caches https://cache.nixos.org/; then
+                echo "warning: don't know how to get latest Nix" >&2
+            fi
+            # Older version of nix-store -r don't support --add-root.
+            [ -e $tmpDir/nix ] || ln -sf $nixStorePath $tmpDir/nix
+            if [ -n "$buildHost" ]; then
+                remoteNixStorePath="$(prebuiltNix "$(buildHostCmd uname -m)")"
+                remoteNix="$remoteNixStorePath/bin"
+                if ! buildHostCmd nix-store -r $remoteNixStorePath \
+                  --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then
+                    remoteNix=
                     echo "warning: don't know how to get latest Nix" >&2
                 fi
-                # Older version of nix-store -r don't support --add-root.
-                [ -e $tmpDir/nix ] || ln -sf $nixStorePath $tmpDir/nix
-                if [ -n "$buildHost" ]; then
-                    remoteNixStorePath="$(prebuiltNix "$(buildHostCmd uname -m)")"
-                    remoteNix="$remoteNixStorePath/bin"
-                    if ! buildHostCmd nix-store -r $remoteNixStorePath \
-                      --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then
-                        remoteNix=
-                        echo "warning: don't know how to get latest Nix" >&2
-                    fi
-                fi
             fi
         fi
     fi
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index a35f6ad8ae54..a3bae78c0ffc 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -4,7 +4,6 @@
 { config, pkgs, modulesPath, ... }:
 
 let
-
   cfg = config.installer;
 
   makeProg = args: pkgs.substituteAll (args // {
@@ -17,6 +16,14 @@ 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;
@@ -26,6 +33,7 @@ let
     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]; }
@@ -69,6 +77,7 @@ in
 
     environment.systemPackages =
       [ nixos-build-vms
+        nixos-prepare-root
         nixos-install
         nixos-rebuild
         nixos-generate-config
@@ -77,7 +86,7 @@ in
       ];
 
     system.build = {
-      inherit nixos-install nixos-generate-config nixos-option nixos-rebuild;
+      inherit nixos-install nixos-prepare-root nixos-generate-config nixos-option nixos-rebuild;
     };
 
   };
diff --git a/nixos/modules/misc/extra-arguments.nix b/nixos/modules/misc/extra-arguments.nix
index 19002b17dace..f4ee94ecc0d7 100644
--- a/nixos/modules/misc/extra-arguments.nix
+++ b/nixos/modules/misc/extra-arguments.nix
@@ -2,16 +2,6 @@
 
 {
   _module.args = {
-    pkgs_i686 = import ../../.. {
-      system = "i686-linux";
-      # FIXME: we enable config.allowUnfree to make packages like
-      # nvidia-x11 available. This isn't a problem because if the user has
-      # ‘nixpkgs.config.allowUnfree = false’, then evaluation will fail on
-      # the 64-bit package anyway. However, it would be cleaner to respect
-      # nixpkgs.config here.
-      config.allowUnfree = true;
-    };
-
     utils = import ../../lib/utils.nix pkgs;
   };
 }
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 6ab4b24a3491..d217b3452fb5 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -64,7 +64,7 @@
       cups = 36;
       foldingathome = 37;
       sabnzbd = 38;
-      kdm = 39;
+      #kdm = 39; # dropped in 17.03
       ghostone = 40;
       git = 41;
       fourstore = 42;
@@ -206,7 +206,7 @@
       ripple-data-api = 186;
       mediatomb = 187;
       rdnssd = 188;
-      ihaskell = 189;
+      # ihaskell = 189; # unused
       i2p = 190;
       lambdabot = 191;
       asterisk = 192;
@@ -282,6 +282,18 @@
       infinoted = 264;
       keystone = 265;
       glance = 266;
+      couchpotato = 267;
+      gogs = 268;
+      pdns-recursor = 269;
+      kresd = 270;
+      rpc = 271;
+      geoip = 272;
+      fcron = 273;
+      sonarr = 274;
+      radarr = 275;
+      jackett = 276;
+      aria2 = 277;
+      clickhouse = 278;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -328,7 +340,7 @@
       #cups = 36; # unused
       #foldingathome = 37; # unused
       #sabnzd = 38; # unused
-      #kdm = 39; # unused
+      #kdm = 39; # unused, even before 17.03
       ghostone = 40;
       git = 41;
       fourstore = 42;
@@ -465,7 +477,7 @@
       #ripple-data-api = 186; #unused
       mediatomb = 187;
       #rdnssd = 188; # unused
-      ihaskell = 189;
+      # ihaskell = 189; # unused
       i2p = 190;
       lambdabot = 191;
       asterisk = 192;
@@ -534,6 +546,17 @@
       infinoted = 264;
       keystone = 265;
       glance = 266;
+      couchpotato = 267;
+      gogs = 268;
+      kresd = 270;
+      #rpc = 271; # unused
+      #geoip = 272; # unused
+      fcron = 273;
+      sonarr = 274;
+      radarr = 275;
+      jackett = 276;
+      aria2 = 277;
+      clickhouse = 278;
 
       # 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/locate.nix b/nixos/modules/misc/locate.nix
index 3cb5bb1a351a..6d9bc915ba03 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -4,10 +4,12 @@ with lib;
 
 let
   cfg = config.services.locate;
+  isMLocate = hasPrefix "mlocate" cfg.locate.name;
+  isFindutils = hasPrefix "findutils" cfg.locate.name;
 in {
-  options.services.locate = {
+  options.services.locate = with types; {
     enable = mkOption {
-      type = types.bool;
+      type = bool;
       default = false;
       description = ''
         If enabled, NixOS will periodically update the database of
@@ -16,8 +18,9 @@ in {
     };
 
     locate = mkOption {
-      type = types.package;
+      type = package;
       default = pkgs.findutils;
+      defaultText = "pkgs.findutils";
       example = "pkgs.mlocate";
       description = ''
         The locate implementation to use
@@ -25,7 +28,7 @@ in {
     };
 
     interval = mkOption {
-      type = types.str;
+      type = str;
       default = "02:15";
       example = "hourly";
       description = ''
@@ -38,11 +41,8 @@ in {
       '';
     };
 
-    # This is no longer supported, but we keep it to give a better warning below
-    period = mkOption { visible = false; };
-
     extraFlags = mkOption {
-      type = types.listOf types.str;
+      type = listOf str;
       default = [ ];
       description = ''
         Extra flags to pass to <command>updatedb</command>.
@@ -50,7 +50,7 @@ in {
     };
 
     output = mkOption {
-      type = types.path;
+      type = path;
       default = "/var/cache/locatedb";
       description = ''
         The database file to build.
@@ -58,7 +58,7 @@ in {
     };
 
     localuser = mkOption {
-      type = types.str;
+      type = nullOr str;
       default = "nobody";
       description = ''
         The user to search non-network directories as, using
@@ -66,31 +66,82 @@ in {
       '';
     };
 
-    includeStore = mkOption {
-      type = types.bool;
+    pruneFS = mkOption {
+      type = listOf str;
+      default = ["afs" "anon_inodefs" "auto" "autofs" "bdev" "binfmt" "binfmt_misc" "cgroup" "cifs" "coda" "configfs" "cramfs" "cpuset" "debugfs" "devfs" "devpts" "devtmpfs" "ecryptfs" "eventpollfs" "exofs" "futexfs" "ftpfs" "fuse" "fusectl" "gfs" "gfs2" "hostfs" "hugetlbfs" "inotifyfs" "iso9660" "jffs2" "lustre" "misc" "mqueue" "ncpfs" "nnpfs" "ocfs" "ocfs2" "pipefs" "proc" "ramfs" "rpc_pipefs" "securityfs" "selinuxfs" "sfs" "shfs" "smbfs" "sockfs" "spufs" "nfs" "NFS" "nfs4" "nfsd" "sshfs" "subfs" "supermount" "sysfs" "tmpfs" "ubifs" "udf" "usbfs" "vboxsf" "vperfctrfs" ];
+      description = ''
+        Which filesystem types to exclude from indexing
+      '';
+    };
+
+    prunePaths = mkOption {
+      type = listOf path;
+      default = ["/tmp" "/var/tmp" "/var/cache" "/var/lock" "/var/run" "/var/spool" "/nix/store"];
+      description = ''
+        Which paths to exclude from indexing
+      '';
+    };
+
+    pruneNames = mkOption {
+      type = listOf str;
+      default = [];
+      description = ''
+        Directory components which should exclude paths containing them from indexing
+      '';
+    };
+
+    pruneBindMounts = mkOption {
+      type = bool;
       default = false;
       description = ''
-        Whether to include <filename>/nix/store</filename> in the locate database.
+        Whether not to index bind mounts
       '';
     };
+    
   };
 
-  config = {
-    warnings =
-      let opt = options.services.locate.period; in
-      optional opt.isDefined "The ‘services.locate.period’ option in ${showFiles opt.files} has been removed; please replace it with ‘services.locate.interval’, using the systemd.time(7) calendar event format.";
+  config = mkIf cfg.enable {
+    users.extraGroups = mkIf isMLocate { mlocate = {}; };
+
+    security.wrappers = mkIf isMLocate {
+      locate = {
+        group = "mlocate";
+        owner = "root";
+        permissions = "u+rx,g+x,o+x";
+        setgid = true;
+        setuid = false;
+        source = "${cfg.locate}/bin/locate";
+      };
+    };
+
+    nixpkgs.config = { locate.dbfile = cfg.output; };
+
+    environment.systemPackages = [ cfg.locate ];
+
+    environment.variables = mkIf (!isMLocate)
+      { LOCATE_PATH = cfg.output;
+      };
 
+    warnings = optional (isMLocate && cfg.localuser != null) "mlocate does not support searching as user other than root"
+            ++ optional (isFindutils && cfg.pruneNames != []) "findutils locate does not support pruning by directory component"
+            ++ optional (isFindutils && cfg.pruneBindMounts) "findutils locate does not support skipping bind mounts";
+  
     systemd.services.update-locatedb =
       { description = "Update Locate Database";
-        path  = [ pkgs.su ];
+        path = mkIf (!isMLocate) [ pkgs.su ];
         script =
           ''
-            mkdir -m 0755 -p $(dirname ${toString cfg.output})
+            install -m ${if isMLocate then "0750" else "0755"} -o root -g ${if isMLocate then "mlocate" else "root"} -d $(dirname ${cfg.output})
             exec ${cfg.locate}/bin/updatedb \
-              --localuser=${cfg.localuser} \
-              ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \
+              ${optionalString (cfg.localuser != null) ''--localuser=${cfg.localuser}''} \
               --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
           '';
+        environment = {
+          PRUNEFS = concatStringsSep " " cfg.pruneFS;
+          PRUNEPATHS = concatStringsSep " " cfg.prunePaths;
+          PRUNENAMES = concatStringsSep " " cfg.pruneNames;
+          PRUNE_BIND_MOUNTS = if cfg.pruneBindMounts then "yes" else "no";
+        };
         serviceConfig.Nice = 19;
         serviceConfig.IOSchedulingClass = "idle";
         serviceConfig.PrivateTmp = "yes";
@@ -100,7 +151,7 @@ in {
         serviceConfig.ReadWriteDirectories = dirOf cfg.output;
       };
 
-    systemd.timers.update-locatedb = mkIf cfg.enable
+    systemd.timers.update-locatedb =
       { description = "Update timer for locate database";
         partOf      = [ "update-locatedb.service" ];
         wantedBy    = [ "timers.target" ];
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 7d50b8025bdd..1793c1447d60 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -29,41 +29,64 @@ let
     };
 
   configType = mkOptionType {
-    name = "nixpkgs config";
+    name = "nixpkgs-config";
+    description = "nixpkgs config";
     check = traceValIfNot isConfig;
     merge = args: fold (def: mergeConfig def.value) {};
   };
 
+  overlayType = mkOptionType {
+    name = "nixpkgs-overlay";
+    description = "nixpkgs overlay";
+    check = builtins.isFunction;
+    merge = lib.mergeOneOption;
+  };
+
+  _pkgs = import ../../.. config.nixpkgs;
+
 in
 
 {
-  options = {
-
-    nixpkgs.config = mkOption {
+  options.nixpkgs = {
+    config = mkOption {
       default = {};
       example = literalExample
         ''
-          { firefox.enableGeckoMediaPlayer = true;
-            packageOverrides = pkgs: {
-              firefox60Pkgs = pkgs.firefox60Pkgs.override {
-                enableOfficialBranding = true;
-              };
-            };
-          }
+          { firefox.enableGeckoMediaPlayer = 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, and to override packages
-        globally through the <varname>packageOverrides</varname>
-        option.  The latter is a function that takes as an argument
-        the <emphasis>original</emphasis> Nixpkgs, and must evaluate
-        to a set of new or overridden packages.
+        package configuration options.
       '';
     };
 
-    nixpkgs.system = mkOption {
+    overlays = mkOption {
+      default = [];
+      example = literalExample
+        ''
+          [ (self: super: {
+              openssh = super.openssh.override {
+                hpnSupport = true;
+                withKerberos = true;
+                kerberos = self.libkrb5;
+              };
+            };
+          ) ]
+        '';
+      type = types.listOf overlayType;
+      description = ''
+        List of overlays to use with the Nix Packages collection.
+        (For details, see the Nixpkgs documentation.)  It allows
+        you to override packages globally. This is a function that
+        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.
+      '';
+    };
+
+    system = mkOption {
       type = types.str;
       example = "i686-linux";
       description = ''
@@ -73,14 +96,12 @@ in
         multi-platform deployment, or when building virtual machines.
       '';
     };
-
   };
 
   config = {
-    _module.args.pkgs = import ../../.. {
-      system = config.nixpkgs.system;
-
-      inherit (config.nixpkgs) config;
+    _module.args = {
+      pkgs = _pkgs;
+      pkgs_i686 = _pkgs.pkgsi686Linux;
     };
   };
 }
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 70cd3fb9766a..315c33a462c6 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -95,7 +95,7 @@ in
       nixosVersionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
       # Note: code names must only increase in alphabetical order.
-      nixosCodeName = "Gorilla";
+      nixosCodeName = "Hummingbird";
     };
 
     # Generate /etc/os-release.  See
@@ -106,9 +106,12 @@ 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})"
-        HOME_URL="http://nixos.org/"
+        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 f76852793d46..672521166e49 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1,8 +1,9 @@
 [
   ./config/debug-info.nix
   ./config/fonts/corefonts.nix
-  ./config/fonts/fontconfig-ultimate.nix
   ./config/fonts/fontconfig.nix
+  ./config/fonts/fontconfig-penultimate.nix
+  ./config/fonts/fontconfig-ultimate.nix
   ./config/fonts/fontdir.nix
   ./config/fonts/fonts.nix
   ./config/fonts/ghostscript.nix
@@ -26,9 +27,12 @@
   ./config/vpnc.nix
   ./config/zram.nix
   ./hardware/all-firmware.nix
+  ./hardware/ckb.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/intel-microcode.nix
+  ./hardware/sensor/iio.nix
   ./hardware/ksm.nix
+  ./hardware/mcelog.nix
   ./hardware/network/b43.nix
   ./hardware/network/intel-2100bg.nix
   ./hardware/network/intel-2200bg.nix
@@ -37,9 +41,11 @@
   ./hardware/network/rtl8192c.nix
   ./hardware/opengl.nix
   ./hardware/pcmcia.nix
+  ./hardware/usb-wwan.nix
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
+  ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/bumblebee.nix
   ./hardware/video/displaylink.nix
   ./hardware/video/nvidia.nix
@@ -65,6 +71,7 @@
   ./programs/atop.nix
   ./programs/bash/bash.nix
   ./programs/blcr.nix
+  ./programs/browserpass.nix
   ./programs/cdemu.nix
   ./programs/chromium.nix
   ./programs/command-not-found/command-not-found.nix
@@ -79,9 +86,12 @@
   ./programs/light.nix
   ./programs/man.nix
   ./programs/mosh.nix
+  ./programs/mtr.nix
   ./programs/nano.nix
   ./programs/oblogout.nix
+  ./programs/qt5ct.nix
   ./programs/screen.nix
+  ./programs/slock.nix
   ./programs/shadow.nix
   ./programs/shell.nix
   ./programs/spacefm.nix
@@ -90,10 +100,13 @@
   ./programs/tmux.nix
   ./programs/venus.nix
   ./programs/vim.nix
+  ./programs/wireshark.nix
   ./programs/wvdial.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
+  ./programs/zsh/oh-my-zsh.nix
   ./programs/zsh/zsh.nix
+  ./programs/zsh/zsh-syntax-highlighting.nix
   ./rename.nix
   ./security/acme.nix
   ./security/apparmor.nix
@@ -101,9 +114,11 @@
   ./security/audit.nix
   ./security/ca.nix
   ./security/chromium-suid-sandbox.nix
+  ./security/dhparams.nix
   ./security/duosec.nix
   ./security/grsecurity.nix
   ./security/hidepid.nix
+  ./security/lock-kernel-modules.nix
   ./security/oath.nix
   ./security/pam.nix
   ./security/pam_usb.nix
@@ -112,7 +127,7 @@
   ./security/prey.nix
   ./security/rngd.nix
   ./security/rtkit.nix
-  ./security/setuid-wrappers.nix
+  ./security/wrappers/default.nix
   ./security/sudo.nix
   ./services/amqp/activemq/default.nix
   ./services/amqp/rabbitmq.nix
@@ -121,6 +136,7 @@
   ./services/audio/liquidsoap.nix
   ./services/audio/mpd.nix
   ./services/audio/mopidy.nix
+  ./services/audio/slimserver.nix
   ./services/audio/squeezelite.nix
   ./services/audio/ympd.nix
   ./services/backup/almir.nix
@@ -129,7 +145,6 @@
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
   ./services/backup/rsnapshot.nix
-  ./services/backup/sitecopy-backup.nix
   ./services/backup/tarsnap.nix
   ./services/backup/znapzend.nix
   ./services/cluster/fleet.nix
@@ -140,6 +155,7 @@
   ./services/computing/torque/mom.nix
   ./services/computing/slurm/slurm.nix
   ./services/continuous-integration/buildbot/master.nix
+  ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agent.nix
   ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/gitlab-runner.nix
@@ -150,6 +166,7 @@
   ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
+  ./services/databases/clickhouse.nix
   ./services/databases/couchdb.nix
   ./services/databases/firebird.nix
   ./services/databases/hbase.nix
@@ -196,6 +213,7 @@
   ./services/hardware/bluetooth.nix
   ./services/hardware/brltty.nix
   ./services/hardware/freefall.nix
+  ./services/hardware/illum.nix
   ./services/hardware/irqbalance.nix
   ./services/hardware/nvidia-optimus.nix
   ./services/hardware/pcscd.nix
@@ -204,6 +222,7 @@
   ./services/hardware/tcsd.nix
   ./services/hardware/tlp.nix
   ./services/hardware/thinkfan.nix
+  ./services/hardware/trezord.nix
   ./services/hardware/udev.nix
   ./services/hardware/udisks2.nix
   ./services/hardware/upower.nix
@@ -211,6 +230,7 @@
   ./services/logging/awstats.nix
   ./services/logging/fluentd.nix
   ./services/logging/graylog.nix
+  ./services/logging/journalbeat.nix
   ./services/logging/klogd.nix
   ./services/logging/logcheck.nix
   ./services/logging/logrotate.nix
@@ -235,6 +255,7 @@
   ./services/mail/rmilter.nix
   ./services/misc/apache-kafka.nix
   ./services/misc/autofs.nix
+  ./services/misc/autorandr.nix
   ./services/misc/bepasty.nix
   ./services/misc/canto-daemon.nix
   ./services/misc/calibre-server.nix
@@ -242,6 +263,7 @@
   ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/cgminer.nix
   ./services/misc/confd.nix
+  ./services/misc/couchpotato.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
   ./services/misc/dysnomia.nix
@@ -253,11 +275,15 @@
   ./services/misc/felix.nix
   ./services/misc/folding-at-home.nix
   ./services/misc/gammu-smsd.nix
+  ./services/misc/geoip-updater.nix
   #./services/misc/gitit.nix
   ./services/misc/gitlab.nix
   ./services/misc/gitolite.nix
+  ./services/misc/gogs.nix
   ./services/misc/gpsd.nix
-  ./services/misc/ihaskell.nix
+  #./services/misc/ihaskell.nix
+  ./services/misc/irkerd.nix
+  ./services/misc/jackett.nix
   ./services/misc/leaps.nix
   ./services/misc/mantisbt.nix
   ./services/misc/mathics.nix
@@ -278,6 +304,7 @@
   ./services/misc/parsoid.nix
   ./services/misc/phd.nix
   ./services/misc/plex.nix
+  ./services/misc/radarr.nix
   ./services/misc/redmine.nix
   ./services/misc/rippled.nix
   ./services/misc/ripple-rest.nix
@@ -286,6 +313,7 @@
   ./services/misc/siproxd.nix
   ./services/misc/sonarr.nix
   ./services/misc/spice-vdagentd.nix
+  ./services/misc/ssm-agent.nix
   ./services/misc/sssd.nix
   ./services/misc/subsonic.nix
   ./services/misc/sundtek.nix
@@ -309,13 +337,16 @@
   ./services/monitoring/monit.nix
   ./services/monitoring/munin.nix
   ./services/monitoring/nagios.nix
+  ./services/monitoring/netdata.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
   ./services/monitoring/prometheus/blackbox-exporter.nix
+  ./services/monitoring/prometheus/fritzbox-exporter.nix
   ./services/monitoring/prometheus/json-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/riemann.nix
   ./services/monitoring/riemann-dash.nix
@@ -323,15 +354,18 @@
   ./services/monitoring/scollector.nix
   ./services/monitoring/smartd.nix
   ./services/monitoring/statsd.nix
+  ./services/monitoring/sysstat.nix
   ./services/monitoring/systemhealth.nix
   ./services/monitoring/teamviewer.nix
   ./services/monitoring/telegraf.nix
   ./services/monitoring/ups.nix
   ./services/monitoring/uptime.nix
+  ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
   ./services/monitoring/zabbix-server.nix
   ./services/network-filesystems/cachefilesd.nix
   ./services/network-filesystems/drbd.nix
+  ./services/network-filesystems/glusterfs.nix
   ./services/network-filesystems/ipfs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
@@ -366,6 +400,7 @@
   ./services/networking/dhcpd.nix
   ./services/networking/dnschain.nix
   ./services/networking/dnscrypt-proxy.nix
+  ./services/networking/dnscrypt-wrapper.nix
   ./services/networking/dnsmasq.nix
   ./services/networking/ejabberd.nix
   ./services/networking/fan.nix
@@ -391,7 +426,9 @@
   ./services/networking/i2p.nix
   ./services/networking/iodine.nix
   ./services/networking/ircd-hybrid/default.nix
+  ./services/networking/keepalived/default.nix
   ./services/networking/kippo.nix
+  ./services/networking/kresd.nix
   ./services/networking/lambdabot.nix
   ./services/networking/libreswan.nix
   ./services/networking/logmein-hamachi.nix
@@ -407,6 +444,7 @@
   ./services/networking/namecoind.nix
   ./services/networking/nat.nix
   ./services/networking/networkmanager.nix
+  ./services/networking/nftables.nix
   ./services/networking/ngircd.nix
   ./services/networking/nix-serve.nix
   ./services/networking/nntp-proxy.nix
@@ -422,16 +460,18 @@
   ./services/networking/pdnsd.nix
   ./services/networking/polipo.nix
   ./services/networking/powerdns.nix
+  ./services/networking/pdns-recursor.nix
   ./services/networking/pptpd.nix
   ./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
   ./services/networking/radvd.nix
   ./services/networking/rdnssd.nix
+  ./services/networking/redsocks.nix
   ./services/networking/rpcbind.nix
   ./services/networking/sabnzbd.nix
   ./services/networking/searx.nix
@@ -468,6 +508,7 @@
   ./services/networking/wpa_supplicant.nix
   ./services/networking/xinetd.nix
   ./services/networking/xl2tpd.nix
+  ./services/networking/xrdp.nix
   ./services/networking/zerobin.nix
   ./services/networking/zerotierone.nix
   ./services/networking/znc.nix
@@ -488,16 +529,19 @@
   ./services/security/frandom.nix
   ./services/security/haka.nix
   ./services/security/haveged.nix
-  ./services/security/hologram.nix
+  ./services/security/hologram-server.nix
+  ./services/security/hologram-agent.nix
   ./services/security/munge.nix
   ./services/security/oauth2_proxy.nix
   ./services/security/physlock.nix
-  ./services/security/torify.nix
+  ./services/security/sshguard.nix
   ./services/security/tor.nix
+  ./services/security/torify.nix
   ./services/security/torsocks.nix
   ./services/system/cgmanager.nix
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
+  ./services/system/earlyoom.nix
   ./services/system/kerberos.nix
   ./services/system/nscd.nix
   ./services/system/uptimed.nix
@@ -512,6 +556,7 @@
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
+  ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/nixbot.nix
   ./services/web-apps/pump.io.nix
@@ -542,10 +587,10 @@
   ./services/x11/display-managers/auto.nix
   ./services/x11/display-managers/default.nix
   ./services/x11/display-managers/gdm.nix
-  ./services/x11/display-managers/kdm.nix
   ./services/x11/display-managers/lightdm.nix
   ./services/x11/display-managers/sddm.nix
   ./services/x11/display-managers/slim.nix
+  ./services/x11/display-managers/xpra.nix
   ./services/x11/hardware/libinput.nix
   ./services/x11/hardware/multitouch.nix
   ./services/x11/hardware/synaptics.nix
@@ -624,10 +669,12 @@
   ./tasks/scsi-link-power-management.nix
   ./tasks/swraid.nix
   ./tasks/trackpoint.nix
+  ./tasks/powertop.nix
   ./testing/service-runner.nix
   ./virtualisation/container-config.nix
   ./virtualisation/containers.nix
   ./virtualisation/docker.nix
+  ./virtualisation/ecs-agent.nix
   ./virtualisation/libvirtd.nix
   ./virtualisation/lxc.nix
   ./virtualisation/lxcfs.nix
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 99b45228ce4d..6b4d8c737eba 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -42,6 +42,9 @@
       # Virtio (QEMU, KVM etc.) support.
       "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "virtio_balloon" "virtio_console"
 
+      # Hyper-V support.
+      "hv_storvsc"
+
       # Keyboards
       "usbhid" "hid_apple" "hid_logitech_dj" "hid_lenovo_tpkbd" "hid_roccat"
     ];
diff --git a/nixos/modules/profiles/graphical.nix b/nixos/modules/profiles/graphical.nix
index 8ee1628f876c..e23375375188 100644
--- a/nixos/modules/profiles/graphical.nix
+++ b/nixos/modules/profiles/graphical.nix
@@ -1,13 +1,13 @@
-# This module defines a NixOS configuration that contains X11 and
-# KDE 4.  It's used by the graphical installation CD.
+# This module defines a NixOS configuration with the Plasma 5 desktop.
+# It's used by the graphical installation CD.
 
 { config, pkgs, ... }:
 
 {
   services.xserver = {
     enable = true;
-    displayManager.kdm.enable = true;
-    desktopManager.kde4.enable = true;
+    displayManager.sddm.enable = true;
+    desktopManager.plasma5.enable = true;
     synaptics.enable = true; # for touchpad support on many laptops
   };
 
diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
new file mode 100644
index 000000000000..8bde2e4f4984
--- /dev/null
+++ b/nixos/modules/profiles/hardened.nix
@@ -0,0 +1,62 @@
+# A profile with most (vanilla) hardening options enabled by default,
+# potentially at the cost of features and performance.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  boot.kernelPackages = mkDefault pkgs.linuxPackages_hardened;
+
+  security.hideProcessInformation = mkDefault true;
+
+  security.lockKernelModules = mkDefault true;
+
+  security.apparmor.enable = mkDefault true;
+
+  boot.kernelParams = [
+    # Overwrite free'd memory
+    "page_poison=1"
+
+    # Disable legacy virtual syscalls
+    "vsyscall=none"
+
+    # Disable hibernation (allows replacing the running kernel)
+    "nohibernate"
+  ];
+
+  # Restrict ptrace() usage to processes with a pre-defined relationship
+  # (e.g., parent/child)
+  boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkOverride 500 1;
+
+  # Prevent replacing the running kernel image w/o reboot
+  boot.kernel.sysctl."kernel.kexec_load_disabled" = mkDefault true;
+
+  # Restrict access to kernel ring buffer (information leaks)
+  boot.kernel.sysctl."kernel.dmesg_restrict" = mkDefault true;
+
+  # Hide kptrs even for processes with CAP_SYSLOG
+  boot.kernel.sysctl."kernel.kptr_restrict" = mkOverride 500 2;
+
+  # Unprivileged access to bpf() has been used for privilege escalation in
+  # the past
+  boot.kernel.sysctl."kernel.unprivileged_bpf_disabled" = mkDefault true;
+
+  # Disable bpf() JIT (to eliminate spray attacks)
+  boot.kernel.sysctl."net.core.bpf_jit_enable" = mkDefault false;
+
+  # ... or at least apply some hardening to it
+  boot.kernel.sysctl."net.core.bpf_jit_harden" = mkDefault true;
+
+  # A recurring problem with user namespaces is that there are
+  # still code paths where the kernel's permission checking logic
+  # fails to account for namespacing, instead permitting a
+  # namespaced process to act outside the namespace with the
+  # same privileges as it would have inside it.  This is particularly
+  # bad in the common case of running as root within the namespace.
+  #
+  # Setting the number of allowed userns to 0 effectively disables
+  # the feature at runtime.  Attempting to create a user namespace
+  # with unshare will then fail with "no space left on device".
+  boot.kernel.sysctl."user.max_user_namespaces" = mkDefault 0;
+}
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 7949e600f86a..a24fa75e01db 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -45,8 +45,13 @@ with lib;
             "Type `systemctl start display-manager' to\nstart the graphical user interface."}
       '';
 
-    # Allow sshd to be started manually through "start sshd".
-    services.openssh.enable = true;
+    # Allow sshd to be started manually through "systemctl start sshd".
+    services.openssh = {
+      enable = true;
+      # Allow password login to the installation, if the user sets a password via "passwd"
+      # It is safe as root doesn't have a password by default and SSH is disabled by default
+      permitRootLogin = "yes";
+    };
     systemd.services.sshd.wantedBy = mkOverride 50 [];
 
     # Enable wpa_supplicant, but don't start it by default.
@@ -66,9 +71,8 @@ with lib;
     boot.kernel.sysctl."vm.overcommit_memory" = "1";
 
     # To speed up installation a little bit, include the complete
-    # stdenv in the Nix store on the CD.  Archive::Cpio is needed for
-    # the initrd builder.
-    system.extraDependencies = [ pkgs.stdenv pkgs.busybox pkgs.perlPackages.ArchiveCpio ];
+    # stdenv in the Nix store on the CD.
+    system.extraDependencies = with pkgs; [ stdenv stdenvNoCC busybox ];
 
     # Show all debug messages from the kernel but don't log refused packets
     # because we have the firewall enabled. This makes installs from the
@@ -76,5 +80,6 @@ with lib;
     boot.consoleLogLevel = mkDefault 7;
     networking.firewall.logRefusedConnections = mkDefault false;
 
+    environment.systemPackages = [ pkgs.vim ];
   };
 }
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
index 9ba81899e588..18290555b79d 100644
--- a/nixos/modules/programs/adb.nix
+++ b/nixos/modules/programs/adb.nix
@@ -10,7 +10,6 @@ with lib;
     programs.adb = {
       enable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Whether to configure system to use Android Debug Bridge (adb).
diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix
new file mode 100644
index 000000000000..2b7ec1856431
--- /dev/null
+++ b/nixos/modules/programs/browserpass.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+  options = {
+    programs.browserpass = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to install the NativeMessaging configuration for installed browsers.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.browserpass.enable {
+    environment.systemPackages = [ pkgs.browserpass ];
+    environment.etc."chromium/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json";
+    environment.etc."opt/chrome/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json";
+  };
+}
diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix
index 9741aa7ca539..55529d73cb60 100644
--- a/nixos/modules/programs/command-not-found/command-not-found.nix
+++ b/nixos/modules/programs/command-not-found/command-not-found.nix
@@ -8,13 +8,14 @@
 with lib;
 
 let
-
+  cfg = config.programs.command-not-found;
   commandNotFound = pkgs.substituteAll {
     name = "command-not-found";
     dir = "bin";
     src = ./command-not-found.pl;
     isExecutable = true;
     inherit (pkgs) perl;
+    inherit (cfg) dbPath;
     perlFlags = concatStrings (map (path: "-I ${path}/lib/perl5/site_perl ")
       [ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite pkgs.perlPackages.StringShellQuote ]);
   };
@@ -22,50 +23,66 @@ let
 in
 
 {
+  options.programs.command-not-found = {
+
+    enable = mkEnableOption "command-not-found hook for interactive shell";
+
+    dbPath = mkOption {
+      default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ;
+      description = ''
+        Absolute path to programs.sqlite.
+
+        By default this file will be provided by your channel
+        (nixexprs.tar.xz).
+      '';
+      type = types.path;
+    };
+  };
 
-  programs.bash.interactiveShellInit =
-    ''
-      # This function is called whenever a command is not found.
-      command_not_found_handle() {
-        local p=/run/current-system/sw/bin/command-not-found
-        if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then
-          # Run the helper program.
-          $p "$@"
-          # Retry the command if we just installed it.
-          if [ $? = 126 ]; then
-            "$@"
+  config = mkIf cfg.enable {
+    programs.bash.interactiveShellInit =
+      ''
+        # This function is called whenever a command is not found.
+        command_not_found_handle() {
+          local p=${commandNotFound}/bin/command-not-found
+          if [ -x $p -a -f ${cfg.dbPath} ]; then
+            # Run the helper program.
+            $p "$@"
+            # Retry the command if we just installed it.
+            if [ $? = 126 ]; then
+              "$@"
+            else
+              return 127
+            fi
           else
+            echo "$1: command not found" >&2
             return 127
           fi
-        else
-          echo "$1: command not found" >&2
-          return 127
-        fi
-      }
-    '';
+        }
+      '';
 
-  programs.zsh.interactiveShellInit =
-    ''
-      # This function is called whenever a command is not found.
-      command_not_found_handler() {
-        local p=/run/current-system/sw/bin/command-not-found
-        if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then
-          # Run the helper program.
-          $p "$@"
+    programs.zsh.interactiveShellInit =
+      ''
+        # This function is called whenever a command is not found.
+        command_not_found_handler() {
+          local p=${commandNotFound}/bin/command-not-found
+          if [ -x $p -a -f ${cfg.dbPath} ]; then
+            # Run the helper program.
+            $p "$@"
 
-          # Retry the command if we just installed it.
-          if [ $? = 126 ]; then
-            "$@"
+            # Retry the command if we just installed it.
+            if [ $? = 126 ]; then
+              "$@"
+            fi
+          else
+            # Indicate than there was an error so ZSH falls back to its default handler
+            echo "$1: command not found" >&2
+            return 127
           fi
-        else
-          # Indicate than there was an error so ZSH falls back to its default handler
-          return 127
-        fi
-      }
-    '';
+        }
+      '';
 
-  environment.systemPackages = [ commandNotFound ];
-
-  # TODO: tab completion for uninstalled commands! :-)
+    environment.systemPackages = [ commandNotFound ];
+  };
 
 }
diff --git a/nixos/modules/programs/command-not-found/command-not-found.pl b/nixos/modules/programs/command-not-found/command-not-found.pl
index 5bdda26592e6..ab7aa204653c 100644
--- a/nixos/modules/programs/command-not-found/command-not-found.pl
+++ b/nixos/modules/programs/command-not-found/command-not-found.pl
@@ -8,7 +8,7 @@ use Config;
 
 my $program = $ARGV[0];
 
-my $dbPath = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite";
+my $dbPath = "@dbPath@";
 
 my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
     or die "cannot open database `$dbPath'";
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index a35b5cc9513e..48a1e2a0a883 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -17,10 +17,10 @@ in
   config = {
 
     environment.variables =
-      { LOCATE_PATH = "/var/cache/locatedb";
-        NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
+      { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
         PAGER = mkDefault "less -R";
         EDITOR = mkDefault "nano";
+        XCURSOR_PATH = "$HOME/.icons";
       };
 
     environment.profiles =
@@ -43,6 +43,7 @@ in
         GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" ];
         XDG_CONFIG_DIRS = [ "/etc/xdg" ];
         XDG_DATA_DIRS = [ "/share" ];
+        XCURSOR_PATH = [ "/share/icons" ];
         MOZ_PLUGIN_PATH = [ "/lib/mozilla/plugins" ];
         LIBEXEC_PATH = [ "/lib/libexec" ];
       };
diff --git a/nixos/modules/programs/gphoto2.nix b/nixos/modules/programs/gphoto2.nix
index 47822562aee1..ca7c6fb28f52 100644
--- a/nixos/modules/programs/gphoto2.nix
+++ b/nixos/modules/programs/gphoto2.nix
@@ -10,7 +10,6 @@ with lib;
     programs.gphoto2 = {
       enable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Whether to configure system to use gphoto2.
diff --git a/nixos/modules/programs/kbdlight.nix b/nixos/modules/programs/kbdlight.nix
index 0172368e968f..58e45872fac8 100644
--- a/nixos/modules/programs/kbdlight.nix
+++ b/nixos/modules/programs/kbdlight.nix
@@ -11,6 +11,6 @@ in
 
   config = mkIf cfg.enable {
     environment.systemPackages = [ pkgs.kbdlight ];
-    security.setuidPrograms = [ "kbdlight" ];
+    security.wrappers.kbdlight.source = "${pkgs.kbdlight.out}/bin/kbdlight";
   };
 }
diff --git a/nixos/modules/programs/light.nix b/nixos/modules/programs/light.nix
index 09cd1113d9c7..6f8c389acc97 100644
--- a/nixos/modules/programs/light.nix
+++ b/nixos/modules/programs/light.nix
@@ -21,6 +21,6 @@ in
 
   config = mkIf cfg.enable {
     environment.systemPackages = [ pkgs.light ];
-    security.setuidPrograms = [ "light" ];
+    security.wrappers.light.source = "${pkgs.light.out}/bin/light";
   };
 }
diff --git a/nixos/modules/programs/man.nix b/nixos/modules/programs/man.nix
index e59ffd6f936d..5b20a38d8856 100644
--- a/nixos/modules/programs/man.nix
+++ b/nixos/modules/programs/man.nix
@@ -11,6 +11,7 @@ with lib;
       default = true;
       description = ''
         Whether to enable manual pages and the <command>man</command> command.
+        This also includes "man" outputs of all <literal>systemPackages</literal>.
       '';
     };
 
diff --git a/nixos/modules/programs/mosh.nix b/nixos/modules/programs/mosh.nix
index 1c29eddf01d6..b3aa55e189a3 100644
--- a/nixos/modules/programs/mosh.nix
+++ b/nixos/modules/programs/mosh.nix
@@ -14,7 +14,6 @@ in
         Whether to enable mosh. Note, this will open ports in your firewall!
       '';
       default = false;
-      example = true;
       type = lib.types.bool;
     };
   };
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
new file mode 100644
index 000000000000..927fe68be875
--- /dev/null
+++ b/nixos/modules/programs/mtr.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.mtr;
+in {
+  options = {
+    programs.mtr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add mtr to the global environment and configure a
+          setcap wrapper for it.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.mtr = {
+      source = "${pkgs.mtr}/bin/mtr";
+      capabilities = "cap_net_raw+p";
+    };
+  };
+}
diff --git a/nixos/modules/programs/nano.nix b/nixos/modules/programs/nano.nix
index b8803eec7be1..27b6d446c75d 100644
--- a/nixos/modules/programs/nano.nix
+++ b/nixos/modules/programs/nano.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 let
   cfg = config.programs.nano;
@@ -20,16 +20,22 @@ in
         example = ''
           set nowrap
           set tabstospaces
-          set tabsize 4
+          set tabsize 2
         '';
       };
+      syntaxHighlight = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = "Whether to enable syntax highlight for various languages.";
+      };
     };
   };
 
   ###### implementation
 
   config = lib.mkIf (cfg.nanorc != "") {
-    environment.etc."nanorc".text = cfg.nanorc;
+    environment.etc."nanorc".text = lib.concatStrings [ cfg.nanorc
+      (lib.optionalString cfg.syntaxHighlight ''include "${pkgs.nano}/share/nano/*.nanorc"'') ];
   };
 
 }
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
new file mode 100644
index 000000000000..550634e65be9
--- /dev/null
+++ b/nixos/modules/programs/qt5ct.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.romildo ];
+
+  ###### interface
+  options = {
+    programs.qt5ct = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the Qt5 Configuration Tool (qt5ct), a
+          program that allows users to configure Qt5 settings (theme,
+          font, icons, etc.) under desktop environments or window
+          manager without Qt integration.
+
+          Official home page: <link xlink:href="https://sourceforge.net/projects/qt5ct/">https://sourceforge.net/projects/qt5ct/</link>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.qt5ct.enable {
+    environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
+    environment.systemPackages = [ pkgs.qt5ct ];
+  };
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index ce4d46e19bf9..0f3f42901bab 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -101,11 +101,15 @@ in
         chpasswd = { rootOK = true; };
       };
 
-    security.setuidPrograms = [ "su" "chfn" ]
-      ++ [ "newuidmap" "newgidmap" ] # new in shadow 4.2.x
-      ++ lib.optionals config.users.mutableUsers
-      [ "passwd" "sg" "newgrp" ];
-
+    security.wrappers = {
+      su.source        = "${pkgs.shadow.su}/bin/su";
+      chfn.source      = "${pkgs.shadow.out}/bin/chfn";
+      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/slock.nix b/nixos/modules/programs/slock.nix
new file mode 100644
index 000000000000..0e1281e62cd7
--- /dev/null
+++ b/nixos/modules/programs/slock.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.slock;
+
+in
+{
+  options = {
+    programs.slock = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to install slock screen locker with setuid wrapper.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.slock ];
+    security.wrappers.slock.source = "${pkgs.slock.out}/bin/slock";
+  };
+}
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
index f5d1873cc5c8..44756171b74c 100644
--- a/nixos/modules/programs/ssmtp.nix
+++ b/nixos/modules/programs/ssmtp.nix
@@ -22,7 +22,6 @@ in
       directDelivery = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Use the trivial Mail Transfer Agent (MTA)
           <command>ssmtp</command> package to allow programs to send
@@ -40,7 +39,8 @@ in
         example = "mail.example.org";
         description = ''
           The host name of the default mail server to use to deliver
-          e-mail.
+          e-mail. Can also contain a port number (ex: mail.example.org:587),
+          defaults to port 25 if no port is given.
         '';
       };
 
@@ -65,7 +65,6 @@ in
       useTLS = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Whether TLS should be used to connect to the default mail
           server.
@@ -75,7 +74,6 @@ in
       useSTARTTLS = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Whether the STARTTLS should be used to connect to the default
           mail server.  (This is needed for TLS-capable mail servers
@@ -98,9 +96,28 @@ in
         example = "correctHorseBatteryStaple";
         description = ''
           Password used for SMTP auth. (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)
+
+          It's recommended to use <option>authPassFile</option>
+          which takes precedence over <option>authPass</option>.
+        '';
+      };
+
+      authPassFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/run/keys/ssmtp-authpass";
+        description = ''
+          Path to a file that contains the password used for SMTP auth. The file
+          should not contain a trailing newline, if the password does not contain one.
+          This file should be readable by the users that need to execute ssmtp.
+
+          <option>authPassFile</option> takes precedence over <option>authPass</option>.
+
+          Warning: when <option>authPass</option> is non-empty <option>authPassFile</option>
+          defaults to a file in the WORLD-READABLE Nix store containing that password.
         '';
       };
-      
+
       setSendmail = mkOption {
         type = types.bool;
         default = true;
@@ -114,21 +131,28 @@ in
 
   config = mkIf cfg.directDelivery {
 
+    networking.defaultMailServer.authPassFile = mkIf (cfg.authPass != "")
+      (mkDefault (toString (pkgs.writeTextFile {
+        name = "ssmtp-authpass";
+        text = cfg.authPass;
+      })));
+
     environment.etc."ssmtp/ssmtp.conf".text =
+      let yesNo = yes : if yes then "YES" else "NO"; in
       ''
         MailHub=${cfg.hostName}
         FromLineOverride=YES
-        ${if cfg.root != "" then "root=${cfg.root}" else ""}
-        ${if cfg.domain != "" then "rewriteDomain=${cfg.domain}" else ""}
-        UseTLS=${if cfg.useTLS then "YES" else "NO"}
-        UseSTARTTLS=${if cfg.useSTARTTLS then "YES" else "NO"}
+        ${optionalString (cfg.root   != "") "root=${cfg.root}"}
+        ${optionalString (cfg.domain != "") "rewriteDomain=${cfg.domain}"}
+        UseTLS=${yesNo cfg.useTLS}
+        UseSTARTTLS=${yesNo cfg.useSTARTTLS}
         #Debug=YES
-        ${if cfg.authUser != "" then "AuthUser=${cfg.authUser}" else ""}
-        ${if cfg.authPass != "" then "AuthPass=${cfg.authPass}" else ""}
+        ${optionalString (cfg.authUser != "")       "AuthUser=${cfg.authUser}"}
+        ${optionalString (!isNull cfg.authPassFile) "AuthPassFile=${cfg.authPassFile}"}
       '';
 
     environment.systemPackages = [pkgs.ssmtp];
-    
+
     services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
       program = "sendmail";
       source = "${pkgs.ssmtp}/bin/sendmail";
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
index f0f8d03505ac..ed1d88a420a2 100644
--- a/nixos/modules/programs/tmux.nix
+++ b/nixos/modules/programs/tmux.nix
@@ -65,7 +65,6 @@ in {
 
       aggressiveResize = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Resize the window to the size of the smallest session for which it is the current window.
@@ -81,14 +80,12 @@ in {
 
       clock24 = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Use 24 hour clock.";
       };
 
       customPaneNavigationAndResize = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode.";
       };
@@ -124,14 +121,12 @@ in {
 
       newSession = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Automatically spawn a session if trying to attach and none are running.";
       };
 
       reverseSplit = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Reverse the window split shortcuts.";
       };
diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix
index 731ebed14c7b..110570ac3f06 100644
--- a/nixos/modules/programs/venus.nix
+++ b/nixos/modules/programs/venus.nix
@@ -45,7 +45,7 @@ in
         description = ''
           Specification (in the format described by
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
           which the Venus will collect feeds.
         '';
       };
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
index 8476c1accd31..fe0e7f2c6d6b 100644
--- a/nixos/modules/programs/vim.nix
+++ b/nixos/modules/programs/vim.nix
@@ -9,7 +9,6 @@ in {
     defaultEditor = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         When enabled, installs vim and configures vim to be the default editor
         using the EDITOR environment variable.
diff --git a/nixos/modules/programs/wireshark.nix b/nixos/modules/programs/wireshark.nix
new file mode 100644
index 000000000000..710d223b6f59
--- /dev/null
+++ b/nixos/modules/programs/wireshark.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wireshark;
+  wireshark = cfg.package;
+in {
+  options = {
+    programs.wireshark = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add Wireshark to the global environment and configure a
+          setcap wrapper for 'dumpcap' for users in the 'wireshark' group.
+        '';
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.wireshark-cli;
+        defaultText = "pkgs.wireshark-cli";
+        description = ''
+          Which Wireshark package to install in the global environment.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ wireshark ];
+    users.extraGroups.wireshark = {};
+
+    security.wrappers.dumpcap = {
+      source = "${wireshark}/bin/dumpcap";
+      capabilities = "cap_net_raw+p";
+      owner = "root";
+      group = "wireshark";
+      permissions = "u+rx,g+x";
+    };
+  };
+}
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
new file mode 100644
index 000000000000..335f596ca80f
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.oh-my-zsh;
+in
+  {
+    options = {
+      programs.zsh.oh-my-zsh = {
+        enable = mkOption {
+          default = false;
+          description = ''
+            Enable oh-my-zsh.
+          '';
+        };
+
+        plugins = mkOption {
+          default = [];
+          type = types.listOf(types.str);
+          description = ''
+            List of oh-my-zsh plugins
+          '';
+        };
+
+        custom = mkOption {
+          default = "";
+          type = types.str;
+          description = ''
+            Path to a custom oh-my-zsh package to override config of oh-my-zsh.
+          '';
+        };
+
+        theme = mkOption {
+          default = "";
+          type = types.str;
+          description = ''
+            Name of the theme to be used by oh-my-zsh.
+          '';
+        };
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = with pkgs; [ oh-my-zsh ];
+
+      programs.zsh.interactiveShellInit = with pkgs; with builtins; ''
+        # oh-my-zsh configuration generated by NixOS
+        export ZSH=${oh-my-zsh}/share/oh-my-zsh
+
+        ${optionalString (length(cfg.plugins) > 0)
+          "plugins=(${concatStringsSep " " cfg.plugins})"
+        }
+
+        ${optionalString (stringLength(cfg.custom) > 0)
+          "ZSH_CUSTOM=\"${cfg.custom}\""
+        }
+
+        ${optionalString (stringLength(cfg.theme) > 0)
+          "ZSH_THEME=\"${cfg.theme}\""
+        }
+
+        source $ZSH/oh-my-zsh.sh
+      '';
+    };
+  }
diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
new file mode 100644
index 000000000000..fde241ca3ce3
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.syntax-highlighting;
+in
+  {
+    options = {
+      programs.zsh.syntax-highlighting = {
+        enable = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Enable zsh-syntax-highlighting.
+          '';
+        };
+
+        highlighters = mkOption {
+          default = [ "main" ];
+
+          # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+          type = types.listOf(types.enum([
+            "main"
+            "brackets"
+            "pattern"
+            "cursor"
+            "root"
+            "line"
+          ]));
+
+          description = ''
+            Specifies the highlighters to be used by zsh-syntax-highlighting.
+
+            The following defined options can be found here:
+            https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+          '';
+        };
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ];
+
+      programs.zsh.interactiveShellInit = with pkgs; with builtins; ''
+        source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+
+        ${optionalString (length(cfg.highlighters) > 0)
+          "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})"
+        }
+      '';
+    };
+  }
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 990e6648e82b..acb3e987aee6 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -84,14 +84,6 @@ in
         type = types.bool;
       };
 
-      enableSyntaxHighlighting = mkOption {
-        default = false;
-        description = ''
-          Enable zsh-syntax-highlighting
-        '';
-        type = types.bool;
-      };
-      
       enableAutosuggestions = mkOption {
         default = false;
         description = ''
@@ -130,10 +122,6 @@ in
 
         ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""}
 
-        ${optionalString (cfg.enableSyntaxHighlighting)
-          "source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
-        }
-
         ${optionalString (cfg.enableAutosuggestions)
           "source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
         }
@@ -143,7 +131,6 @@ in
 
         ${cfge.interactiveShellInit}
 
-
         HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
       '';
 
@@ -206,8 +193,7 @@ in
     environment.etc."zinputrc".source = ./zinputrc;
 
     environment.systemPackages = [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions
-      ++ optional cfg.enableSyntaxHighlighting pkgs.zsh-syntax-highlighting;
+      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
 
     environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 758f229d59d7..8a313f6c7fca 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -10,7 +10,6 @@ with lib;
     (mkRenamedOptionModule [ "fonts" "enableFontConfig" ] [ "fonts" "fontconfig" "enable" ])
     (mkRenamedOptionModule [ "fonts" "extraFonts" ] [ "fonts" "fonts" ])
 
-    (mkRenamedOptionModule [ "security" "extraSetuidPrograms" ] [ "security" "setuidPrograms" ])
     (mkRenamedOptionModule [ "networking" "enableWLAN" ] [ "networking" "wireless" "enable" ])
     (mkRenamedOptionModule [ "networking" "enableRT73Firmware" ] [ "networking" "enableRalinkFirmware" ])
 
@@ -18,6 +17,7 @@ with lib;
     (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" ])
+    (mkRenamedOptionModule [ "services" "logstash" "address" ] [ "services" "logstash" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "kibana" "host" ] [ "services" "kibana" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "mpd" "network" "host" ] [ "services" "mpd" "network" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "neo4j" "host" ] [ "services" "neo4j" "listenAddress" ])
@@ -32,6 +32,12 @@ with lib;
 
     (mkRenamedOptionModule [ "services" "clamav" "updater" "config" ] [ "services" "clamav" "updater" "extraConfig" ])
 
+    (mkRemovedOptionModule [ "security" "setuidOwners" ] "Use security.wrappers instead")
+    (mkRemovedOptionModule [ "security" "setuidPrograms" ] "Use security.wrappers instead")
+
+    (mkRemovedOptionModule [ "services" "rmilter" "bindInetSockets" ] "Use services.rmilter.bindSocket.* instead")
+    (mkRemovedOptionModule [ "services" "rmilter" "bindUnixSockets" ] "Use services.rmilter.bindSocket.* instead")
+
     # Old Grub-related options.
     (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ])
     (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ])
@@ -100,9 +106,6 @@ with lib;
     (mkRenamedOptionModule [ "services" "xserver" "windowManager" "xbmc" ] [ "services" "xserver" "desktopManager" "kodi" ])
     (mkRenamedOptionModule [ "services" "xserver" "desktopManager" "xbmc" ] [ "services" "xserver" "desktopManager" "kodi" ])
 
-    # DNSCrypt-proxy
-    (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ])
-
     (mkRenamedOptionModule [ "services" "hostapd" "extraCfg" ] [ "services" "hostapd" "extraConfig" ])
 
     # Enlightenment
@@ -138,9 +141,6 @@ with lib;
     # Unity3D
     (mkRenamedOptionModule [ "programs" "unity3d" "enable" ] [ "security" "chromiumSuidSandbox" "enable" ])
 
-    # fontconfig-ultimate
-    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "rendering" ] [ "fonts" "fontconfig" "ultimate" "preset" ])
-
     # murmur
     (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ])
 
@@ -164,6 +164,27 @@ with lib;
         else { addr = value inetAddr; port = value inetPort; }
     ))
 
+    # dhcpd
+    (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ])
+
+    # locate
+    (mkRenamedOptionModule [ "services" "locate" "period" ] [ "services" "locate" "interval" ])
+    (mkRemovedOptionModule [ "services" "locate" "includeStore" ] "Use services.locate.prunePaths" )
+
+    # nfs
+    (mkRenamedOptionModule [ "services" "nfs" "lockdPort" ] [ "services" "nfs" "server" "lockdPort" ])
+    (mkRenamedOptionModule [ "services" "nfs" "statdPort" ] [ "services" "nfs" "server" "statdPort" ])
+
+    # KDE Plasma 5
+    (mkRenamedOptionModule [ "services" "xserver" "desktopManager" "kde5" ] [ "services" "xserver" "desktopManager" "plasma5" ])
+
+    # Fontconfig
+    (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "config" "fonts" "fontconfig" "allowBitmaps" ])
+    (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "allowType1" ] [ "config" "fonts" "fontconfig" "allowType1" ])
+    (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "config" "fonts" "fontconfig" "useEmbeddedBitmaps" ])
+    (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "config" "fonts" "fontconfig" "forceAutohint" ])
+    (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "config" "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
     (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
@@ -178,5 +199,13 @@ with lib;
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
     (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
+    (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" ] "")
+
+    # ZSH
+    (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntax-highlighting" "enable" ])
   ];
 }
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 726e54711410..5301ac14805d 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -19,6 +19,12 @@ let
         '';
       };
 
+      domain = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Domain to fetch certificate for (defaults to the entry name)";
+      };
+
       email = mkOption {
         type = types.nullOr types.str;
         default = null;
@@ -110,7 +116,7 @@ in
         description = ''
           Systemd calendar expression when to check for renewal. See
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>.
+          <manvolnum>7</manvolnum></citerefentry>.
         '';
       };
 
@@ -129,7 +135,7 @@ in
 
       certs = mkOption {
         default = { };
-        type = with types; loaOf (submodule certOpts);
+        type = with types; attrsOf (submodule certOpts);
         description = ''
           Attribute set of certificates to get signed and renewed.
         '';
@@ -157,9 +163,10 @@ in
           servicesLists = mapAttrsToList certToServices cfg.certs;
           certToServices = cert: data:
               let
+                domain = if data.domain != null then data.domain else cert;
                 cpath = "${cfg.directory}/${cert}";
                 rights = if data.allowKeysForGroup then "750" else "700";
-                cmdline = [ "-v" "-d" cert "--default_root" data.webroot "--valid_min" cfg.validMin ]
+                cmdline = [ "-v" "-d" 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);
@@ -178,7 +185,7 @@ in
                   path = [ pkgs.simp_le ];
                   preStart = ''
                     mkdir -p '${cfg.directory}'
-                    chown '${data.user}:${data.group}' '${cfg.directory}'
+                    chown -R '${data.user}:${data.group}' '${cfg.directory}'
                     if [ ! -d '${cpath}' ]; then
                       mkdir '${cpath}'
                     fi
@@ -284,6 +291,8 @@ in
             OnCalendar = cfg.renewInterval;
             Unit = "acme-${cert}.service";
             Persistent = "yes";
+            AccuracySec = "5m";
+            RandomizedDelaySec = "1h";
           };
         })
       );
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
index 4a6d61d26766..dfbf5d859ba9 100644
--- a/nixos/modules/security/apparmor-suid.nix
+++ b/nixos/modules/security/apparmor-suid.nix
@@ -19,7 +19,7 @@ with lib;
   config = mkIf (cfg.confineSUIDApplications) {
     security.apparmor.profiles = [ (pkgs.writeText "ping" ''
       #include <tunables/global>
-      /var/setuid-wrappers/ping {
+      /run/wrappers/bin/ping {
         #include <abstractions/base>
         #include <abstractions/consoles>
         #include <abstractions/nameservice>
@@ -33,7 +33,6 @@ with lib;
         ${pkgs.attr.out}/lib/libattr.so* mr,
 
         ${pkgs.iputils}/bin/ping mixr,
-        /var/setuid-wrappers/ping.real r,
 
         #/etc/modules.conf r,
 
diff --git a/nixos/modules/security/chromium-suid-sandbox.nix b/nixos/modules/security/chromium-suid-sandbox.nix
index 88fbe518c2de..0458ffb6c46c 100644
--- a/nixos/modules/security/chromium-suid-sandbox.nix
+++ b/nixos/modules/security/chromium-suid-sandbox.nix
@@ -27,6 +27,6 @@ in
 
   config = mkIf cfg.enable {
     environment.systemPackages = [ sandbox ];
-    security.setuidPrograms    = [ sandbox.passthru.sandboxExecutableName ];
+    security.wrappers."${sandbox.passthru.sandboxExecutableName}".source = "${sandbox}/bin/${sandbox.passthru.sandboxExecutableName}";
   };
 }
diff --git a/nixos/modules/security/dhparams.nix b/nixos/modules/security/dhparams.nix
new file mode 100644
index 000000000000..55c75713101d
--- /dev/null
+++ b/nixos/modules/security/dhparams.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.security.dhparams;
+in
+{
+  options = {
+    security.dhparams = {
+      params = mkOption {
+        description =
+          ''
+            Diffie-Hellman parameters to generate.
+
+            The value is the size (in bits) of the DH params to generate. The
+            generated DH params path can be found in
+            <filename><replaceable>security.dhparams.path</replaceable>/<replaceable>name</replaceable>.pem</filename>.
+
+            Note: The name of the DH params is taken as being the name of the
+            service it serves: the params will be generated before the said
+            service is started.
+
+            Warning: If you are removing all dhparams from this list, you have
+            to leave security.dhparams.enable for at least one activation in
+            order to have them be cleaned up. This also means if you rollback to
+            a version without any dhparams the existing ones won't be cleaned
+            up.
+          '';
+        type = with types; attrsOf int;
+        default = {};
+        example = { nginx = 3072; };
+      };
+
+      path = mkOption {
+        description =
+          ''
+            Path to the directory in which Diffie-Hellman parameters will be
+            stored.
+          '';
+        type = types.str;
+        default = "/var/lib/dhparams";
+      };
+
+      enable = mkOption {
+        description =
+          ''
+            Whether to generate new DH params and clean up old DH params.
+          '';
+        default = false;
+        type = types.bool;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services = {
+      dhparams-init = {
+        description = "Cleanup old Diffie-Hellman parameters";
+        wantedBy = [ "multi-user.target" ]; # Clean up even when no DH params is set
+        serviceConfig.Type = "oneshot";
+        script =
+          # Create directory
+          ''
+            if [ ! -d ${cfg.path} ]; then
+              mkdir -p ${cfg.path}
+            fi
+          '' +
+          # Remove old dhparams
+          ''
+            for file in ${cfg.path}/*; do
+              if [ ! -f "$file" ]; then
+                continue
+              fi
+          '' + concatStrings (mapAttrsToList (name: value:
+          ''
+              if [ "$file" == "${cfg.path}/${name}.pem" ] && \
+                  ${pkgs.openssl}/bin/openssl dhparam -in "$file" -text | head -n 1 | grep "(${toString value} bit)" > /dev/null; then
+                continue
+              fi
+          ''
+          ) cfg.params) +
+          ''
+              rm $file
+            done
+
+            # TODO: Ideally this would be removing the *former* cfg.path, though this
+            # does not seem really important as changes to it are quite unlikely
+            rmdir --ignore-fail-on-non-empty ${cfg.path}
+          '';
+      };
+    } //
+      mapAttrs' (name: value: nameValuePair "dhparams-gen-${name}" {
+        description = "Generate Diffie-Hellman parameters for ${name} if they don't exist yet";
+        after = [ "dhparams-init.service" ];
+        before = [ "${name}.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig.Type = "oneshot";
+        script =
+          ''
+            mkdir -p ${cfg.path}
+            if [ ! -f ${cfg.path}/${name}.pem ]; then
+              ${pkgs.openssl}/bin/openssl dhparam -out ${cfg.path}/${name}.pem ${toString value}
+            fi
+          '';
+      }) cfg.params;
+  };
+}
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
index 97e2d39dc076..9ca818e86ffa 100644
--- a/nixos/modules/security/duosec.nix
+++ b/nixos/modules/security/duosec.nix
@@ -187,7 +187,8 @@ in
       ];
 
      environment.systemPackages = [ pkgs.duo-unix ];
-     security.setuidPrograms    = [ "login_duo" ];
+
+     security.wrappers.login_duo.source = "${pkgs.duo-unix.out}/bin/login_duo";
      environment.etc = loginCfgFile ++ pamCfgFile;
 
      /* If PAM *and* SSH are enabled, then don't do anything special.
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index 3726b6c78185..d23c7f2e86de 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -13,7 +13,7 @@ in
 
 {
   meta = {
-    maintainers = with maintainers; [ joachifm ];
+    maintainers = with maintainers; [ ];
     doc = ./grsecurity.xml;
   };
 
@@ -21,7 +21,6 @@ in
 
     enable = mkOption {
       type = types.bool;
-      example = true;
       default = false;
       description = ''
         Enable grsecurity/PaX.
@@ -30,7 +29,6 @@ in
 
     lockTunables = mkOption {
       type = types.bool;
-      example = false;
       default = true;
       description = ''
         Whether to automatically lock grsecurity tunables
@@ -43,7 +41,6 @@ in
 
     disableEfiRuntimeServices = mkOption {
       type = types.bool;
-      example = false;
       default = true;
       description = ''
         Whether to disable access to EFI runtime services.  Enabling EFI runtime
diff --git a/nixos/modules/security/grsecurity.xml b/nixos/modules/security/grsecurity.xml
index a7bcf4924f01..0a884b3f9b55 100644
--- a/nixos/modules/security/grsecurity.xml
+++ b/nixos/modules/security/grsecurity.xml
@@ -7,35 +7,36 @@
   <title>Grsecurity/PaX</title>
 
   <para>
-    Grsecurity/PaX is a set of patches against the Linux kernel that make it
-    harder to exploit bugs.  The patchset includes protections such as
-    enforcement of non-executable memory, address space layout randomization,
-    and chroot jail hardening.  These and other
+    Grsecurity/PaX is a set of patches against the Linux kernel that
+    implements an extensive suite of
     <link xlink:href="https://grsecurity.net/features.php">features</link>
-    render entire classes of exploits inert without additional efforts on the
-    part of the adversary.
+    designed to increase the difficulty of exploiting kernel and
+    application bugs.
   </para>
 
   <para>
     The NixOS grsecurity/PaX module is designed with casual users in mind and is
-    intended to be compatible with normal desktop usage, without unnecessarily
-    compromising security.  The following sections describe the configuration
-    and administration of a grsecurity/PaX enabled NixOS system.  For
-    more comprehensive coverage, please refer to the
+    intended to be compatible with normal desktop usage, without
+    <emphasis>unnecessarily</emphasis> compromising security.  The
+    following sections describe the configuration and administration of
+    a grsecurity/PaX enabled NixOS system.  For more comprehensive
+    coverage, please refer to the
     <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity">grsecurity wikibook</link>
     and the
     <link xlink:href="https://wiki.archlinux.org/index.php/Grsecurity">Arch
     Linux wiki page on grsecurity</link>.
 
-    <note><para>grsecurity/PaX is only available for the latest linux -stable
-    kernel; patches against older kernels are available from upstream only for
-    a fee.</para></note>
+    <warning><para>Upstream has ceased free support for grsecurity/PaX.  See
+    <link xlink:href="https://grsecurity.net/passing_the_baton.php">
+    the announcement</link> for more information.  Consequently, NixOS
+    support for grsecurity/PaX also must cease.  Enabling this module will
+    result in a build error.</para></warning>
     <note><para>We standardise on a desktop oriented configuration primarily due
     to lack of resources.  The grsecurity/PaX configuration state space is huge
     and each configuration requires quite a bit of testing to ensure that the
     resulting packages work as advertised.  Defining additional package sets
     would likely result in a large number of functionally broken packages, to
-    nobody's benefit.</para></note>.
+    nobody's benefit.</para></note>
   </para>
 
   <sect1 xml:id="sec-grsec-enable"><title>Enabling grsecurity/PaX</title>
@@ -126,10 +127,10 @@
     The NixOS kernel is built using upstream's recommended settings for a
     desktop deployment that generally favours security over performance.  This
     section details deviations from upstream's recommendations that may
-    compromise operational security.
+    compromise security.
 
     <warning><para>There may be additional problems not covered here!</para>
-    </warning>.
+    </warning>
   </para>
 
   <itemizedlist>
@@ -159,8 +160,8 @@
     <listitem><para>
       The NixOS module conditionally weakens <command>chroot</command>
       restrictions to accommodate NixOS lightweight containers and sandboxed Nix
-      builds.  This is problematic if the deployment also runs a privileged
-      network facing process that <emphasis>relies</emphasis> on
+      builds.  This can be problematic if the deployment also runs privileged
+      network facing processes that <emphasis>rely</emphasis> on
       <command>chroot</command> for isolation.
     </para></listitem>
 
@@ -215,21 +216,24 @@
             GRKERNSEC_CONFIG_SERVER y
             GRKERNSEC_CONFIG_SECURITY y
           '';
-          };
-      }
+        };
+      };
     </programlisting>
   </para>
 
   <para>
-    The wikibook provides an exhaustive listing of
+    The grsecurity/PaX wikibook provides an exhaustive listing of
     <link xlink:href="https://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options">kernel configuration options</link>.
   </para>
 
   <para>
     The NixOS module makes several assumptions about the kernel and so
     may be incompatible with your customised kernel. Currently, the only way
-    to work around incompatibilities is to eschew the NixOS module.
+    to work around these incompatibilities is to eschew the NixOS
+    module.
+  </para>
 
+  <para>
     If not using the NixOS module, a custom grsecurity package set can
     be specified inline instead, as in
     <programlisting>
@@ -290,7 +294,7 @@
 
     <listitem><para>User initiated autoloading of modules (e.g., when
     using fuse or loop devices) is disallowed; either load requisite modules
-    as root or add them to<option>boot.kernelModules</option>.</para></listitem>
+    as root or add them to <option>boot.kernelModules</option>.</para></listitem>
 
     <listitem><para>Virtualization: KVM is the preferred virtualization
     solution. Xen, Virtualbox, and VMWare are
@@ -310,7 +314,7 @@
       Overflows in boot critical code (e.g., the root filesystem module) can
       render the system unbootable.  Work around by setting
       <programlisting>
-        boot.kernel.kernelParams = [ "pax_size_overflow_report_only" ];
+        boot.kernelParams = [ "pax_size_overflow_report_only" ];
       </programlisting>
     </para></listitem>
 
diff --git a/nixos/modules/security/lock-kernel-modules.nix b/nixos/modules/security/lock-kernel-modules.nix
new file mode 100644
index 000000000000..260ec3fc9464
--- /dev/null
+++ b/nixos/modules/security/lock-kernel-modules.nix
@@ -0,0 +1,36 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    security.lockKernelModules = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Disable kernel module loading once the system is fully initialised.
+        Module loading is disabled until the next reboot.  Problems caused
+        by delayed module loading can be fixed by adding the module(s) in
+        question to <option>boot.kernelModules</option>.
+      '';
+    };
+  };
+
+  config = mkIf config.security.lockKernelModules {
+    systemd.services.disable-kernel-module-loading = rec {
+      description = "Disable kernel module loading";
+
+      wantedBy = [ config.systemd.defaultUnit ];
+      after = [ "systemd-udev-settle.service" "firewall.service" "systemd-modules-load.service" ] ++ wantedBy;
+
+      script = "echo -n 1 > /proc/sys/kernel/modules_disabled";
+
+      unitConfig.ConditionPathIsReadWrite = "/proc/sys/kernel";
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 96e7c45d4963..5632500df2e0 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -212,6 +212,17 @@ let
         '';
       };
 
+      enableKwallet = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, pam_wallet will attempt to automatically unlock the
+          user's default KDE wallet upon login. If the user has no wallet named
+          "kdewallet", or the login password does not match their wallet
+          password, KDE will prompt separately after login.
+        '';
+      };
+
       text = mkOption {
         type = types.nullOr types.lines;
         description = "Contents of the PAM service file.";
@@ -253,6 +264,8 @@ let
               "auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"}
           ${optionalString cfg.usbAuth
               "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
+          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
+              "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
         '' +
           # Modules in this block require having the password set in PAM_AUTHTOK.
           # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
@@ -260,19 +273,20 @@ 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)) ''
+          (optionalString (cfg.unixAuth && (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet)) ''
               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"}
               ${optionalString cfg.pamMount
                 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
+              ${optionalString cfg.enableKwallet
+                ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
+                 " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
           ${optionalString cfg.otpwAuth
               "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
-              "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
           ${optionalString use_ldap
               "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
           ${optionalString config.services.sssd.enable
@@ -334,6 +348,9 @@ let
               "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
           ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable)
               "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
+          ${optionalString (cfg.enableKwallet)
+              ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
+               " kwalletd=${pkgs.libsForQt5.kwallet}/bin/kwalletd5")}
         '');
     };
 
@@ -472,19 +489,20 @@ in
       ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ]
       ++ optionals config.security.pam.enableEcryptfs [ pkgs.ecryptfs ];
 
-    security.setuidPrograms =
-        optionals config.security.pam.enableEcryptfs [ "mount.ecryptfs_private" "umount.ecryptfs_private" ];
+    security.wrappers = {
+      unix_chkpwd = {
+        source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
+        owner = "root";
+        setuid = true;
+      };
+    } // (if config.security.pam.enableEcryptfs then {
+      "mount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/mount.ecryptfs_private";
+       "umount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/umount.ecryptfs_private";
+    } else {});
 
     environment.etc =
       mapAttrsToList (n: v: makePAMService v) config.security.pam.services;
 
-    security.setuidOwners = [ {
-      program = "unix_chkpwd";
-      source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
-      owner = "root";
-      setuid = true;
-    } ];
-
     security.pam.services =
       { other.text =
           ''
diff --git a/nixos/modules/security/pam_usb.nix b/nixos/modules/security/pam_usb.nix
index 11708a1f0167..6f811dab8d76 100644
--- a/nixos/modules/security/pam_usb.nix
+++ b/nixos/modules/security/pam_usb.nix
@@ -32,10 +32,12 @@ in
 
   config = mkIf (cfg.enable || anyUsbAuth) {
 
-    # pmount need to have a set-uid bit to make pam_usb works in user
-    # environment. (like su, sudo)
+    # Make sure pmount and pumount are setuid wrapped.
+    security.wrappers = {
+      pmount.source = "${pkgs.pmount.out}/bin/pmount";
+      pumount.source = "${pkgs.pmount.out}/bin/pumount";
+    };
 
-    security.setuidPrograms = [ "pmount" "pumount" ];
     environment.systemPackages = [ pkgs.pmount ];
 
   };
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
index 507f81bbf073..7e59408a5b0b 100644
--- a/nixos/modules/security/polkit.nix
+++ b/nixos/modules/security/polkit.nix
@@ -64,7 +64,7 @@ in
     systemd.packages = [ pkgs.polkit.out ];
 
     systemd.services.polkit.restartTriggers = [ config.system.path ];
-    systemd.services.polkit.unitConfig.X-StopIfChanged = false;
+    systemd.services.polkit.stopIfChanged = false;
 
     # The polkit daemon reads action/rule files
     environment.pathsToLink = [ "/share/polkit-1" ];
@@ -83,16 +83,10 @@ in
 
     security.pam.services.polkit-1 = {};
 
-    security.setuidPrograms = [ "pkexec" ];
-
-    security.setuidOwners = [
-      { program = "polkit-agent-helper-1";
-        owner = "root";
-        group = "root";
-        setuid = true;
-        source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
-      }
-    ];
+    security.wrappers = {
+      pkexec.source = "${pkgs.polkit.bin}/bin/pkexec";
+      "polkit-agent-helper-1".source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
+    };
 
     system.activationScripts.polkit =
       ''
diff --git a/nixos/modules/security/setuid-wrapper.c b/nixos/modules/security/setuid-wrapper.c
deleted file mode 100644
index ffd0b65b7629..000000000000
--- a/nixos/modules/security/setuid-wrapper.c
+++ /dev/null
@@ -1,81 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-
-/* Make sure assertions are not compiled out.  */
-#undef NDEBUG
-
-extern char **environ;
-
-static char * wrapperDir = WRAPPER_DIR;
-
-int main(int argc, char * * argv)
-{
-    char self[PATH_MAX];
-
-    int len = readlink("/proc/self/exe", self, sizeof(self) - 1);
-    assert (len > 0);
-    self[len] = 0;
-
-    /* Make sure that we are being executed from the right location,
-       i.e., `wrapperDir'.  This is to prevent someone from
-       creating hard link `X' from some other location, along with a
-       false `X.real' file, to allow arbitrary programs from being
-       executed setuid.  */
-    assert ((strncmp(self, wrapperDir, strlen(wrapperDir)) == 0) &&
-            (self[strlen(wrapperDir)] == '/'));
-
-    /* Make *really* *really* sure that we were executed as `self',
-       and not, say, as some other setuid program.  That is, our
-       effective uid/gid should match the uid/gid of `self'. */
-    //printf("%d %d\n", geteuid(), getegid());
-
-    struct stat st;
-    assert (lstat(self, &st) != -1);
-
-    //printf("%d %d\n", st.st_uid, st.st_gid);
-
-    assert ((st.st_mode & S_ISUID) == 0 ||
-            (st.st_uid == geteuid()));
-
-    assert ((st.st_mode & S_ISGID) == 0 ||
-            st.st_gid == getegid());
-
-    /* And, of course, we shouldn't be writable. */
-    assert (!(st.st_mode & (S_IWGRP | S_IWOTH)));
-
-
-    /* Read the path of the real (wrapped) program from <self>.real. */
-    char realFN[PATH_MAX + 10];
-    int realFNSize = snprintf (realFN, sizeof(realFN), "%s.real", self);
-    assert (realFNSize < sizeof(realFN));
-
-    int fdSelf = open(realFN, O_RDONLY);
-    assert (fdSelf != -1);
-
-    char real[PATH_MAX];
-    len = read(fdSelf, real, PATH_MAX);
-    assert (len != -1);
-    assert (len < sizeof (real));
-    assert (len > 0);
-    real[len] = 0;
-
-    close(fdSelf);
-
-    //printf("real = %s, len = %d\n", real, len);
-
-    execve(real, argv, environ);
-
-    fprintf(stderr, "%s: cannot run `%s': %s\n",
-        argv[0], real, strerror(errno));
-
-    exit(1);
-}
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
deleted file mode 100644
index fe220c94313f..000000000000
--- a/nixos/modules/security/setuid-wrappers.nix
+++ /dev/null
@@ -1,146 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  inherit (config.security) wrapperDir;
-
-  setuidWrapper = pkgs.stdenv.mkDerivation {
-    name = "setuid-wrapper";
-    unpackPhase = "true";
-    installPhase = ''
-      mkdir -p $out/bin
-      cp ${./setuid-wrapper.c} setuid-wrapper.c
-      gcc -Wall -O2 -DWRAPPER_DIR=\"/run/setuid-wrapper-dirs\" \
-          setuid-wrapper.c -o $out/bin/setuid-wrapper
-    '';
-  };
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    security.setuidPrograms = mkOption {
-      type = types.listOf types.str;
-      default = [];
-      example = ["passwd"];
-      description = ''
-        The Nix store cannot contain setuid/setgid programs directly.
-        For this reason, NixOS can automatically generate wrapper
-        programs that have the necessary privileges.  This option
-        lists the names of programs in the system environment for
-        which setuid root wrappers should be created.
-      '';
-    };
-
-    security.setuidOwners = mkOption {
-      type = types.listOf types.attrs;
-      default = [];
-      example =
-        [ { program = "sendmail";
-            owner = "nobody";
-            group = "postdrop";
-            setuid = false;
-            setgid = true;
-            permissions = "u+rx,g+x,o+x";
-          }
-        ];
-      description = ''
-        This option allows the ownership and permissions on the setuid
-        wrappers for specific programs to be overridden from the
-        default (setuid root, but not setgid root).
-      '';
-    };
-
-    security.wrapperDir = mkOption {
-      internal = true;
-      type = types.path;
-      default = "/var/setuid-wrappers";
-      description = ''
-        This option defines the path to the setuid wrappers.  It
-        should generally not be overriden. Some packages in Nixpkgs
-        expect that <option>wrapperDir</option> is
-        <filename>/var/setuid-wrappers</filename>.
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = {
-
-    security.setuidPrograms = [ "fusermount" ];
-
-    system.activationScripts.setuid =
-      let
-        setuidPrograms =
-          (map (x: { program = x; owner = "root"; group = "root"; setuid = true; })
-            config.security.setuidPrograms)
-          ++ config.security.setuidOwners;
-
-        makeSetuidWrapper =
-          { program
-          , source ? ""
-          , owner ? "nobody"
-          , group ? "nogroup"
-          , setuid ? false
-          , setgid ? false
-          , permissions ? "u+rx,g+x,o+x"
-          }:
-
-          ''
-            if ! source=${if source != "" then source else "$(readlink -f $(PATH=$SETUID_PATH type -tP ${program}))"}; then
-                # If we can't find the program, fall back to the
-                # system profile.
-                source=/nix/var/nix/profiles/default/bin/${program}
-            fi
-
-            cp ${setuidWrapper}/bin/setuid-wrapper $wrapperDir/${program}
-            echo -n "$source" > $wrapperDir/${program}.real
-            chmod 0000 $wrapperDir/${program} # to prevent races
-            chown ${owner}.${group} $wrapperDir/${program}
-            chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" $wrapperDir/${program}
-          '';
-
-      in stringAfter [ "users" ]
-        ''
-          # Look in the system path and in the default profile for
-          # programs to be wrapped.
-          SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin
-
-          mkdir -p /run/setuid-wrapper-dirs
-          wrapperDir=$(mktemp --directory --tmpdir=/run/setuid-wrapper-dirs setuid-wrappers.XXXXXXXXXX)
-          chmod a+rx $wrapperDir
-
-          ${concatMapStrings makeSetuidWrapper setuidPrograms}
-
-          if [ -L ${wrapperDir} ]; then
-            # Atomically replace the symlink
-            # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
-            old=$(readlink ${wrapperDir})
-            ln --symbolic --force --no-dereference $wrapperDir ${wrapperDir}-tmp
-            mv --no-target-directory ${wrapperDir}-tmp ${wrapperDir}
-            rm --force --recursive $old
-          elif [ -d ${wrapperDir} ]; then
-            # Compatibility with old state, just remove the folder and symlink
-            rm -f ${wrapperDir}/*
-            # if it happens to be a tmpfs
-            ${pkgs.utillinux}/bin/umount ${wrapperDir} || true
-            rm -d ${wrapperDir}
-            ln -d --symbolic $wrapperDir ${wrapperDir}
-          else
-            # For initial setup
-            ln --symbolic $wrapperDir ${wrapperDir}
-          fi
-        '';
-
-  };
-
-}
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index f5612e1b0c5d..67a9b9a45ee3 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -81,7 +81,10 @@ in
         ${cfg.extraConfig}
       '';
 
-    security.setuidPrograms = [ "sudo" "sudoedit" ];
+    security.wrappers = {
+      sudo.source = "${pkgs.sudo.out}/bin/sudo";
+      sudoedit.source = "${pkgs.sudo.out}/bin/sudoedit";
+    };
 
     environment.systemPackages = [ sudo ];
 
diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
new file mode 100644
index 000000000000..c051b7d49e3f
--- /dev/null
+++ b/nixos/modules/security/wrappers/default.nix
@@ -0,0 +1,228 @@
+{ config, lib, pkgs, ... }:
+let
+
+  inherit (config.security) wrapperDir wrappers;
+
+  parentWrapperDir = dirOf wrapperDir;
+
+  programs =
+    (lib.mapAttrsToList
+      (n: v: (if v ? "program" then v else v // {program=n;}))
+      wrappers);
+
+  securityWrapper = pkgs.stdenv.mkDerivation {
+    name            = "security-wrapper";
+    phases          = [ "installPhase" "fixupPhase" ];
+    buildInputs     = [ pkgs.libcap pkgs.libcap_ng pkgs.linuxHeaders ];
+    hardeningEnable = [ "pie" ];
+    installPhase = ''
+      mkdir -p $out/bin
+      gcc -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \
+          -lcap-ng -lcap ${./wrapper.c} -o $out/bin/security-wrapper
+    '';
+  };
+
+  ###### Activation script for the setcap wrappers
+  mkSetcapProgram =
+    { program
+    , capabilities
+    , source
+    , owner  ? "nobody"
+    , group  ? "nogroup"
+    , permissions ? "u+rx,g+x,o+x"
+    , ...
+    }:
+    assert (lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3");
+    ''
+      cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program}
+      echo -n "${source}" > $wrapperDir/${program}.real
+
+      # Prevent races
+      chmod 0000 $wrapperDir/${program}
+      chown ${owner}.${group} $wrapperDir/${program}
+
+      # Set desired capabilities on the file plus cap_setpcap so
+      # the wrapper program can elevate the capabilities set on
+      # its file into the Ambient set.
+      ${pkgs.libcap.out}/bin/setcap "cap_setpcap,${capabilities}" $wrapperDir/${program}
+
+      # Set the executable bit
+      chmod ${permissions} $wrapperDir/${program}
+    '';
+
+  ###### Activation script for the setuid wrappers
+  mkSetuidProgram =
+    { program
+    , source
+    , owner  ? "nobody"
+    , group  ? "nogroup"
+    , setuid ? false
+    , setgid ? false
+    , permissions ? "u+rx,g+x,o+x"
+    , ...
+    }:
+    ''
+      cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program}
+      echo -n "${source}" > $wrapperDir/${program}.real
+
+      # Prevent races
+      chmod 0000 $wrapperDir/${program}
+      chown ${owner}.${group} $wrapperDir/${program}
+
+      chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" $wrapperDir/${program}
+    '';
+
+  mkWrappedPrograms =
+    builtins.map
+      (s: if (s ? "capabilities")
+          then mkSetcapProgram
+                 ({ owner = "root";
+                    group = "root";
+                  } // s)
+          else if 
+             (s ? "setuid"  && s.setuid  == true) ||
+             (s ? "setguid" && s.setguid == true) ||
+             (s ? "permissions")
+          then mkSetuidProgram s
+          else mkSetuidProgram
+                 ({ owner  = "root";
+                    group  = "root";
+                    setuid = true;
+                    setgid = false;
+                    permissions = "u+rx,g+x,o+x";
+                  } // s)
+      ) programs;
+in
+{
+
+  ###### interface
+
+  options = {
+    security.wrappers = lib.mkOption {
+      type = lib.types.attrs;
+      default = {};
+      example = lib.literalExample
+        ''
+          { sendmail.source = "/nix/store/.../bin/sendmail";
+            ping = {
+              source  = "${pkgs.iputils.out}/bin/ping";
+              owner   = "nobody";
+              group   = "nogroup";
+              capabilities = "cap_net_raw+ep";
+            };
+          }
+        '';
+      description = ''
+        This option allows the ownership and permissions on the setuid
+        wrappers for specific programs to be overridden from the
+        default (setuid root, but not setgid root).
+
+        <note>
+          <para>The sub-attribute <literal>source</literal> is mandatory,
+          it must be the absolute path to the program to be wrapped.
+          </para>
+
+          <para>The sub-attribute <literal>program</literal> is optional and
+          can give the wrapper program a new name. The default name is the same
+          as the attribute name itself.</para>
+
+          <para>Additionally, this option can set capabilities on a
+          wrapper program that propagates those capabilities down to the
+          wrapped, real program.</para>
+
+          <para>NOTE: cap_setpcap, which is required for the wrapper
+          program to be able to raise caps into the Ambient set is NOT
+          raised to the Ambient set so that the real program cannot
+          modify its own capabilities!! This may be too restrictive for
+          cases in which the real program needs cap_setpcap but it at
+          least leans on the side security paranoid vs. too
+          relaxed.</para>
+        </note>
+      '';
+    };
+
+    security.wrapperDir = lib.mkOption {
+      type        = lib.types.path;
+      default     = "/run/wrappers/bin";
+      internal    = true;
+      description = ''
+        This option defines the path to the wrapper programs. It
+        should not be overriden.
+      '';
+    };
+  };
+
+  ###### implementation
+  config = {
+
+    security.wrappers.fusermount.source = "${pkgs.fuse}/bin/fusermount";
+
+    boot.specialFileSystems.${parentWrapperDir} = {
+      fsType = "tmpfs";
+      options = [ "nodev" ];
+    };
+
+    # Make sure our wrapperDir exports to the PATH env variable when
+    # initializing the shell
+    environment.extraInit = ''
+      # Wrappers override other bin directories.
+      export PATH="${wrapperDir}:$PATH"
+    '';
+
+    ###### setcap activation script
+    system.activationScripts.wrappers =
+      lib.stringAfter [ "users" ]
+        ''
+          # Look in the system path and in the default profile for
+          # programs to be wrapped.
+          WRAPPER_PATH=${config.system.path}/bin:${config.system.path}/sbin
+
+          # Remove the old /var/setuid-wrappers path from the system...
+          #
+          # TODO: this is only necessary for upgrades 16.09 => 17.x;
+          # this conditional removal block needs to be removed after
+          # the release.
+          if [ -d /var/setuid-wrappers ]; then
+            rm -rf /var/setuid-wrappers
+            ln -s /run/wrappers/bin /var/setuid-wrappers
+          fi
+
+          # Remove the old /run/setuid-wrappers-dir path from the
+          # system as well...
+          #
+          # TODO: this is only necessary for upgrades 16.09 => 17.x;
+          # this conditional removal block needs to be removed after
+          # the release.
+          if [ -d /run/setuid-wrapper-dirs ]; then
+            rm -rf /run/setuid-wrapper-dirs
+            ln -s /run/wrappers/bin /run/setuid-wrapper-dirs
+          fi
+
+          # TODO: this is only necessary for upgrades 16.09 => 17.x;
+          # this conditional removal block needs to be removed after
+          # the release.
+          if readlink -f /run/booted-system | grep nixos-17 > /dev/null; then
+            rm -rf /run/setuid-wrapper-dirs
+            rm -rf /var/setuid-wrappers
+          fi
+
+          # We want to place the tmpdirs for the wrappers to the parent dir.
+          wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
+          chmod a+rx $wrapperDir
+
+          ${lib.concatStringsSep "\n" mkWrappedPrograms}
+
+          if [ -L ${wrapperDir} ]; then
+            # Atomically replace the symlink
+            # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
+            old=$(readlink -f ${wrapperDir})
+            ln --symbolic --force --no-dereference $wrapperDir ${wrapperDir}-tmp
+            mv --no-target-directory ${wrapperDir}-tmp ${wrapperDir}
+            rm --force --recursive $old
+          else
+            # For initial setup
+            ln --symbolic $wrapperDir ${wrapperDir}
+          fi
+        '';
+  };
+}
diff --git a/nixos/modules/security/wrappers/wrapper.c b/nixos/modules/security/wrappers/wrapper.c
new file mode 100644
index 000000000000..7091e314bb22
--- /dev/null
+++ b/nixos/modules/security/wrappers/wrapper.c
@@ -0,0 +1,239 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <assert.h>
+#include <errno.h>
+#include <linux/capability.h>
+#include <sys/capability.h>
+#include <linux/prctl.h>
+#include <sys/prctl.h>
+#include <cap-ng.h>
+
+// Make sure assertions are not compiled out, we use them to codify
+// invariants about this program and we want it to fail fast and
+// loudly if they are violated.
+#undef NDEBUG
+
+extern char **environ;
+
+// The WRAPPER_DIR macro is supplied at compile time so that it cannot
+// be changed at runtime
+static char * wrapperDir = WRAPPER_DIR;
+
+// Wrapper debug variable name
+static char * wrapperDebug = "WRAPPER_DEBUG";
+
+// Update the capabilities of the running process to include the given
+// capability in the Ambient set.
+static void set_ambient_cap(cap_value_t cap)
+{
+    capng_get_caps_process();
+
+    if (capng_update(CAPNG_ADD, CAPNG_INHERITABLE, (unsigned long) cap))
+    {
+        perror("cannot raise the capability into the Inheritable set\n");
+        exit(1);
+    }
+
+    capng_apply(CAPNG_SELECT_CAPS);
+    
+    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long) cap, 0, 0))
+    {
+        perror("cannot raise the capability into the Ambient set\n");
+        exit(1);
+    }
+}
+
+// Given the path to this program, fetch its configured capability set
+// (as set by `setcap ... /path/to/file`) and raise those capabilities
+// into the Ambient set.
+static int make_caps_ambient(const char *selfPath)
+{
+    cap_t caps = cap_get_file(selfPath);
+
+    if(!caps)
+    {
+        if(getenv(wrapperDebug))
+            fprintf(stderr, "no caps set or could not retrieve the caps for this file, not doing anything...");
+
+        return 1;
+    }
+
+    // We use `cap_to_text` and iteration over the tokenized result
+    // string because, as of libcap's current release, there is no
+    // facility for retrieving an array of `cap_value_t`'s that can be
+    // given to `prctl` in order to lift that capability into the
+    // Ambient set.
+    //
+    // Some discussion was had around shot-gunning all of the
+    // capabilities we know about into the Ambient set but that has a
+    // security smell and I deemed the risk of the current
+    // implementation crashing the program to be lower than the risk
+    // of a privilege escalation security hole being introduced by
+    // raising all capabilities, even ones we didn't intend for the
+    // program, into the Ambient set.
+    //
+    // `cap_t` which is returned by `cap_get_*` is an opaque type and
+    // even if we could retrieve the bitmasks (which, as far as I can
+    // tell we cannot) in order to get the `cap_value_t`
+    // representation for each capability we would have to take the
+    // total number of capabilities supported and iterate over the
+    // sequence of integers up-to that maximum total, testing each one
+    // against the bitmask ((bitmask >> n) & 1) to see if it's set and
+    // aggregating each "capability integer n" that is set in the
+    // bitmask.
+    //
+    // That, combined with the fact that we can't easily get the
+    // bitmask anyway seemed much more brittle than fetching the
+    // `cap_t`, transforming it into a textual representation,
+    // tokenizing the string, and using `cap_from_name` on the token
+    // to get the `cap_value_t` that we need for `prctl`. There is
+    // indeed risk involved if the output string format of
+    // `cap_to_text` ever changes but at this time the combination of
+    // factors involving the below list have led me to the conclusion
+    // that the best implementation at this time is reading then
+    // parsing with *lots of documentation* about why we're doing it
+    // this way.
+    //
+    // 1. No explicit API for fetching an array of `cap_value_t`'s or
+    //    for transforming a `cap_t` into such a representation
+    // 2. The risk of a crash is lower than lifting all capabilities
+    //    into the Ambient set
+    // 3. libcap is depended on heavily in the Linux ecosystem so
+    //    there is a high chance that the output representation of
+    //    `cap_to_text` will not change which reduces our risk that
+    //    this parsing step will cause a crash
+    //
+    // The preferred method, should it ever be available in the
+    // future, would be to use libcap API's to transform the result
+    // from a `cap_get_*` into an array of `cap_value_t`'s that can
+    // then be given to prctl.
+    //
+    // - Parnell
+    ssize_t capLen;
+    char* capstr = cap_to_text(caps, &capLen);
+    cap_free(caps);
+    
+    // TODO: For now, we assume that cap_to_text always starts its
+    // result string with " =" and that the first capability is listed
+    // immediately after that. We should verify this.
+    assert(capLen >= 2);
+    capstr += 2;
+
+    char* saveptr = NULL;
+    for(char* tok = strtok_r(capstr, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr))
+    {
+      cap_value_t capnum;
+      if (cap_from_name(tok, &capnum))
+      {
+          if(getenv(wrapperDebug))
+              fprintf(stderr, "cap_from_name failed, skipping: %s", tok);
+      }
+      else if (capnum == CAP_SETPCAP)
+      {
+          // Check for the cap_setpcap capability, we set this on the
+          // wrapper so it can elevate the capabilities to the Ambient
+          // set but we do not want to propagate it down into the
+          // wrapped program.
+          //
+          // TODO: what happens if that's the behavior you want
+          // though???? I'm preferring a strict vs. loose policy here.
+          if(getenv(wrapperDebug))
+              fprintf(stderr, "cap_setpcap in set, skipping it\n");
+      }
+      else
+      {
+          set_ambient_cap(capnum);
+
+          if(getenv(wrapperDebug))
+              fprintf(stderr, "raised %s into the Ambient capability set\n", tok);
+      }
+    }
+    cap_free(capstr);
+
+    return 0;
+}
+
+int main(int argc, char * * argv)
+{
+    // I *think* it's safe to assume that a path from a symbolic link
+    // should safely fit within the PATH_MAX system limit. Though I'm
+    // not positive it's safe...
+    char selfPath[PATH_MAX];
+    int selfPathSize = readlink("/proc/self/exe", selfPath, sizeof(selfPath));
+
+    assert(selfPathSize > 0);
+
+    // Assert we have room for the zero byte, this ensures the path
+    // isn't being truncated because it's too big for the buffer.
+    //
+    // A better way to handle this might be to use something like the
+    // whereami library (https://github.com/gpakosz/whereami) or a
+    // loop that resizes the buffer and re-reads the link if the
+    // contents are being truncated.
+    assert(selfPathSize < sizeof(selfPath));
+
+    // Set the zero byte since readlink doesn't do that for us.
+    selfPath[selfPathSize] = '\0';
+
+    // Make sure that we are being executed from the right location,
+    // i.e., `safeWrapperDir'.  This is to prevent someone from creating
+    // hard link `X' from some other location, along with a false
+    // `X.real' file, to allow arbitrary programs from being executed
+    // with elevated capabilities.
+    int len = strlen(wrapperDir);
+    if (len > 0 && '/' == wrapperDir[len - 1])
+      --len;
+    assert(!strncmp(selfPath, wrapperDir, len));
+    assert('/' == wrapperDir[0]);
+    assert('/' == selfPath[len]);
+
+    // Make *really* *really* sure that we were executed as
+    // `selfPath', and not, say, as some other setuid program. That
+    // is, our effective uid/gid should match the uid/gid of
+    // `selfPath'.
+    struct stat st;
+    assert(lstat(selfPath, &st) != -1);
+
+    assert(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
+    assert(!(st.st_mode & S_ISGID) || (st.st_gid == getegid()));
+
+    // And, of course, we shouldn't be writable.
+    assert(!(st.st_mode & (S_IWGRP | S_IWOTH)));
+
+    // Read the path of the real (wrapped) program from <self>.real.
+    char realFN[PATH_MAX + 10];
+    int realFNSize = snprintf (realFN, sizeof(realFN), "%s.real", selfPath);
+    assert (realFNSize < sizeof(realFN));
+
+    int fdSelf = open(realFN, O_RDONLY);
+    assert (fdSelf != -1);
+
+    char sourceProg[PATH_MAX];
+    len = read(fdSelf, sourceProg, PATH_MAX);
+    assert (len != -1);
+    assert (len < sizeof(sourceProg));
+    assert (len > 0);
+    sourceProg[len] = 0;
+
+    close(fdSelf);
+
+    // Read the capabilities set on the wrapper and raise them in to
+    // the Ambient set so the program we're wrapping receives the
+    // capabilities too!
+    make_caps_ambient(selfPath);
+
+    execve(sourceProg, argv, environ);
+    
+    fprintf(stderr, "%s: cannot run `%s': %s\n",
+        argv[0], sourceProg, strerror(errno));
+
+    exit(1);
+}
+
+
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index a89215d73828..56af8fe152e0 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
 
+  name = "mpd";
+
   uid = config.ids.uids.mpd;
   gid = config.ids.gids.mpd;
   cfg = config.services.mpd;
@@ -54,13 +56,14 @@ in {
         description = ''
           Extra directives added to to the end of MPD's configuration file,
           mpd.conf. Basic configuration like file location and uid/gid
-          is added automatically to the beginning of the file.
+          is added automatically to the beginning of the file. For available
+          options see <literal>man 5 mpd.conf</literal>'.
         '';
       };
 
       dataDir = mkOption {
         type = types.path;
-        default = "/var/lib/mpd";
+        default = "/var/lib/${name}";
         description = ''
           The directory where MPD stores its state, tag cache,
           playlists etc.
@@ -69,13 +72,13 @@ in {
 
       user = mkOption {
         type = types.str;
-        default = "mpd";
+        default = name;
         description = "User account under which MPD runs.";
       };
 
       group = mkOption {
         type = types.str;
-        default = "mpd";
+        default = name;
         description = "Group account under which MPD runs.";
       };
 
@@ -131,17 +134,17 @@ in {
       };
     };
 
-    users.extraUsers = optionalAttrs (cfg.user == "mpd") (singleton {
+    users.extraUsers = optionalAttrs (cfg.user == name) (singleton {
       inherit uid;
-      name = "mpd";
+      inherit name;
       group = cfg.group;
       extraGroups = [ "audio" ];
       description = "Music Player Daemon user";
       home = "${cfg.dataDir}";
     });
 
-    users.extraGroups = optionalAttrs (cfg.group == "mpd") (singleton {
-      name = "mpd";
+    users.extraGroups = optionalAttrs (cfg.group == name) (singleton {
+      inherit name;
       gid = gid;
     });
   };
diff --git a/nixos/modules/services/audio/slimserver.nix b/nixos/modules/services/audio/slimserver.nix
new file mode 100644
index 000000000000..7d661dd60408
--- /dev/null
+++ b/nixos/modules/services/audio/slimserver.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.slimserver;
+
+in {
+  options = {
+
+    services.slimserver = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable slimserver.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.slimserver;
+        defaultText = "pkgs.slimserver";
+        description = "Slimserver package to use.";
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/slimserver";
+        description = ''
+          The directory where slimserver stores its state, tag cache,
+          playlists etc.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.slimserver = {
+      after = [ "network.target" ];
+      description = "Slim Server for Logitech Squeezebox Players";
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = "mkdir -p ${cfg.dataDir} && chown -R slimserver:slimserver ${cfg.dataDir}";
+      serviceConfig = {
+        User = "slimserver";
+        PermissionsStartOnly = true;
+        ExecStart = "${cfg.package}/slimserver.pl --logdir ${cfg.dataDir}/logs --prefsdir ${cfg.dataDir}/prefs --cachedir ${cfg.dataDir}/cache";
+      };
+    };
+
+    users = {
+      users.slimserver = {
+        description = "Slimserver daemon user";
+        home = cfg.dataDir;
+        group = "slimserver";
+      };
+      groups.slimserver = {};
+    };
+  };
+
+}
+
diff --git a/nixos/modules/services/backup/rsnapshot.nix b/nixos/modules/services/backup/rsnapshot.nix
index 16815bcc8605..bb5dcab1dcf2 100644
--- a/nixos/modules/services/backup/rsnapshot.nix
+++ b/nixos/modules/services/backup/rsnapshot.nix
@@ -26,7 +26,6 @@ in
       enableManualRsnapshot = mkOption {
         description = "Whether to enable manual usage of the rsnapshot command with this module.";
         default = true;
-        example = false;
         type = types.bool;
       };
 
diff --git a/nixos/modules/services/backup/sitecopy-backup.nix b/nixos/modules/services/backup/sitecopy-backup.nix
deleted file mode 100644
index 6e4721ded68b..000000000000
--- a/nixos/modules/services/backup/sitecopy-backup.nix
+++ /dev/null
@@ -1,106 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  inherit (pkgs) sitecopy;
-
-  stateDir = "/var/spool/sitecopy";
-
-  sitecopyCron = backup : ''
-    ${if backup ? period then backup.period else config.services.sitecopy.period} root ${sitecopy}/bin/sitecopy --storepath=${stateDir} --rcfile=${stateDir}/${backup.name}.conf --update ${backup.name} >> /var/log/sitecopy.log 2>&1
-  '';
-in
-
-{
-
-  options = {
-
-    services.sitecopy = {
-
-      enable = mkOption {
-        default = false;
-        description = ''
-          Whether to enable <command>sitecopy</command> backups of specified
-          directories.
-        '';
-      };
-
-      period = mkOption {
-        default = "15 04 * * *";
-        description = ''
-          This option defines (in the format used by <command>cron</command>)
-          when the <command>sitecopy</command> backups are to be run.
-          The default is to update at 04:15 (at night) every day.
-        '';
-      };
-
-      backups = mkOption {
-        example = [
-          { name = "test";
-            local = "/tmp/backup";
-            remote = "/staff-groups/ewi/st/strategoxt/backup/test";
-            server = "webdata.tudelft.nl";
-            protocol = "webdav";
-            https = true ;
-            symlinks = "maintain" ;
-          }
-        ];
-        default = [];
-        description = ''
-           List of attribute sets describing the backups.
-
-           Username/password are extracted from
-           <filename>${stateDir}/sitecopy.secrets</filename> at activation
-           time. The secrets file lines should have the following structure:
-           <screen>
-             server username password
-           </screen>
-        '';
-      };
-
-    };
-
-  };
-
-  config = mkIf config.services.sitecopy.enable {
-    environment.systemPackages = [ sitecopy ];
-
-    services.cron.systemCronJobs = map sitecopyCron config.services.sitecopy.backups;
-
-    system.activationScripts.sitecopyBackup = stringAfter [ "stdio" "users" ]
-      ''
-        mkdir -m 0700 -p ${stateDir}
-        chown root ${stateDir}
-        touch ${stateDir}/sitecopy.secrets
-        chown root ${stateDir}/sitecopy.secrets
-
-        ${lib.concatStrings (map ( b: ''
-            unset secrets
-            unset secret
-            secrets=`grep '^${b.server}' ${stateDir}/sitecopy.secrets | head -1`
-            secret=($secrets)
-            cat > ${stateDir}/${b.name}.conf << EOF
-              site ${b.name}
-              server ${b.server}
-              protocol ${b.protocol}
-              username ''${secret[1]}
-              password ''${secret[2]}
-              local ${b.local}
-              remote ${b.remote}
-              symlinks ${b.symlinks}
-              ${if b.https then "http secure" else ""}
-            EOF
-            chmod 0600 ${stateDir}/${b.name}.conf
-            if ! test -e ${stateDir}/${b.name} ; then
-              echo " * Initializing sitecopy '${b.name}'"
-              ${sitecopy}/bin/sitecopy --storepath=${stateDir} --rcfile=${stateDir}/${b.name}.conf --initialize ${b.name}
-            else
-              echo " * Sitecopy '${b.name}' already initialized"
-            fi
-          '' ) config.services.sitecopy.backups
-        )}
-      '';
-  };
-
-}
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 67112343c335..7c9dedb67ad2 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -230,6 +230,14 @@ in
                   Download bandwidth rate limit in bytes.
                 '';
               };
+
+              verbose = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to produce verbose logging output.
+                '';
+              };
             };
           }
         ));
@@ -293,7 +301,10 @@ in
         '';
 
         script =
-          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" -c -f "${name}-$(date +"%Y%m%d%H%M%S")" ${concatStringsSep " " cfg.directories}'';
+          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \
+                        -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
+                        ${optionalString cfg.verbose "-v"} \
+                        ${concatStringsSep " " cfg.directories}'';
           in if (cfg.cachedir != null) then ''
             mkdir -p ${cfg.cachedir}
             chmod 0700 ${cfg.cachedir}
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index 648089f90b7b..35c0308c9dc8 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -20,15 +20,12 @@ in
         description = "ZnapZend - ZFS Backup System";
         after       = [ "zfs.target" ];
 
-        path = with pkgs; [ znapzend zfs mbuffer openssh ];
+        path = with pkgs; [ zfs mbuffer openssh ];
 
-        script = ''
-          znapzend
-        '';
-
-        reload = ''
-          /bin/kill -HUP $MAINPID
-        '';
+        serviceConfig = {
+          ExecStart = "${pkgs.znapzend}/bin/znapzend";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        };
       };
     };
 
diff --git a/nixos/modules/services/cluster/kubernetes.nix b/nixos/modules/services/cluster/kubernetes.nix
index 029b11ad98b7..f58306ab63ed 100644
--- a/nixos/modules/services/cluster/kubernetes.nix
+++ b/nixos/modules/services/cluster/kubernetes.nix
@@ -45,7 +45,7 @@ let
   cniConfig = pkgs.buildEnv {
     name = "kubernetes-cni-config";
     paths = imap (i: entry:
-      pkgs.writeTextDir "${10+i}-${entry.type}.conf" (builtins.toJSON entry)
+      pkgs.writeTextDir "${toString (10+i)}-${entry.type}.conf" (builtins.toJSON entry)
     ) cfg.kubelet.cni.config;
   };
 
@@ -76,6 +76,7 @@ in {
       description = "Kubernetes package to use.";
       type = types.package;
       default = pkgs.kubernetes;
+      defaultText = "pkgs.kubernetes";
     };
 
     verbose = mkOption {
@@ -596,7 +597,7 @@ in {
     (mkIf cfg.kubelet.enable {
       systemd.services.kubelet = {
         description = "Kubernetes Kubelet Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "network.target" "docker.service" "kube-apiserver.service" ];
         path = with pkgs; [ gitMinimal openssh docker utillinux iproute ethtool thin-provisioning-tools iptables ];
         preStart = ''
@@ -605,14 +606,15 @@ in {
           ${concatMapStringsSep "\n" (p: "ln -fs ${p.plugins}/* /opt/cni/bin") cfg.kubelet.cni.packages}
         '';
         serviceConfig = {
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kubelet \
             --pod-manifest-path=${manifests} \
             --kubeconfig=${kubeconfig} \
             --require-kubeconfig \
             --address=${cfg.kubelet.address} \
             --port=${toString cfg.kubelet.port} \
-            --register-node=${if cfg.kubelet.registerNode then "true" else "false"} \
-            --register-schedulable=${if cfg.kubelet.registerSchedulable then "true" else "false"} \
+            --register-node=${boolToString cfg.kubelet.registerNode} \
+            --register-schedulable=${boolToString cfg.kubelet.registerSchedulable} \
             ${optionalString (cfg.kubelet.tlsCertFile != null)
               "--tls-cert-file=${cfg.kubelet.tlsCertFile}"} \
             ${optionalString (cfg.kubelet.tlsKeyFile != null)
@@ -620,7 +622,7 @@ in {
             --healthz-bind-address=${cfg.kubelet.healthz.bind} \
             --healthz-port=${toString cfg.kubelet.healthz.port} \
             --hostname-override=${cfg.kubelet.hostname} \
-            --allow-privileged=${if cfg.kubelet.allowPrivileged then "true" else "false"} \
+            --allow-privileged=${boolToString cfg.kubelet.allowPrivileged} \
             --root-dir=${cfg.dataDir} \
             --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \
             ${optionalString (cfg.kubelet.clusterDns != "")
@@ -654,9 +656,10 @@ in {
     (mkIf cfg.apiserver.enable {
       systemd.services.kube-apiserver = {
         description = "Kubernetes Kubelet Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "network.target" "docker.service" ];
         serviceConfig = {
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kube-apiserver \
             --etcd-servers=${concatStringsSep "," cfg.etcd.servers} \
             ${optionalString (cfg.etcd.caFile != null)
@@ -669,14 +672,14 @@ in {
             --bind-address=0.0.0.0 \
             ${optionalString (cfg.apiserver.advertiseAddress != null)
               "--advertise-address=${cfg.apiserver.advertiseAddress}"} \
-            --allow-privileged=${if cfg.apiserver.allowPrivileged then "true" else "false"} \
+            --allow-privileged=${boolToString cfg.apiserver.allowPrivileged}\
             ${optionalString (cfg.apiserver.tlsCertFile != null)
               "--tls-cert-file=${cfg.apiserver.tlsCertFile}"} \
             ${optionalString (cfg.apiserver.tlsKeyFile != null)
               "--tls-private-key-file=${cfg.apiserver.tlsKeyFile}"} \
             ${optionalString (cfg.apiserver.tokenAuth != null)
               "--token-auth-file=${cfg.apiserver.tokenAuth}"} \
-            --kubelet-https=${if cfg.apiserver.kubeletHttps then "true" else "false"} \
+            --kubelet-https=${boolToString cfg.apiserver.kubeletHttps} \
             ${optionalString (cfg.apiserver.kubeletClientCaFile != null)
               "--kubelet-certificate-authority=${cfg.apiserver.kubeletClientCaFile}"} \
             ${optionalString (cfg.apiserver.kubeletClientCertFile != null)
@@ -712,13 +715,14 @@ in {
     (mkIf cfg.scheduler.enable {
       systemd.services.kube-scheduler = {
         description = "Kubernetes Scheduler Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "kube-apiserver.service" ];
         serviceConfig = {
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kube-scheduler \
             --address=${cfg.scheduler.address} \
             --port=${toString cfg.scheduler.port} \
-            --leader-elect=${if cfg.scheduler.leaderElect then "true" else "false"} \
+            --leader-elect=${boolToString cfg.scheduler.leaderElect} \
             --kubeconfig=${kubeconfig} \
             ${optionalString cfg.verbose "--v=6"} \
             ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
@@ -734,16 +738,17 @@ in {
     (mkIf cfg.controllerManager.enable {
       systemd.services.kube-controller-manager = {
         description = "Kubernetes Controller Manager Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "kube-apiserver.service" ];
         serviceConfig = {
           RestartSec = "30s";
           Restart = "on-failure";
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kube-controller-manager \
             --address=${cfg.controllerManager.address} \
             --port=${toString cfg.controllerManager.port} \
             --kubeconfig=${kubeconfig} \
-            --leader-elect=${if cfg.controllerManager.leaderElect then "true" else "false"} \
+            --leader-elect=${boolToString cfg.controllerManager.leaderElect} \
             ${if (cfg.controllerManager.serviceAccountKeyFile!=null)
               then "--service-account-private-key-file=${cfg.controllerManager.serviceAccountKeyFile}"
               else "--service-account-private-key-file=/var/run/kubernetes/apiserver.key"} \
@@ -766,16 +771,17 @@ in {
     (mkIf cfg.proxy.enable {
       systemd.services.kube-proxy = {
         description = "Kubernetes Proxy Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "kube-apiserver.service" ];
         path = [pkgs.iptables];
         serviceConfig = {
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kube-proxy \
             --kubeconfig=${kubeconfig} \
             --bind-address=${cfg.proxy.address} \
             ${optionalString cfg.verbose "--v=6"} \
             ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
-            ${cfg.controllerManager.extraOpts}
+            ${cfg.proxy.extraOpts}
           '';
           WorkingDirectory = cfg.dataDir;
         };
@@ -785,9 +791,10 @@ in {
     (mkIf cfg.dns.enable {
       systemd.services.kube-dns = {
         description = "Kubernetes Dns Service";
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "kubernetes.target" ];
         after = [ "kube-apiserver.service" ];
         serviceConfig = {
+          Slice = "kubernetes.slice";
           ExecStart = ''${cfg.package}/bin/kube-dns \
             --kubecfg-file=${kubeconfig} \
             --dns-port=${toString cfg.dns.port} \
@@ -835,6 +842,11 @@ in {
         cfg.proxy.enable ||
         cfg.dns.enable
     ) {
+      systemd.targets.kubernetes = {
+        description = "Kubernetes";
+        wantedBy = [ "multi-user.target" ];
+      };
+
       systemd.tmpfiles.rules = [
         "d /opt/cni/bin 0755 root root -"
         "d /var/run/kubernetes 0755 kubernetes kubernetes -"
diff --git a/nixos/modules/services/computing/boinc/client.nix b/nixos/modules/services/computing/boinc/client.nix
index 91bd463732de..e43b6bbb2536 100644
--- a/nixos/modules/services/computing/boinc/client.nix
+++ b/nixos/modules/services/computing/boinc/client.nix
@@ -12,7 +12,6 @@ in
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Whether to enable the BOINC distributed computing client. If this
           option is set to true, the boinc_client daemon will be run as a
@@ -41,7 +40,6 @@ in
       allowRemoteGuiRpc = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           If set to true, any remote host can connect to and control this BOINC
           client (subject to password authentication). If instead set to false,
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index a40be4f546ea..533751734fa5 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -7,7 +7,7 @@ with lib;
 let
   cfg = config.services.buildbot-master;
   escapeStr = s: escape ["'"] s;
-  masterCfg = pkgs.writeText "master.cfg" ''
+  masterCfg = if cfg.masterCfg == null then pkgs.writeText "master.cfg" ''
     from buildbot.plugins import *
     factory = util.BuildFactory()
     c = BuildmasterConfig = dict(
@@ -27,9 +27,8 @@ let
       factory.addStep(step)
 
     ${cfg.extraConfig}
-  '';
-
-  configFile = if cfg.masterCfg == null then masterCfg else cfg.masterCfg;
+  ''
+  else cfg.masterCfg;
 
 in {
   options = {
@@ -67,15 +66,10 @@ in {
       };
 
       masterCfg = mkOption {
-        type = with types; nullOr path;
-        description = ''
-          Optionally pass path to raw master.cfg file.
-          Other options in this configuration will be ignored.
-        '';
+        type = types.nullOr types.path;
+        description = "Optionally pass master.cfg path. Other options in this configuration will be ignored.";
         default = null;
-        example = literalExample ''
-          pkgs.writeText "master.cfg" "BuildmasterConfig = c = {}"
-        '';
+        example = "/etc/nixos/buildbot/master.cfg";
       };
 
       schedulers = mkOption {
@@ -91,7 +85,7 @@ in {
         type = types.listOf types.str;
         description = "List of Builders.";
         default = [
-          "util.BuilderConfig(name='runtests',workernames=['default-worker'],factory=factory)"
+          "util.BuilderConfig(name='runtests',workernames=['example-worker'],factory=factory)"
         ];
       };
 
@@ -99,9 +93,9 @@ in {
         type = types.listOf types.str;
         description = "List of Workers.";
         default = [
-          "worker.Worker('default-worker', 'password')"
+          "worker.Worker('example-worker', 'pass')"
         ];
-        example = [ "worker.LocalWorker('default-worker')" ];
+        example = [ "worker.LocalWorker('example-worker')" ];
       };
 
       status = mkOption {
@@ -124,7 +118,7 @@ in {
 
       extraGroups = mkOption {
         type = types.listOf types.str;
-        default = [ "nixbld" ];
+        default = [];
         description = "List of extra groups that the buildbot user should be a part of.";
       };
 
@@ -186,16 +180,14 @@ in {
       package = mkOption {
         type = types.package;
         default = pkgs.buildbot-ui;
-        description = ''
-          Package to use for buildbot.
-          <literal>buildbot-full</literal> is required in order to use local workers.
-        '';
-        example = pkgs.buildbot-full;
+        defaultText = "pkgs.buildbot-ui";
+        description = "Package to use for buildbot.";
+        example = literalExample "pkgs.buildbot-full";
       };
 
       packages = mkOption {
         default = [ ];
-        example = [ pkgs.git ];
+        example = literalExample "[ pkgs.git ]";
         type = types.listOf types.package;
         description = "Packages to add to PATH for the buildbot process.";
       };
@@ -209,7 +201,7 @@ in {
 
     users.extraUsers = optional (cfg.user == "buildbot") {
       name = "buildbot";
-      description = "buildbot user";
+      description = "Buildbot User.";
       isNormalUser = true;
       createHome = true;
       home = cfg.home;
@@ -219,23 +211,22 @@ in {
     };
 
     systemd.services.buildbot-master = {
-      description = "Buildbot Continuous Integration Server";
+      description = "Buildbot Continuous Integration Server.";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = cfg.packages;
 
       serviceConfig = {
-        Type = "forking";
+        Type = "simple";
         User = cfg.user;
         Group = cfg.group;
         WorkingDirectory = cfg.home;
-        ExecStart = "${cfg.package}/bin/buildbot start ${cfg.buildbotDir}";
+        ExecStart = "${cfg.package}/bin/buildbot start --nodaemon ${cfg.buildbotDir}";
       };
 
       preStart = ''
-        mkdir -vp ${cfg.buildbotDir}
-        chown -c ${cfg.user}:${cfg.group} ${cfg.buildbotDir}
-        ln -sf ${configFile} ${cfg.buildbotDir}/master.cfg
+        ${pkgs.coreutils}/bin/mkdir -vp ${cfg.buildbotDir}
+        ${pkgs.coreutils}/bin/ln -sfv ${masterCfg} ${cfg.buildbotDir}/master.cfg
         ${cfg.package}/bin/buildbot create-master ${cfg.buildbotDir}
       '';
 
@@ -247,4 +238,6 @@ in {
     };
   };
 
+  meta.maintainers = with lib.maintainers; [ nand0p Mic92 ];
+
 }
diff --git a/nixos/modules/services/continuous-integration/buildbot/worker.nix b/nixos/modules/services/continuous-integration/buildbot/worker.nix
new file mode 100644
index 000000000000..e4ee4dd861ef
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/buildbot/worker.nix
@@ -0,0 +1,126 @@
+# NixOS module for Buildbot Worker.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.buildbot-worker;
+
+in {
+  options = {
+    services.buildbot-worker = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Buildbot Worker.";
+      };
+
+      user = mkOption {
+        default = "bbworker";
+        type = types.str;
+        description = "User the buildbot Worker should execute under.";
+      };
+
+      group = mkOption {
+        default = "bbworker";
+        type = types.str;
+        description = "Primary group of buildbot Worker user.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "List of extra groups that the Buildbot Worker user should be a part of.";
+      };
+
+      home = mkOption {
+        default = "/home/bbworker";
+        type = types.path;
+        description = "Buildbot home directory.";
+      };
+
+      buildbotDir = mkOption {
+        default = "${cfg.home}/worker";
+        type = types.path;
+        description = "Specifies the Buildbot directory.";
+      };
+
+      workerUser = mkOption {
+        default = "example-worker";
+        type = types.str;
+        description = "Specifies the Buildbot Worker user.";
+      };
+
+      workerPass = mkOption {
+        default = "pass";
+        type = types.str;
+        description = "Specifies the Buildbot Worker password.";
+      };
+
+      masterUrl = mkOption {
+        default = "localhost:9989";
+        type = types.str;
+        description = "Specifies the Buildbot Worker connection string.";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.buildbot-worker;
+        defaultText = "pkgs.buildbot-worker";
+        description = "Package to use for buildbot worker.";
+        example = literalExample "pkgs.buildbot-worker";
+      };
+
+      packages = mkOption {
+        default = [ ];
+        example = literalExample "[ pkgs.git ]";
+        type = types.listOf types.package;
+        description = "Packages to add to PATH for the buildbot process.";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups = optional (cfg.group == "bbworker") {
+      name = "bbworker";
+    };
+
+    users.extraUsers = optional (cfg.user == "bbworker") {
+      name = "bbworker";
+      description = "Buildbot Worker User.";
+      isNormalUser = true;
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      extraGroups = cfg.extraGroups;
+      useDefaultShell = true;
+    };
+
+    systemd.services.buildbot-worker = {
+      description = "Buildbot Worker.";
+      after = [ "network.target" "buildbot-master.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = cfg.packages;
+
+      preStart = ''
+        ${pkgs.coreutils}/bin/mkdir -vp ${cfg.buildbotDir}
+        ${cfg.package}/bin/buildbot-worker create-worker ${cfg.buildbotDir} ${cfg.masterUrl} ${cfg.workerUser} ${cfg.workerPass}
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = cfg.home;
+        ExecStart = "${cfg.package}/bin/buildbot-worker start --nodaemon ${cfg.buildbotDir}";
+      };
+
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ nand0p ];
+
+}
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 1fe4d28f9f35..ba90b1b1a2c0 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -20,6 +20,14 @@ in
       description = "The working directory used";
     };
 
+    package = mkOption {
+      description = "Gitlab Runner package to use";
+      default = pkgs.gitlab-runner;
+      defaultText = "pkgs.gitlab-runner";
+      type = types.package;
+      example = literalExample "pkgs.gitlab-runner_1_11";
+    };
+
   };
 
   config = mkIf cfg.enable {
@@ -29,7 +37,7 @@ in
       requires = [ "docker.service" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = ''${pkgs.gitlab-runner.bin}/bin/gitlab-runner run \
+        ExecStart = ''${cfg.package.bin}/bin/gitlab-runner run \
           --working-directory ${cfg.workDir} \
           --config ${configFile} \
           --service gitlab-runner \
@@ -38,6 +46,9 @@ in
       };
     };
 
+    # Make the gitlab-runner command availabe so users can query the runner
+    environment.systemPackages = [ cfg.package ];
+
     users.extraUsers.gitlab-runner = {
       group = "gitlab-runner";
       extraGroups = [ "docker" ];
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index fa550f68b338..c515622d11a0 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -233,6 +233,7 @@ in
           hydra_logo ${cfg.logo}
         ''}
         gc_roots_dir ${cfg.gcRootsDir}
+        use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
       '';
 
     environment.systemPackages = [ cfg.package ];
@@ -328,7 +329,7 @@ in
           IN_SYSTEMD = "1"; # to get log severity levels
         };
         serviceConfig =
-          { ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner -v --option build-use-substitutes ${if cfg.useSubstitutes then "true" else "false"}";
+          { ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner -v --option build-use-substitutes ${boolToString cfg.useSubstitutes}";
             ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock";
             User = "hydra-queue-runner";
             Restart = "always";
diff --git a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
index 7b1fe6269fe9..861b46a2d642 100644
--- a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
@@ -29,6 +29,22 @@ in {
         '';
       };
 
+      accessUser = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          User id in Jenkins used to reload config.
+        '';
+      };
+
+      accessToken = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          User token in Jenkins used to reload config.
+        '';
+      };
+
       yamlJobs = mkOption {
         default = "";
         type = types.lines;
@@ -110,6 +126,11 @@ in {
           # Stamp file is placed in $JENKINS_HOME/jobs/$JOB_NAME/ to indicate
           # ownership. Enables tracking and removal of stale jobs.
           ownerStamp = ".config-xml-managed-by-nixos-jenkins-job-builder";
+          reloadScript = ''
+            echo "Asking Jenkins to reload config"
+            CRUMB=$(curl -s 'http://${cfg.accessUser}:${cfg.accessToken}@${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
+            curl --silent -X POST -H "$CRUMB" http://${cfg.accessUser}:${cfg.accessToken}@${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}/reload
+          '';
         in
           ''
             rm -rf ${jobBuilderOutputDir}
@@ -142,10 +163,7 @@ in {
                 echo "Deleting stale job \"$jobname\""
                 rm -rf "$jobdir"
             done
-
-            echo "Asking Jenkins to reload config"
-            curl --silent -X POST http://${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}/reload
-          '';
+          '' + (if cfg.accessUser != "" then reloadScript else "");
       serviceConfig = {
         User = jenkinsCfg.user;
         RuntimeDirectory = "jenkins-job-builder";
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index b43b448ed7e1..1e5cd8f54130 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -21,8 +21,8 @@ let
   cassandraConf = ''
     cluster_name: ${cfg.clusterName}
     num_tokens: 256
-    auto_bootstrap: ${if cfg.autoBootstrap then "true" else "false"}
-    hinted_handoff_enabled: ${if cfg.hintedHandOff then "true" else "false"}
+    auto_bootstrap: ${boolToString cfg.autoBootstrap}
+    hinted_handoff_enabled: ${boolToString cfg.hintedHandOff}
     hinted_handoff_throttle_in_kb: ${builtins.toString cfg.hintedHandOffThrottle}
     max_hints_delivery_threads: 2
     max_hint_window_in_ms: 10800000 # 3 hours
@@ -62,7 +62,7 @@ let
     rpc_keepalive: true
     rpc_server_type: sync
     thrift_framed_transport_size_in_mb: 15
-    incremental_backups: ${if cfg.incrementalBackups then "true" else "false"}
+    incremental_backups: ${boolToString cfg.incrementalBackups}
     snapshot_before_compaction: false
     auto_snapshot: true
     column_index_size_in_kb: 64
@@ -89,7 +89,7 @@ let
       truststore: ${cfg.trustStorePath}
       truststore_password: ${cfg.trustStorePassword}
     client_encryption_options:
-      enabled: ${if cfg.clientEncryption then "true" else "false"}
+      enabled: ${boolToString cfg.clientEncryption}
       keystore: ${cfg.keyStorePath}
       keystore_password: ${cfg.keyStorePassword}
     internode_compression: all
@@ -310,7 +310,6 @@ in {
     autoBootstrap = mkOption {
       description = "It makes new (non-seed) nodes automatically migrate the right data to themselves.";
       default = true;
-      example = true;
       type = types.bool;
     };
     streamingSocketTimoutInMS = mkOption {
diff --git a/nixos/modules/services/databases/clickhouse.nix b/nixos/modules/services/databases/clickhouse.nix
new file mode 100644
index 000000000000..631d7f8cba79
--- /dev/null
+++ b/nixos/modules/services/databases/clickhouse.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.clickhouse;
+  confDir = "/etc/clickhouse-server";
+  stateDir = "/var/lib/clickhouse";
+in
+with lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.clickhouse = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable ClickHouse database server.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers.clickhouse = {
+      name = "clickhouse";
+      uid = config.ids.uids.clickhouse;
+      group = "clickhouse";
+      description = "ClickHouse server user";
+    };
+
+    users.extraGroups.clickhouse.gid = config.ids.gids.clickhouse;
+
+    systemd.services.clickhouse = {
+      description = "ClickHouse server";
+
+      wantedBy = [ "multi-user.target" ];
+
+      after = [ "network.target" ];
+
+      preStart = ''
+        mkdir -p ${stateDir}
+        chown clickhouse:clickhouse ${confDir} ${stateDir}
+      '';
+
+      script = ''
+        cd "${confDir}"
+        exec ${pkgs.clickhouse}/bin/clickhouse-server
+      '';
+
+      serviceConfig = {
+        User = "clickhouse";
+        Group = "clickhouse";
+        PermissionsStartOnly = true;
+      };
+    };
+
+    environment.etc = {
+      "clickhouse-server/config.xml" = {
+        source = "${pkgs.clickhouse}/etc/clickhouse-server/config.xml";
+      };
+
+      "clickhouse-server/users.xml" = {
+        source = "${pkgs.clickhouse}/etc/clickhouse-server/users.xml";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix
index d4d231456c52..52247bfb983e 100644
--- a/nixos/modules/services/databases/couchdb.nix
+++ b/nixos/modules/services/databases/couchdb.nix
@@ -4,20 +4,29 @@ with lib;
 
 let
   cfg = config.services.couchdb;
-  configFile = pkgs.writeText "couchdb.ini"
+  useVersion2 = strings.versionAtLeast (strings.getVersion cfg.package) "2.0";
+  configFile = pkgs.writeText "couchdb.ini" (
     ''
       [couchdb]
       database_dir = ${cfg.databaseDir}
       uri_file = ${cfg.uriFile}
       view_index_dir = ${cfg.viewIndexDir}
-
+    '' + (if useVersion2 then
+    ''
+      [chttpd]
+    '' else
+    ''
       [httpd]
+    '') +
+    ''
       port = ${toString cfg.port}
       bind_address = ${cfg.bindAddress}
 
       [log]
       file = ${cfg.logFile}
-    '';
+    '');
+  executable = if useVersion2 then "${cfg.package}/bin/couchdb"
+    else ''${cfg.package}/bin/couchdb -a ${configFile} -a ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig} -a ${cfg.configFile}'';
 
 in {
 
@@ -130,7 +139,6 @@ in {
 
       configFile = mkOption {
         type = types.string;
-        default = "/var/lib/couchdb/couchdb.ini";
         description = ''
           Configuration file for persisting runtime changes. File
           needs to be readable and writable from couchdb user/group.
@@ -147,6 +155,9 @@ in {
 
     environment.systemPackages = [ cfg.package ];
 
+    services.couchdb.configFile = mkDefault
+      (if useVersion2 then "/var/lib/couchdb/local.ini" else "/var/lib/couchdb/couchdb.ini");
+
     systemd.services.couchdb = {
       description = "CouchDB Server";
       wantedBy = [ "multi-user.target" ];
@@ -170,11 +181,20 @@ in {
         fi
         '';
 
+      environment = mkIf useVersion2 {
+        # we are actually specifying 4 configuration files:
+        # 1. the preinstalled default.ini
+        # 2. the module configuration
+        # 3. the extraConfig from the module options
+        # 4. the locally writable config file, which couchdb itself writes to
+        ERL_FLAGS= ''-couch_ini ${cfg.package}/etc/default.ini ${configFile} ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig} ${cfg.configFile}'';
+      };
+
       serviceConfig = {
         PermissionsStartOnly = true;
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${cfg.package}/bin/couchdb -a ${configFile} -a ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig} -a ${cfg.configFile}";
+        ExecStart = executable;
       };
     };
 
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
index 38e46a0c6ef9..c56564f57f36 100644
--- a/nixos/modules/services/databases/mongodb.nix
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -4,8 +4,6 @@ with lib;
 
 let
 
-  b2s = x: if x then "true" else "false";
-
   cfg = config.services.mongodb;
 
   mongodb = cfg.package;
diff --git a/nixos/modules/services/databases/neo4j.nix b/nixos/modules/services/databases/neo4j.nix
index 7b51f1af6899..424e08a6ee34 100644
--- a/nixos/modules/services/databases/neo4j.nix
+++ b/nixos/modules/services/databases/neo4j.nix
@@ -27,12 +27,17 @@ let
     ''}
     dbms.shell.enabled=true
     ${cfg.extraServerConfig}
-  '';
 
-  wrapperConfig = pkgs.writeText "neo4j-wrapper.conf" ''
+    # Default JVM parameters from neo4j.conf
+    dbms.jvm.additional=-XX:+UseG1GC
+    dbms.jvm.additional=-XX:-OmitStackTraceInFastThrow
+    dbms.jvm.additional=-XX:+AlwaysPreTouch
+    dbms.jvm.additional=-XX:+UnlockExperimentalVMOptions
+    dbms.jvm.additional=-XX:+TrustFinalNonStaticFields
+    dbms.jvm.additional=-XX:+DisableExplicitGC
+    dbms.jvm.additional=-Djdk.tls.ephemeralDHKeySize=2048
+
     dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
-    dbms.jvm.additional=-XX:+UseConcMarkSweepGC
-    dbms.jvm.additional=-XX:+CMSClassUnloadingEnabled
   '';
 
 in {
@@ -123,16 +128,16 @@ in {
         ExecStart = "${cfg.package}/bin/neo4j console";
         User = "neo4j";
         PermissionsStartOnly = true;
+        LimitNOFILE = 40000;
       };
       preStart = ''
         mkdir -m 0700 -p ${cfg.dataDir}/{data/graph.db,conf,logs}
         ln -fs ${serverConfig} ${cfg.dataDir}/conf/neo4j.conf
-        ln -fs ${wrapperConfig} ${cfg.dataDir}/conf/neo4j-wrapper.conf
         if [ "$(id -u)" = 0 ]; then chown -R neo4j ${cfg.dataDir}; fi
       '';
     };
 
-    environment.systemPackages = [ pkgs.neo4j ];
+    environment.systemPackages = [ cfg.package ];
 
     users.extraUsers = singleton {
       name = "neo4j";
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index b8e6c0cec3dc..e884098cb08d 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -25,7 +25,6 @@ in
         description = "
           Whether to enable the ldap server.
         ";
-        example = true;
       };
 
       user = mkOption {
@@ -68,10 +67,10 @@ in
         ";
         example = literalExample ''
             '''
-            include ${pkgs.openldap.out}/etc/openldap/schema/core.schema
-            include ${pkgs.openldap.out}/etc/openldap/schema/cosine.schema
-            include ${pkgs.openldap.out}/etc/openldap/schema/inetorgperson.schema
-            include ${pkgs.openldap.out}/etc/openldap/schema/nis.schema
+            include ${pkgs.openldap.out}/etc/schema/core.schema
+            include ${pkgs.openldap.out}/etc/schema/cosine.schema
+            include ${pkgs.openldap.out}/etc/schema/inetorgperson.schema
+            include ${pkgs.openldap.out}/etc/schema/nis.schema
 
             database bdb 
             suffix dc=example,dc=org 
diff --git a/nixos/modules/services/databases/stanchion.nix b/nixos/modules/services/databases/stanchion.nix
index f2dbb78b5c4b..a4597cac3cd6 100644
--- a/nixos/modules/services/databases/stanchion.nix
+++ b/nixos/modules/services/databases/stanchion.nix
@@ -76,14 +76,6 @@ in
         '';
       };
 
-      stanchionSsl = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Tell stanchion to use SSL.
-        '';
-      };
-
       distributedCookie = mkOption {
         type = types.str;
         default = "riak";
@@ -148,8 +140,6 @@ in
 
       distributed_cookie = ${cfg.distributedCookie}
 
-      stanchion_ssl=${if cfg.stanchionSsl then "on" else "off"}
-
       ${cfg.extraConfig}
     '';
 
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 08fa6de6374c..2c5a0c4849ef 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -21,7 +21,6 @@ in {
     enable = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Whether to enable a user service for the Emacs daemon. Use <literal>emacsclient</literal> to connect to the
         daemon. If <literal>true</literal>, <varname>services.emacs.install</varname> is
@@ -32,7 +31,6 @@ in {
     install = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Whether to install a user service for the Emacs daemon. Once
         the service is started, use emacsclient to connect to the
@@ -57,7 +55,6 @@ in {
     defaultEditor = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         When enabled, configures emacsclient to be the default editor
         using the EDITOR environment variable.
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index e03f6046de8e..9d6395ebd74c 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -79,10 +79,11 @@
             </listitem>
           </varlistentry>
           <varlistentry>
-            <term><varname>emacs24Macport</varname></term>
+            <term><varname>emacsMacport</varname></term>
+            <term><varname>emacs25Macport</varname></term>
             <listitem>
               <para>
-                Emacs 24 with the "Mac port" patches, providing a more
+                Emacs 25 with the "Mac port" patches, providing a more
                 native look and feel under OS X.
               </para>
             </listitem>
@@ -316,10 +317,10 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
       <para>
         If you are not on NixOS or want to install this particular
         Emacs only for yourself, you can do so by adding it to your
-        <filename>~/.nixpkgs/config.nix</filename>
+        <filename>~/.config/nixpkgs/config.nix</filename>
         (see <link xlink:href="http://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides">Nixpkgs manual</link>):
         <example>
-          <title>Custom Emacs in <filename>~/.nixpkgs/system.nix</filename></title>
+          <title>Custom Emacs in <filename>~/.config/nixpkgs/config.nix</filename></title>
           <programlisting><![CDATA[
 {
   packageOverrides = super: let self = super.pkgs; in {
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 0369752997a7..e7f070d08773 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -14,6 +14,31 @@ let
     read-data=${factorio}/share/factorio/data
     write-data=${stateDir}
   '';
+  serverSettings = {
+    name = cfg.game-name;
+    description = cfg.description;
+    visibility = {
+      public = cfg.public;
+      lan = cfg.lan;
+    };
+    username = cfg.username;
+    password = cfg.password;
+    token = cfg.token;
+    game_password = cfg.game-password;
+    require_user_verification = true;
+    max_upload_in_kilobytes_per_second = 0;
+    minimum_latency_in_ticks = 0;
+    ignore_player_limit_for_returning_players = false;
+    allow_commands = "admins-only";
+    autosave_interval = cfg.autosave-interval;
+    autosave_slots = 5;
+    afk_autokick_interval = 0;
+    auto_pause = true;
+    only_admins_can_pause_the_game = true;
+    autosave_only_on_server = true;
+    admins = [];
+  };
+  serverSettingsFile = pkgs.writeText "server-settings.json" (builtins.toJSON (filterAttrsRecursive (n: v: v != null) serverSettings));
   modDir = pkgs.factorio-mkModDirDrv cfg.mods;
 in
 {
@@ -67,12 +92,68 @@ in
           derivations via nixos-channel. Until then, this is for experts only.
         '';
       };
+      game-name = mkOption {
+        type = types.nullOr types.string;
+        default = "Factorio Game";
+        description = ''
+          Name of the game as it will appear in the game listing.
+        '';
+      };
+      description = mkOption {
+        type = types.nullOr types.string;
+        default = "";
+        description = ''
+          Description of the game that will appear in the listing.
+        '';
+      };
+      public = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Game will be published on the official Factorio matching server.
+        '';
+      };
+      lan = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Game will be broadcast on LAN.
+        '';
+      };
+      username = mkOption {
+        type = types.nullOr types.string;
+        default = null;
+        description = ''
+          Your factorio.com login credentials. Required for games with visibility public.
+        '';
+      };
+      password = mkOption {
+        type = types.nullOr types.string;
+        default = null;
+        description = ''
+          Your factorio.com login credentials. Required for games with visibility public.
+        '';
+      };
+      token = mkOption {
+        type = types.nullOr types.string;
+        default = null;
+        description = ''
+          Authentication token. May be used instead of 'password' above.
+        '';
+      };
+      game-password = mkOption {
+        type = types.nullOr types.string;
+        default = null;
+        description = ''
+          Game password.
+        '';
+      };
       autosave-interval = mkOption {
         type = types.nullOr types.int;
         default = null;
-        example = 2;
+        example = 10;
         description = ''
-          The time, in minutes, between autosaves.
+          Autosave interval in minutes.
         '';
       };
     };
@@ -120,8 +201,8 @@ in
           "--config=${cfg.configFile}"
           "--port=${toString cfg.port}"
           "--start-server=${mkSavePath cfg.saveName}"
+          "--server-settings=${serverSettingsFile}"
           (optionalString (cfg.mods != []) "--mod-directory=${modDir}")
-          (optionalString (cfg.autosave-interval != null) "--autosave-interval ${toString cfg.autosave-interval}")
         ];
       };
     };
diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix
index 087bd0e04098..b0f9ff56d1b2 100644
--- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix
+++ b/nixos/modules/services/hardware/amd-hybrid-graphics.nix
@@ -25,15 +25,22 @@
       path = [ pkgs.bash ];
       description = "Disable AMD Card";
       after = [ "sys-kernel-debug.mount" ];
-      requires = [ "sys-kernel-debug.mount" ];
-      wantedBy = [ "multi-user.target" ];
+      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; exit 0'";
-        ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch; exit 0'";
+        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 2c271b328179..4a8cd86b0b11 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -1,67 +1,73 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+
 let
-    bluez-bluetooth = if config.services.xserver.desktopManager.kde4.enable then pkgs.bluez else pkgs.bluez5;
-
-    configBluez = {
-        description = "Bluetooth Service";
-        serviceConfig = {
-          Type = "dbus";
-          BusName = "org.bluez";
-          ExecStart = "${getBin bluez-bluetooth}/bin/bluetoothd -n";
-        };
-        wantedBy = [ "bluetooth.target" ];
-    };
+  bluez-bluetooth = pkgs.bluez;
+  cfg = config.hardware.bluetooth;
 
-    configBluez5 =  {
-        description = "Bluetooth Service";
-        serviceConfig = {
-          Type = "dbus";
-          BusName = "org.bluez";
-          ExecStart = "${getBin bluez-bluetooth}/bin/bluetoothd -n";
-          NotifyAccess="main";
-          CapabilityBoundingSet="CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
-          LimitNPROC=1;
-        };
-        wantedBy = [ "bluetooth.target" ];
-    };
+in {
 
-    obexConfig = {
-        description = "Bluetooth OBEX service";
-        serviceConfig = {
-          Type = "dbus";
-          BusName = "org.bluez.obex";
-          ExecStart = "${getBin bluez-bluetooth}/bin/obexd";
-        };
-    };
+  ###### interface
 
-    bluezConfig = if config.services.xserver.desktopManager.kde4.enable then configBluez else configBluez5;
-in
+  options = {
 
-{
+    hardware.bluetooth = {
+      enable = mkEnableOption "support for Bluetooth.";
 
-  ###### interface
+      powerOnBoot = mkOption {
+        type    = types.bool;
+        default = true;
+        description = "Whether to power up the default Bluetooth controller on boot.";
+      };
 
-  options = {
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          [General]
+          ControllerMode = bredr
+        '';
+        description = ''
+          Set additional configuration for system-wide bluetooth (/etc/bluetooth/main.conf).
 
-    hardware.bluetooth.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Whether to enable support for Bluetooth.";
+          NOTE: We already include [Policy], so any configuration under the Policy group should come first.
+        '';
+      };
     };
 
   };
 
   ###### implementation
-  
-  config = mkIf config.hardware.bluetooth.enable {
+
+  config = mkIf cfg.enable {
 
     environment.systemPackages = [ bluez-bluetooth pkgs.openobex pkgs.obexftp ];
+
+    environment.etc = singleton {
+      source = pkgs.writeText "main.conf" ''
+        [Policy]
+        AutoEnable=${lib.boolToString cfg.powerOnBoot}
+
+        ${cfg.extraConfig}
+      '';
+      target = "bluetooth/main.conf";
+    };
+
     services.udev.packages = [ bluez-bluetooth ];
     services.dbus.packages = [ bluez-bluetooth ];
-    systemd.services."dbus-org.bluez" = bluezConfig;
-    systemd.services."dbus-org.bluez.obex" = obexConfig;
+    systemd.packages       = [ bluez-bluetooth ];
+
+    systemd.services = {
+      bluetooth = {
+        wantedBy = [ "bluetooth.target" ];
+        aliases  = [ "dbus-org.bluez.service" ];
+      };
+    };
+
+    systemd.user.services = {
+      obex.aliases = [ "dbus-org.bluez.obex.service" ];
+    };
 
   };
 
diff --git a/nixos/modules/services/hardware/illum.nix b/nixos/modules/services/hardware/illum.nix
new file mode 100644
index 000000000000..ff73c99a6537
--- /dev/null
+++ b/nixos/modules/services/hardware/illum.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.illum;
+in {
+
+  options = {
+
+    services.illum = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable illum, a daemon for controlling screen brightness with brightness buttons.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.illum = {
+      description = "Backlight Adjustment Service";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.illum}/bin/illum-d";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
index 8ddb9ef9c53b..d651ccaa5776 100644
--- a/nixos/modules/services/hardware/sane.nix
+++ b/nixos/modules/services/hardware/sane.nix
@@ -51,7 +51,7 @@ in
         Enable support for SANE scanners.
 
         <note><para>
-          Users in the "scanner" group will gain access to the scanner.
+          Users in the "scanner" group will gain access to the scanner, or the "lp" group if it's also a printer.
         </para></note>
       '';
     };
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index f36a9e7b4596..3b108c87edd2 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -58,6 +58,9 @@ in
     powerManagement.cpuFreqGovernor = null;
 
     systemd.services = {
+      "systemd-rfkill@".enable = false;
+      "systemd-rfkill".enable = false;
+
       tlp = {
         description = "TLP system startup/shutdown";
 
diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix
new file mode 100644
index 000000000000..38d0a3a1d752
--- /dev/null
+++ b/nixos/modules/services/hardware/trezord.nix
@@ -0,0 +1,54 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.trezord;
+in {
+  
+  ### interface
+
+  options = {
+    services.trezord = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Trezor bridge daemon, for use with Trezor hardware bitcoin wallets.
+        '';
+      };
+    };
+  };
+  
+  ### implementation
+
+  config = mkIf cfg.enable {
+    services.udev.packages = lib.singleton (pkgs.writeTextFile {
+      name = "trezord-udev-rules";
+      destination = "/etc/udev/rules.d/51-trezor.rules";
+      text = ''
+        SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0666", GROUP="dialout", SYMLINK+="trezor%n"
+        KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001",  MODE="0666", GROUP="dialout"
+      '';
+    });
+
+    systemd.services.trezord = {
+      description = "TREZOR Bridge";
+      after = [ "systemd-udev-settle.service" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [];
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.trezord}/bin/trezord -f";
+        User = "trezord";
+      };
+    };
+
+    users.users.trezord = {
+      group = "trezord";
+      description = "Trezor bridge daemon user";
+    };
+
+    users.groups.trezord = {};
+  };
+}
+
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 14d65978c320..9f42f9e59ad5 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -35,6 +35,7 @@ let
   udevRules = pkgs.runCommand "udev-rules"
     { preferLocalBuild = true;
       allowSubstitutes = false;
+      packages = unique (map toString cfg.packages);
     }
     ''
       mkdir -p $out
@@ -45,7 +46,7 @@ let
       echo 'ENV{PATH}="${udevPath}/bin:${udevPath}/sbin"' > $out/00-path.rules
 
       # Add the udev rules from other packages.
-      for i in ${toString cfg.packages}; do
+      for i in $packages; do
         echo "Adding rules for package $i"
         for j in $i/{etc,lib}/udev/rules.d/*; do
           echo "Copying $j to $out/$(basename $j)"
@@ -132,10 +133,11 @@ let
   hwdbBin = pkgs.runCommand "hwdb.bin"
     { preferLocalBuild = true;
       allowSubstitutes = false;
+      packages = unique (map toString ([udev] ++ cfg.packages));
     }
     ''
       mkdir -p etc/udev/hwdb.d
-      for i in ${toString ([udev] ++ cfg.packages)}; do
+      for i in $packages; do
         echo "Adding hwdb files for package $i"
         for j in $i/{etc,lib}/udev/hwdb.d/*; do
           ln -s $j etc/udev/hwdb.d/$(basename $j)
@@ -143,7 +145,10 @@ let
       done
 
       echo "Generating hwdb database..."
-      ${udev}/bin/udevadm hwdb --update --root=$(pwd)
+      # hwdb --update doesn't return error code even on errors!
+      res="$(${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/logging/awstats.nix b/nixos/modules/services/logging/awstats.nix
index 8ab7e6acd98e..612ae06d0a79 100644
--- a/nixos/modules/services/logging/awstats.nix
+++ b/nixos/modules/services/logging/awstats.nix
@@ -38,7 +38,7 @@ in
         Specification of the time at which awstats will get updated.
         (in the format described by <citerefentry>
           <refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>)
+          <manvolnum>7</manvolnum></citerefentry>)
       '';
     };
 
diff --git a/nixos/modules/services/logging/fluentd.nix b/nixos/modules/services/logging/fluentd.nix
index 3aa27a152669..9fbec2457371 100644
--- a/nixos/modules/services/logging/fluentd.nix
+++ b/nixos/modules/services/logging/fluentd.nix
@@ -21,6 +21,13 @@ in {
         default = "";
         description = "Fluentd config.";
       };
+
+      package = mkOption {
+        type = types.path;
+        default = pkgs.fluentd;
+        defaultText = "pkgs.fluentd";
+        description = "The fluentd package to use.";
+      };
     };
   };
 
@@ -32,7 +39,7 @@ in {
       description = "Fluentd Daemon";
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.fluentd}/bin/fluentd -c ${pkgs.writeText "fluentd.conf" cfg.config}";
+        ExecStart = "${cfg.package}/bin/fluentd -c ${pkgs.writeText "fluentd.conf" cfg.config}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       };
     };
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index a7785decd19a..9f0fb11f0252 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -4,22 +4,28 @@ with lib;
 
 let
   cfg = config.services.graylog;
-  configBool = b: if b then "true" else "false";
 
   confFile = pkgs.writeText "graylog.conf" ''
-    is_master = ${configBool cfg.isMaster}
+    is_master = ${boolToString cfg.isMaster}
     node_id_file = ${cfg.nodeIdFile}
     password_secret = ${cfg.passwordSecret}
     root_username = ${cfg.rootUsername}
     root_password_sha2 = ${cfg.rootPasswordSha2}
     elasticsearch_cluster_name = ${cfg.elasticsearchClusterName}
-    elasticsearch_discovery_zen_ping_multicast_enabled = ${configBool cfg.elasticsearchDiscoveryZenPingMulticastEnabled}
+    elasticsearch_discovery_zen_ping_multicast_enabled = ${boolToString cfg.elasticsearchDiscoveryZenPingMulticastEnabled}
     elasticsearch_discovery_zen_ping_unicast_hosts = ${cfg.elasticsearchDiscoveryZenPingUnicastHosts}
     message_journal_dir = ${cfg.messageJournalDir}
     mongodb_uri = ${cfg.mongodbUri}
+    plugin_dir = /var/lib/graylog/plugins
 
     ${cfg.extraConfig}
   '';
+
+  glPlugins = pkgs.buildEnv {
+    name = "graylog-plugins";
+    paths = cfg.plugins;
+  };
+
 in
 
 {
@@ -121,6 +127,12 @@ in
         description = "Any other configuration options you might want to add";
       };
 
+      plugins = mkOption {
+        description = "Extra graylog plugins";
+        default = [ ];
+        type = types.listOf types.package;
+      };
+
     };
   };
 
@@ -146,6 +158,16 @@ in
       path = [ pkgs.openjdk8 pkgs.which pkgs.procps ];
       preStart = ''
         mkdir -p /var/lib/graylog -m 755
+
+        rm -rf /var/lib/graylog/plugins || true
+        mkdir -p /var/lib/graylog/plugins -m 755
+
+        for declarativeplugin in `ls ${glPlugins}/bin/`; do
+          ln -sf ${glPlugins}/bin/$declarativeplugin /var/lib/graylog/plugins/$declarativeplugin
+        done
+        for includedplugin in `ls ${cfg.package}/plugin/`; do
+          ln -s ${cfg.package}/plugin/$includedplugin /var/lib/graylog/plugins/$includedplugin || true
+        done
         chown -R ${cfg.user} /var/lib/graylog
 
         mkdir -p ${cfg.messageJournalDir} -m 755
diff --git a/nixos/modules/services/logging/journalbeat.nix b/nixos/modules/services/logging/journalbeat.nix
new file mode 100644
index 000000000000..8186a3b02c37
--- /dev/null
+++ b/nixos/modules/services/logging/journalbeat.nix
@@ -0,0 +1,76 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.journalbeat;
+
+  journalbeatYml = pkgs.writeText "journalbeat.yml" ''
+    name: ${cfg.name}
+    tags: ${builtins.toJSON cfg.tags}
+
+    journalbeat.cursor_state_file: ${cfg.stateDir}/cursor-state
+
+    ${cfg.extraConfig}
+  '';
+
+in
+{
+  options = {
+
+    services.journalbeat = {
+
+      enable = mkEnableOption "journalbeat";
+
+      name = mkOption {
+        type = types.str;
+        default = "journalbeat";
+        description = "Name of the beat";
+      };
+
+      tags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Tags to place on the shipped log messages";
+      };
+
+      stateDir = mkOption {
+        type = types.str;
+        default = "/var/lib/journalbeat";
+        description = "The state directory. Journalbeat's own logs and other data are stored here.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = ''
+          journalbeat:
+            seek_position: cursor
+            cursor_seek_fallback: tail
+            write_cursor_state: true
+            cursor_flush_period: 5s
+            clean_field_names: true
+            convert_to_numbers: false
+            move_metadata_to_field: journal
+            default_type: journal
+        '';
+        description = "Any other configuration options you want to add";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.journalbeat = with pkgs; {
+      description = "Journalbeat log shipper";
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p ${cfg.stateDir}/data
+        mkdir -p ${cfg.stateDir}/logs
+      '';
+      serviceConfig = {
+        ExecStart = "${pkgs.journalbeat}/bin/journalbeat -c ${journalbeatYml} -path.data ${cfg.stateDir}/data -path.logs ${cfg.stateDir}/logs";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 27ed5374f561..2a8ac414720b 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -29,8 +29,8 @@ let
     };
 
   cronJob = ''
-    @reboot   logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags}
-    2 ${cfg.timeOfDay} * * * logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck ${flags}
+    @reboot   logcheck env PATH=/run/wrappers/bin:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags}
+    2 ${cfg.timeOfDay} * * * logcheck env PATH=/run/wrappers/bin:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck ${flags}
   '';
 
   writeIgnoreRule = name: {level, regex, ...}:
@@ -184,7 +184,7 @@ in
         description = ''
           This option defines extra ignore rules.
         '';
-        type = with types; loaOf (submodule ignoreOptions);
+        type = with types; attrsOf (submodule ignoreOptions);
       };
 
       ignoreCron = mkOption {
@@ -192,7 +192,7 @@ in
         description = ''
           This option defines extra ignore rules for cronjobs.
         '';
-        type = with types; loaOf (submodule ignoreCronOptions);
+        type = with types; attrsOf (submodule ignoreCronOptions);
       };
 
       extraGroups = mkOption {
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index 62f6e187ea07..c9477b9e3ab0 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -63,7 +63,7 @@ in
         description = "Enable the logstash web interface.";
       };
 
-      address = mkOption {
+      listenAddress = mkOption {
         type = types.str;
         default = "0.0.0.0";
         description = "Address on which to start webserver.";
@@ -77,7 +77,7 @@ in
 
       inputConfig = mkOption {
         type = types.lines;
-        default = ''stdin { type => "example" }'';
+        default = ''generator { }'';
         description = "Logstash input configuration.";
         example = ''
           # Read from journal
@@ -90,7 +90,7 @@ in
 
       filterConfig = mkOption {
         type = types.lines;
-        default = ''noop {}'';
+        default = "";
         description = "logstash filter configuration.";
         example = ''
           if [type] == "syslog" {
@@ -108,11 +108,11 @@ in
 
       outputConfig = mkOption {
         type = types.lines;
-        default = ''stdout { debug => true debug_format => "json"}'';
+        default = ''stdout { codec => rubydebug }'';
         description = "Logstash output configuration.";
         example = ''
-          redis { host => "localhost" data_type => "list" key => "logstash" codec => json }
-          elasticsearch { embedded => true }
+          redis { host => ["localhost"] data_type => "list" key => "logstash" codec => json }
+          elasticsearch { }
         '';
       };
 
@@ -147,7 +147,7 @@ in
               ${cfg.outputConfig}
             }
           ''} " +
-          ops cfg.enableWeb "-- web -a ${cfg.address} -p ${cfg.port}";
+          ops cfg.enableWeb "-- web -a ${cfg.listenAddress} -p ${cfg.port}";
       };
     };
   };
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 4c9df935debe..3b25e41edb19 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -13,7 +13,7 @@ let
     ''
       base_dir = ${baseDir}
       protocols = ${concatStringsSep " " cfg.protocols}
-      sendmail_path = /var/setuid-wrappers/sendmail
+      sendmail_path = /run/wrappers/bin/sendmail
     ''
 
     (if isNull cfg.sslServerCert then ''
@@ -241,6 +241,9 @@ in
         RuntimeDirectory = [ "dovecot2" ];
       };
 
+      # When copying sieve scripts preserve the original time stamp
+      # (should be 0) so that the compiled sieve script is newer than
+      # the source file and Dovecot won't try to compile it.
       preStart = ''
         rm -rf ${stateDir}/sieve
       '' + optionalString (cfg.sieveScripts != {}) ''
@@ -248,11 +251,11 @@ in
         ${concatStringsSep "\n" (mapAttrsToList (to: from: ''
           if [ -d '${from}' ]; then
             mkdir '${stateDir}/sieve/${to}'
-            cp "${from}/"*.sieve '${stateDir}/sieve/${to}'
+            cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}'
           else
-            cp '${from}' '${stateDir}/sieve/${to}'
+            cp -p '${from}' '${stateDir}/sieve/${to}'
           fi
-           ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
+          ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
         '') cfg.sieveScripts)}
         chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve'
       '';
diff --git a/nixos/modules/services/mail/exim.nix b/nixos/modules/services/mail/exim.nix
index e0890d96a88b..440eae281f40 100644
--- a/nixos/modules/services/mail/exim.nix
+++ b/nixos/modules/services/mail/exim.nix
@@ -70,7 +70,7 @@ in
       etc."exim.conf".text = ''
         exim_user = ${cfg.user}
         exim_group = ${cfg.group}
-        exim_path = /var/setuid-wrappers/exim
+        exim_path = /run/wrappers/bin/exim
         spool_directory = ${cfg.spoolDir}
         ${cfg.config}
       '';
@@ -89,7 +89,7 @@ in
       gid = config.ids.gids.exim;
     };
 
-    security.setuidPrograms = [ "exim" ];
+    security.wrappers.exim.source = "${exim}/bin/exim";
 
     systemd.services.exim = {
       description = "Exim Mail Daemon";
diff --git a/nixos/modules/services/mail/mail.nix b/nixos/modules/services/mail/mail.nix
index 63e8d78b5b02..cfe1b5496a45 100644
--- a/nixos/modules/services/mail/mail.nix
+++ b/nixos/modules/services/mail/mail.nix
@@ -26,7 +26,7 @@ with lib;
 
   config = mkIf (config.services.mail.sendmailSetuidWrapper != null) {
 
-    security.setuidOwners = [ config.services.mail.sendmailSetuidWrapper ];
+    security.wrappers.sendmail = config.services.mail.sendmailSetuidWrapper;
 
   };
 
diff --git a/nixos/modules/services/mail/mlmmj.nix b/nixos/modules/services/mail/mlmmj.nix
index e2b37522cb16..4a01745eb8b6 100644
--- a/nixos/modules/services/mail/mlmmj.nix
+++ b/nixos/modules/services/mail/mlmmj.nix
@@ -18,7 +18,7 @@ let
   footer = domain: list: "To unsubscribe send a mail to ${list}+unsubscribe@${domain}";
   createList = d: l: ''
     ${pkgs.coreutils}/bin/mkdir -p ${listCtl d l}
-    echo ${listAddress d l} > ${listCtl d l}/listadress
+    echo ${listAddress d l} > ${listCtl d l}/listaddress
     echo "${lib.concatStringsSep "\n" (customHeaders d l)}" > ${listCtl d l}/customheaders
     echo ${footer d l} > ${listCtl d l}/footer
     echo ${subjectPrefix l} > ${listCtl d l}/prefix
diff --git a/nixos/modules/services/mail/offlineimap.nix b/nixos/modules/services/mail/offlineimap.nix
index 85ece020905b..4b24bd8d0813 100644
--- a/nixos/modules/services/mail/offlineimap.nix
+++ b/nixos/modules/services/mail/offlineimap.nix
@@ -12,7 +12,6 @@ in {
     install = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Whether to install a user service for Offlineimap. Once
         the service is started, emails will be fetched automatically.
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index cdde41446224..caaa87b94d61 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -79,8 +79,6 @@ let
       relay_domains = ${concatStringsSep ", " cfg.relayDomains}
     ''
     + ''
-      local_recipient_maps =
-
       relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
           cfg.relayHost
         else
diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix
index 8f18b929c114..e17b7516bfff 100644
--- a/nixos/modules/services/mail/rmilter.nix
+++ b/nixos/modules/services/mail/rmilter.nix
@@ -5,35 +5,38 @@ with lib;
 let
 
   rspamdCfg = config.services.rspamd;
+  postfixCfg = config.services.postfix;
   cfg = config.services.rmilter;
 
-  inetSockets = map (sock: let s = stringSplit ":" sock; in "inet:${last s}:${head s}") cfg.bindInetSockets;
-  unixSockets = map (sock: "unix:${sock}") cfg.bindUnixSockets;
+  inetSocket = addr: port: "inet:[${toString port}@${addr}]";
+  unixSocket = sock: "unix:${sock}";
 
-  allSockets = unixSockets ++ inetSockets;
+  systemdSocket = if cfg.bindSocket.type == "unix" then cfg.bindSocket.path
+    else "${cfg.bindSocket.address}:${toString cfg.bindSocket.port}";
+  rmilterSocket = if cfg.bindSocket.type == "unix" then unixSocket cfg.bindSocket.path
+    else inetSocket cfg.bindSocket.address cfg.bindSocket.port;
 
   rmilterConf = ''
-pidfile = /run/rmilter/rmilter.pid;
-bind_socket = ${if cfg.socketActivation then "fd:3" else concatStringsSep ", " allSockets};
-tempdir = /tmp;
-
+    pidfile = /run/rmilter/rmilter.pid;
+    bind_socket = ${if cfg.socketActivation then "fd:3" else rmilterSocket};
+    tempdir = /tmp;
   '' + (with cfg.rspamd; if enable then ''
-spamd {
-        servers = ${concatStringsSep ", " servers};
-        connect_timeout = 1s;
-        results_timeout = 20s;
-        error_time = 10;
-        dead_time = 300;
-        maxerrors = 10;
-        reject_message = "${rejectMessage}";
-        ${optionalString (length whitelist != 0)  "whitelist = ${concatStringsSep ", " whitelist};"}
-
-        # rspamd_metric - metric for using with rspamd
-        # Default: "default"
-        rspamd_metric = "default";
-        ${extraConfig}
-};
-    '' else "") + cfg.extraConfig;
+    spamd {
+      servers = ${concatStringsSep ", " servers};
+      connect_timeout = 1s;
+      results_timeout = 20s;
+      error_time = 10;
+      dead_time = 300;
+      maxerrors = 10;
+      reject_message = "${rejectMessage}";
+      ${optionalString (length whitelist != 0)  "whitelist = ${concatStringsSep ", " whitelist};"}
+
+      # rspamd_metric - metric for using with rspamd
+      # Default: "default"
+      rspamd_metric = "default";
+      ${extraConfig}
+    };
+  '' else "") + cfg.extraConfig;
 
   rmilterConfigFile = pkgs.writeText "rmilter.conf" rmilterConf;
 
@@ -48,11 +51,13 @@ in
     services.rmilter = {
 
       enable = mkOption {
+        type = types.bool;
         default = cfg.rspamd.enable;
         description = "Whether to run the rmilter daemon.";
       };
 
       debug = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to run the rmilter daemon in debug mode.";
       };
@@ -73,25 +78,37 @@ in
         '';
        };
 
-      bindUnixSockets =  mkOption {
-        type = types.listOf types.str;
-        default = ["/run/rmilter/rmilter.sock"];
+      bindSocket.type = mkOption {
+        type = types.enum [ "unix" "inet" ];
+        default = "unix";
         description = ''
-          Unix domain sockets to listen for MTA requests.
+          What kind of socket rmilter should listen on. Either "unix"
+          for an Unix domain socket or "inet" for a TCP socket.
         '';
-        example = ''
-            [ "/run/rmilter.sock"]
+      };
+
+      bindSocket.path = mkOption {
+       type = types.str;
+       default = "/run/rmilter/rmilter.sock";
+       description = ''
+          Path to Unix domain socket to listen on.
         '';
       };
 
-      bindInetSockets = mkOption {
-        type = types.listOf types.str;
-        default = [];
+      bindSocket.address = mkOption {
+        type = types.str;
+        default = "::1";
+        example = "0.0.0.0";
         description = ''
-          Inet addresses to listen (in format accepted by systemd.socket)
+          Inet address to listen on.
         '';
-        example = ''
-            ["127.0.0.1:11990"]
+      };
+
+      bindSocket.port = mkOption {
+        type = types.int;
+        default = 11990;
+        description = ''
+          Inet port to listen on.
         '';
       };
 
@@ -100,14 +117,16 @@ in
         default = true;
         description = ''
           Enable systemd socket activation for rmilter.
-          (disabling socket activation not recommended
-          when unix socket used, and follow to wrong
-          permissions on unix domain socket.)
+
+          Disabling socket activation is not recommended when a Unix
+          domain socket is used and could lead to incorrect
+          permissions.
         '';
       };
 
       rspamd = {
         enable = mkOption {
+          type = types.bool;
           default = rspamdCfg.enable;
           description = "Whether to use rspamd to filter mails";
         };
@@ -157,13 +176,9 @@ in
           type = types.str;
           description = "Addon to postfix configuration";
           default = ''
-smtpd_milters = ${head allSockets}
-# or for TCP socket
-# # smtpd_milters = inet:localhost:9900
-milter_protocol = 6
-milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
-# skip mail without checks if milter will die
-milter_default_action = accept
+            smtpd_milters = ${rmilterSocket}
+            milter_protocol = 6
+            milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
           '';
         };
       };
@@ -175,52 +190,60 @@ milter_default_action = accept
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = mkMerge [
 
-    users.extraUsers = singleton {
-      name = cfg.user;
-      description = "rspamd daemon";
-      uid = config.ids.uids.rmilter;
-      group = cfg.group;
-    };
+    (mkIf cfg.enable {
 
-    users.extraGroups = singleton {
-      name = cfg.group;
-      gid = config.ids.gids.rmilter;
-    };
+      users.extraUsers = singleton {
+        name = cfg.user;
+        description = "rmilter daemon";
+        uid = config.ids.uids.rmilter;
+        group = cfg.group;
+      };
 
-    systemd.services.rmilter = {
-      description = "Rmilter Service";
-
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-
-      serviceConfig = {
-        ExecStart = "${pkgs.rmilter}/bin/rmilter ${optionalString cfg.debug "-d"} -n -c ${rmilterConfigFile}";
-        ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
-        User = cfg.user;
-        Group = cfg.group;
-        PermissionsStartOnly = true;
-        Restart = "always";
-        RuntimeDirectory = "rmilter";
-        RuntimeDirectoryMode = "0755";
+      users.extraGroups = singleton {
+        name = cfg.group;
+        gid = config.ids.gids.rmilter;
       };
 
-    };
+      systemd.services.rmilter = {
+        description = "Rmilter Service";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart = "${pkgs.rmilter}/bin/rmilter ${optionalString cfg.debug "-d"} -n -c ${rmilterConfigFile}";
+          ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
+          User = cfg.user;
+          Group = cfg.group;
+          PermissionsStartOnly = true;
+          Restart = "always";
+          RuntimeDirectory = "rmilter";
+          RuntimeDirectoryMode = "0750";
+        };
 
-    systemd.sockets.rmilter = mkIf cfg.socketActivation {
-      description = "Rmilter service socket";
-      wantedBy = [ "sockets.target" ];
-      socketConfig = {
-        ListenStream = cfg.bindUnixSockets ++ cfg.bindInetSockets;
-        SocketUser = cfg.user;
-        SocketGroup = cfg.group;
-        SocketMode = "0666";
       };
-    };
 
-    services.postfix.extraConfig = optionalString cfg.postfix.enable cfg.postfix.configFragment;
-    users.users.postfix.extraGroups = [ cfg.group ];
-  };
+      systemd.sockets.rmilter = mkIf cfg.socketActivation {
+        description = "Rmilter service socket";
+        wantedBy = [ "sockets.target" ];
+        socketConfig = {
+          ListenStream = systemdSocket;
+          SocketUser = cfg.user;
+          SocketGroup = cfg.group;
+          SocketMode = "0660";
+        };
+      };
+    })
+
+    (mkIf (cfg.enable && cfg.rspamd.enable && rspamdCfg.enable) {
+      users.extraUsers.${cfg.user}.extraGroups = [ rspamdCfg.group ];
+    })
 
+    (mkIf (cfg.enable && cfg.postfix.enable) {
+      services.postfix.extraConfig = cfg.postfix.configFragment;
+      users.extraUsers.${postfixCfg.user}.extraGroups = [ cfg.group ];
+    })
+  ];
 }
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index 98489df78517..6d403e448e04 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -53,8 +53,11 @@ in
       bindSocket = mkOption {
         type = types.listOf types.str;
         default = [
-          "/run/rspamd/rspamd.sock mode=0666 owner=${cfg.user}"
+          "/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}"
+        ]'';
         description = ''
           List of sockets to listen, in format acceptable by rspamd
         '';
diff --git a/nixos/modules/services/misc/apache-kafka.nix b/nixos/modules/services/misc/apache-kafka.nix
index c856d3294c01..82fa1cc2e7e5 100644
--- a/nixos/modules/services/misc/apache-kafka.nix
+++ b/nixos/modules/services/misc/apache-kafka.nix
@@ -19,13 +19,8 @@ let
         ${toString cfg.extraProperties}
       '';
 
-  configDir = pkgs.buildEnv {
-    name = "apache-kafka-conf";
-    paths = [
-      (pkgs.writeTextDir "server.properties" serverProperties)
-      (pkgs.writeTextDir "log4j.properties" cfg.log4jProperties)
-    ];
-  };
+  serverConfig = pkgs.writeText "server.properties" serverProperties;
+  logConfig = pkgs.writeText "log4j.properties" cfg.log4jProperties;
 
 in {
 
@@ -38,7 +33,7 @@ in {
 
     brokerId = mkOption {
       description = "Broker ID.";
-      default = 0;
+      default = -1;
       type = types.int;
     };
 
@@ -143,10 +138,11 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
-            -cp "${cfg.package}/libs/*:${configDir}" \
+            -cp "${cfg.package}/libs/*" \
+            -Dlog4j.configuration=file:${logConfig} \
             ${toString cfg.jvmOptions} \
             kafka.Kafka \
-            ${configDir}/server.properties
+            ${serverConfig}
         '';
         User = "apache-kafka";
         PermissionsStartOnly = true;
diff --git a/nixos/modules/services/misc/autorandr.nix b/nixos/modules/services/misc/autorandr.nix
new file mode 100644
index 000000000000..6746f3fec698
--- /dev/null
+++ b/nixos/modules/services/misc/autorandr.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.autorandr;
+
+in {
+
+  options = {
+
+    services.autorandr = {
+      enable = mkEnableOption "handling of hotplug and sleep events by autorandr";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.udev.packages = [ pkgs.autorandr ];
+
+    environment.systemPackages = [ pkgs.autorandr ];
+
+    # systemd.unitPackages = [ pkgs.autorandr ];
+    systemd.services.autorandr = {
+      unitConfig = {
+        Description = "autorandr execution hook";
+        After = [ "sleep.target" ];
+        StartLimitInterval = "5";
+        StartLimitBurst = "1";
+      };
+      serviceConfig = {
+        ExecStart = "${pkgs.autorandr}/bin/autorandr --batch --change --default default";
+        Type = "oneshot";
+        RemainAfterExit = false;
+      };
+      wantedBy = [ "sleep.target" ];
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix
index 52719222db66..4d78cddcb54f 100644
--- a/nixos/modules/services/misc/bepasty.nix
+++ b/nixos/modules/services/misc/bepasty.nix
@@ -21,7 +21,7 @@ in
         configure a number of bepasty servers which will be started with
         gunicorn.
         '';
-      type = with types ; attrsOf (submodule ({
+      type = with types ; attrsOf (submodule ({ config, ... } : {
 
         options = {
 
@@ -34,7 +34,6 @@ in
             default = "127.0.0.1:8000";
           };
 
-
           dataDir = mkOption {
             type = types.str;
             description = ''
@@ -73,10 +72,28 @@ in
             type = types.str;
             description = ''
               server secret for safe session cookies, must be set.
+
+              Warning: this secret is stored in the WORLD-READABLE Nix store!
+
+              It's recommended to use <option>secretKeyFile</option>
+              which takes precedence over <option>secretKey</option>.
               '';
             default = "";
           };
 
+          secretKeyFile = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              A file that contains the server secret for safe session cookies, must be set.
+
+              <option>secretKeyFile</option> takes precedence over <option>secretKey</option>.
+
+              Warning: when <option>secretKey</option> is non-empty <option>secretKeyFile</option>
+              defaults to a file in the WORLD-READABLE Nix store containing that secret.
+              '';
+          };
+
           workDir = mkOption {
             type = types.str;
             description = ''
@@ -87,11 +104,22 @@ in
           };
 
         };
+        config = {
+          secretKeyFile = mkDefault (
+            if config.secretKey != ""
+            then toString (pkgs.writeTextFile {
+              name = "bepasty-secret-key";
+              text = config.secretKey;
+            })
+            else null
+          );
+        };
       }));
     };
   };
 
   config = mkIf cfg.enable {
+
     environment.systemPackages = [ bepasty ];
 
     # creates gunicorn systemd service for each configured server
@@ -115,7 +143,7 @@ in
           serviceConfig = {
             Type = "simple";
             PrivateTmp = true;
-            ExecStartPre = assert server.secretKey != ""; pkgs.writeScript "bepasty-server.${name}-init" ''
+            ExecStartPre = assert !isNull server.secretKeyFile; pkgs.writeScript "bepasty-server.${name}-init" ''
               #!/bin/sh
               mkdir -p "${server.workDir}"
               mkdir -p "${server.dataDir}"
@@ -123,7 +151,7 @@ in
               cat > ${server.workDir}/bepasty-${name}.conf <<EOF
               SITENAME="${name}"
               STORAGE_FILESYSTEM_DIRECTORY="${server.dataDir}"
-              SECRET_KEY="${server.secretKey}"
+              SECRET_KEY="$(cat "${server.secretKeyFile}")"
               DEFAULT_PERMISSIONS="${server.defaultPermissions}"
               ${server.extraConfig}
               EOF
diff --git a/nixos/modules/services/misc/cgminer.nix b/nixos/modules/services/misc/cgminer.nix
index 5f97cc034148..d5071d8ff767 100644
--- a/nixos/modules/services/misc/cgminer.nix
+++ b/nixos/modules/services/misc/cgminer.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.cgminer;
 
   convType = with builtins;
-    v: if isBool v then (if v then "true" else "false") else toString v;
+    v: if isBool v then boolToString v else toString v;
   mergedHwConfig =
     mapAttrsToList (n: v: ''"${n}": "${(concatStringsSep "," (map convType v))}"'')
       (foldAttrs (n: a: [n] ++ a) [] cfg.hardware);
diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix
index fe13013286b8..8e9bec15dd4f 100755
--- a/nixos/modules/services/misc/confd.nix
+++ b/nixos/modules/services/misc/confd.nix
@@ -12,7 +12,7 @@ let
     nodes = [ ${concatMapStringsSep "," (s: ''"${s}"'') cfg.nodes}, ]
     prefix = "${cfg.prefix}"
     log-level = "${cfg.logLevel}"
-    watch = ${if cfg.watch then "true" else "false"}
+    watch = ${boolToString cfg.watch}
   '';
 
 in {
diff --git a/nixos/modules/services/misc/couchpotato.nix b/nixos/modules/services/misc/couchpotato.nix
new file mode 100644
index 000000000000..496487622351
--- /dev/null
+++ b/nixos/modules/services/misc/couchpotato.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.couchpotato;
+
+in
+{
+  options = {
+    services.couchpotato = {
+      enable = mkEnableOption "CouchPotato Server";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.couchpotato = {
+      description = "CouchPotato Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        mkdir -p /var/lib/couchpotato
+        chown -R couchpotato:couchpotato /var/lib/couchpotato
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = "couchpotato";
+        Group = "couchpotato";
+        PermissionsStartOnly = "true";
+        ExecStart = "${pkgs.couchpotato}/bin/couchpotato";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers = singleton
+      { name = "couchpotato";
+        group = "couchpotato";
+        home = "/var/lib/couchpotato/";
+        description = "CouchPotato daemon user";
+        uid = config.ids.uids.couchpotato;
+      };
+
+    users.extraGroups = singleton
+      { name = "couchpotato";
+        gid = config.ids.gids.couchpotato;
+      };
+  };
+}
diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix
index 1de02d76ba0a..7c91462883f1 100644
--- a/nixos/modules/services/misc/etcd.nix
+++ b/nixos/modules/services/misc/etcd.nix
@@ -119,7 +119,7 @@ in {
     extraConf = mkOption {
       description = ''
         Etcd extra configuration. See
-        <link xlink:href='https://github.com/coreos/etcd/blob/master/Documentation/configuration.md#environment-variables' />
+        <link xlink:href='https://github.com/coreos/etcd/blob/master/Documentation/op-guide/configuration.md#configuration-flags' />
       '';
       type = types.attrsOf types.str;
       default = {};
diff --git a/nixos/modules/services/misc/geoip-updater.nix b/nixos/modules/services/misc/geoip-updater.nix
new file mode 100644
index 000000000000..5135fac8f7dc
--- /dev/null
+++ b/nixos/modules/services/misc/geoip-updater.nix
@@ -0,0 +1,306 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.geoip-updater;
+
+  dbBaseUrl = "https://geolite.maxmind.com/download/geoip/database";
+
+  randomizedTimerDelaySec = "3600";
+
+  # Use writeScriptBin instead of writeScript, so that argv[0] (logged to the
+  # journal) doesn't include the long nix store path hash. (Prefixing the
+  # 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}
+    skipExisting=0
+    debug()
+    {
+        echo "<7>$@"
+    }
+    info()
+    {
+        echo "<6>$@"
+    }
+    error()
+    {
+        echo "<3>$@"
+    }
+    die()
+    {
+        error "$@"
+        exit 1
+    }
+    waitNetworkOnline()
+    {
+        ret=1
+        for i in $(seq 6); do
+            curl_out=$("${pkgs.curl.bin}/bin/curl" \
+                --silent --fail --show-error --max-time 60 "${dbBaseUrl}" 2>&1)
+            if [ $? -eq 0 ]; then
+                debug "Server is reachable (try $i)"
+                ret=0
+                break
+            else
+                debug "Server is unreachable (try $i): $curl_out"
+                sleep 10
+            fi
+        done
+        return $ret
+    }
+    dbFnameTmp()
+    {
+        dburl=$1
+        echo "${cfg.databaseDir}/.$(basename "$dburl")"
+    }
+    dbFnameTmpDecompressed()
+    {
+        dburl=$1
+        echo "${cfg.databaseDir}/.$(basename "$dburl")" | sed 's/\.\(gz\|xz\)$//'
+    }
+    dbFname()
+    {
+        dburl=$1
+        echo "${cfg.databaseDir}/$(basename "$dburl")" | sed 's/\.\(gz\|xz\)$//'
+    }
+    downloadDb()
+    {
+        dburl=$1
+        curl_out=$("${pkgs.curl.bin}/bin/curl" \
+            --silent --fail --show-error --max-time 900 -L -o "$(dbFnameTmp "$dburl")" "$dburl" 2>&1)
+        if [ $? -ne 0 ]; then
+            error "Failed to download $dburl: $curl_out"
+            return 1
+        fi
+    }
+    decompressDb()
+    {
+        fn=$(dbFnameTmp "$1")
+        ret=0
+        case "$fn" in
+            *.gz)
+                cmd_out=$("${pkgs.gzip}/bin/gzip" --decompress --force "$fn" 2>&1)
+                ;;
+            *.xz)
+                cmd_out=$("${pkgs.xz.bin}/bin/xz" --decompress --force "$fn" 2>&1)
+                ;;
+            *)
+                cmd_out=$(echo "File \"$fn\" is neither a .gz nor .xz file")
+                false
+                ;;
+        esac
+        if [ $? -ne 0 ]; then
+            error "$cmd_out"
+            ret=1
+        fi
+    }
+    atomicRename()
+    {
+        dburl=$1
+        mv "$(dbFnameTmpDecompressed "$dburl")" "$(dbFname "$dburl")"
+    }
+    removeIfNotInConfig()
+    {
+        # Arg 1 is the full path of an installed DB.
+        # If the corresponding database is not specified in the NixOS config we
+        # remove it.
+        db=$1
+        for cdb in ${lib.concatStringsSep " " cfg.databases}; do
+            confDb=$(echo "$cdb" | sed 's/\.\(gz\|xz\)$//')
+            if [ "$(basename "$db")" = "$(basename "$confDb")" ]; then
+                return 0
+            fi
+        done
+        rm "$db"
+        if [ $? -eq 0 ]; then
+            debug "Removed $(basename "$db") (not listed in services.geoip-updater.databases)"
+        else
+            error "Failed to remove $db"
+        fi
+    }
+    removeUnspecifiedDbs()
+    {
+        for f in "${cfg.databaseDir}/"*; do
+            test -f "$f" || continue
+            case "$f" in
+                *.dat|*.mmdb|*.csv)
+                    removeIfNotInConfig "$f"
+                    ;;
+                *)
+                    debug "Not removing \"$f\" (unknown file extension)"
+                    ;;
+            esac
+        done
+    }
+    downloadAndInstall()
+    {
+        dburl=$1
+        if [ "$skipExisting" -eq 1 -a -f "$(dbFname "$dburl")" ]; then
+            debug "Skipping existing file: $(dbFname "$dburl")"
+            return 0
+        fi
+        downloadDb "$dburl" || return 1
+        decompressDb "$dburl" || return 1
+        atomicRename "$dburl" || return 1
+        info "Updated $(basename "$(dbFname "$dburl")")"
+    }
+    for arg in "$@"; do
+        case "$arg" in
+            --skip-existing)
+                skipExisting=1
+                info "Option --skip-existing is set: not updating existing databases"
+                ;;
+            *)
+                error "Unknown argument: $arg";;
+        esac
+    done
+    waitNetworkOnline || die "Network is down (${dbBaseUrl} is unreachable)"
+    test -d "${cfg.databaseDir}" || die "Database directory (${cfg.databaseDir}) doesn't exist"
+    debug "Starting update of GeoIP databases in ${cfg.databaseDir}"
+    all_ret=0
+    for db in ${lib.concatStringsSep " \\\n        " cfg.databases}; do
+        downloadAndInstall "${dbBaseUrl}/$db" || all_ret=1
+    done
+    removeUnspecifiedDbs || all_ret=1
+    if [ $all_ret -eq 0 ]; then
+        info "Completed GeoIP database update in ${cfg.databaseDir}"
+    else
+        error "Completed GeoIP database update in ${cfg.databaseDir}, with error(s)"
+    fi
+    # Hack to work around systemd journal race:
+    # https://github.com/systemd/systemd/issues/2913
+    sleep 2
+    exit $all_ret
+  '';
+
+in
+
+{
+  options = {
+    services.geoip-updater = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable periodic downloading of GeoIP databases from
+          maxmind.com. You might want to enable this if you, for instance, use
+          ntopng or Wireshark.
+        '';
+      };
+
+      interval = mkOption {
+        type = types.str;
+        default = "weekly";
+        description = ''
+          Update the GeoIP databases at this time / interval.
+          The format is described in
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>.
+          To prevent load spikes on maxmind.com, the timer interval is
+          randomized by an additional delay of ${randomizedTimerDelaySec}
+          seconds. Setting a shorter interval than this is not recommended.
+        '';
+      };
+
+      databaseDir = mkOption {
+        type = types.path;
+        default = "/var/lib/geoip-databases";
+        description = ''
+          Directory that will contain GeoIP databases.
+        '';
+      };
+
+      databases = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "GeoLiteCountry/GeoIP.dat.gz"
+          "GeoIPv6.dat.gz"
+          "GeoLiteCity.dat.xz"
+          "GeoLiteCityv6-beta/GeoLiteCityv6.dat.gz"
+          "asnum/GeoIPASNum.dat.gz"
+          "asnum/GeoIPASNumv6.dat.gz"
+          "GeoLite2-Country.mmdb.gz"
+          "GeoLite2-City.mmdb.gz"
+        ];
+        description = ''
+          Which GeoIP databases to update. The full URL is ${dbBaseUrl}/ +
+          <literal>the_database</literal>.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = (builtins.filter
+          (x: builtins.match ".*\.(gz|xz)$" x == null) cfg.databases) == [];
+        message = ''
+          services.geoip-updater.databases supports only .gz and .xz databases.
+
+          Current value:
+          ${toString cfg.databases}
+
+          Offending element(s):
+          ${toString (builtins.filter (x: builtins.match ".*\.(gz|xz)$" x == null) cfg.databases)};
+        '';
+      }
+    ];
+
+    users.extraUsers.geoip = {
+      group = "root";
+      description = "GeoIP database updater";
+      uid = config.ids.uids.geoip;
+    };
+
+    systemd.timers.geoip-updater =
+      { description = "GeoIP Updater Timer";
+        partOf = [ "geoip-updater.service" ];
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = cfg.interval;
+        timerConfig.Persistent = "true";
+        timerConfig.RandomizedDelaySec = randomizedTimerDelaySec;
+      };
+
+    systemd.services.geoip-updater = {
+      description = "GeoIP Updater";
+      after = [ "network-online.target" "nss-lookup.target" ];
+      wants = [ "network-online.target" ];
+      preStart = ''
+        mkdir -p "${cfg.databaseDir}"
+        chmod 755 "${cfg.databaseDir}"
+        chown geoip:root "${cfg.databaseDir}"
+      '';
+      serviceConfig = {
+        ExecStart = "${geoip-updater}/bin/geoip-updater";
+        User = "geoip";
+        PermissionsStartOnly = true;
+      };
+    };
+
+    systemd.services.geoip-updater-setup = {
+      description = "GeoIP Updater Setup";
+      after = [ "network-online.target" "nss-lookup.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      conflicts = [ "geoip-updater.service" ];
+      preStart = ''
+        mkdir -p "${cfg.databaseDir}"
+        chmod 755 "${cfg.databaseDir}"
+        chown geoip:root "${cfg.databaseDir}"
+      '';
+      serviceConfig = {
+        ExecStart = "${geoip-updater}/bin/geoip-updater --skip-existing";
+        User = "geoip";
+        PermissionsStartOnly = true;
+        # So it won't be (needlessly) restarted:
+        RemainAfterExit = true;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 1fc3a5cc8691..ee881edb5ab0 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -481,6 +481,7 @@ in {
         mkdir -p ${cfg.statePath}/repositories
         mkdir -p ${gitlabConfig.production.shared.path}/artifacts
         mkdir -p ${gitlabConfig.production.shared.path}/lfs-objects
+        mkdir -p ${gitlabConfig.production.shared.path}/pages
         mkdir -p ${cfg.statePath}/log
         mkdir -p ${cfg.statePath}/shell
         mkdir -p ${cfg.statePath}/tmp/pids
@@ -528,8 +529,8 @@ in {
 
         if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
           if ! test -e "${cfg.statePath}/db-created"; then
-            psql postgres -c "CREATE ROLE gitlab WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'"
-            ${config.services.postgresql.package}/bin/createdb --owner gitlab gitlab || true
+            psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'"
+            ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName} || true
             touch "${cfg.statePath}/db-created"
           fi
         fi
diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix
new file mode 100644
index 000000000000..ad2e36d04d53
--- /dev/null
+++ b/nixos/modules/services/misc/gogs.nix
@@ -0,0 +1,266 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gogs;
+  configFile = pkgs.writeText "app.ini" ''
+    APP_NAME = ${cfg.appName}
+    RUN_USER = ${cfg.user}
+    RUN_MODE = prod
+
+    [database]
+    DB_TYPE = ${cfg.database.type}
+    HOST = ${cfg.database.host}:${toString cfg.database.port}
+    NAME = ${cfg.database.name}
+    USER = ${cfg.database.user}
+    PASSWD = #dbpass#
+    PATH = ${cfg.database.path}
+
+    [repository]
+    ROOT = ${cfg.repositoryRoot}
+
+    [server]
+    DOMAIN = ${cfg.domain}
+    HTTP_ADDR = ${cfg.httpAddress}
+    HTTP_PORT = ${toString cfg.httpPort}
+    ROOT_URL = ${cfg.rootUrl}
+
+    [session]
+    COOKIE_NAME = session
+    COOKIE_SECURE = ${boolToString cfg.cookieSecure}
+
+    [security]
+    SECRET_KEY = #secretkey#
+    INSTALL_LOCK = true
+
+    ${cfg.extraConfig}
+  '';
+in
+
+{
+  options = {
+    services.gogs = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Enable Go Git Service.";
+      };
+
+      useWizard = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Do not generate a configuration and use Gogs' installation wizard instead. The first registered user will be administrator.";
+      };
+
+      stateDir = mkOption {
+        default = "/var/lib/gogs";
+        type = types.str;
+        description = "Gogs data directory.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "gogs";
+        description = "User account under which Gogs runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "gogs";
+        description = "Group account under which Gogs runs.";
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "sqlite3" "mysql" "postgres" ];
+          example = "mysql";
+          default = "sqlite3";
+          description = "Database engine to use.";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "Database host address.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 3306;
+          description = "Database host port.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "gogs";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "gogs";
+          description = "Database user.";
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            The password corresponding to <option>database.user</option>.
+            Warning: this is stored in cleartext in the Nix store!
+            Use <option>database.passwordFile</option> instead.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.path;
+          default = null;
+          example = "/run/keys/gogs-dbpassword";
+          description = ''
+            A file containing the password corresponding to
+            <option>database.user</option>.
+          '';
+        };
+
+        path = mkOption {
+          type = types.str;
+          default = "${cfg.stateDir}/data/gogs.db";
+          description = "Path to the sqlite3 database file.";
+        };
+      };
+
+      appName = mkOption {
+        type = types.str;
+        default = "Gogs: Go Git Service";
+        description = "Application name.";
+      };
+
+      repositoryRoot = mkOption {
+        type = types.str;
+        default = "${cfg.stateDir}/repositories";
+        description = "Path to the git repositories.";
+      };
+
+      domain = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = "Domain name of your server.";
+      };
+
+      rootUrl = mkOption {
+        type = types.str;
+        default = "http://localhost:3000/";
+        description = "Full public URL of Gogs server.";
+      };
+
+      httpAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = "HTTP listen address.";
+      };
+
+      httpPort = mkOption {
+        type = types.int;
+        default = 3000;
+        description = "HTTP listen port.";
+      };
+
+      cookieSecure = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Marks session cookies as "secure" as a hint for browsers to only send
+          them via HTTPS. This option is recommend, if Gogs is being served over HTTPS.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.str;
+        default = "";
+        description = "Configuration lines appended to the generated Gogs configuration file.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.gogs = {
+      description = "Gogs (Go Git Service)";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.gogs.bin ];
+
+      preStart = let
+        runConfig = "${cfg.stateDir}/custom/conf/app.ini";
+        secretKey = "${cfg.stateDir}/custom/conf/secret_key";
+      in ''
+        # copy custom configuration and generate a random secret key if needed
+        ${optionalString (cfg.useWizard == false) ''
+          mkdir -p ${cfg.stateDir}/custom/conf
+          cp -f ${configFile} ${runConfig}
+
+          if [ ! -e ${secretKey} ]; then
+              head -c 16 /dev/urandom | base64 > ${secretKey}
+          fi
+
+          KEY=$(head -n1 ${secretKey})
+          DBPASS=$(head -n1 ${cfg.database.passwordFile})
+          sed -e "s,#secretkey#,$KEY,g" \
+              -e "s,#dbpass#,$DBPASS,g" \
+              -i ${runConfig}
+          chmod 440 ${runConfig} ${secretKey}
+        ''}
+
+        mkdir -p ${cfg.repositoryRoot}
+        # update all hooks' binary paths
+        HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 4 -type f -wholename "*git/hooks/*")
+        if [ "$HOOKS" ]
+        then
+          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gogs,${pkgs.gogs.bin}/bin/gogs,g' $HOOKS
+          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS
+          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS
+          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS
+        fi
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = cfg.stateDir;
+        ExecStart = "${pkgs.gogs.bin}/bin/gogs web";
+        Restart = "always";
+      };
+
+      environment = {
+        USER = cfg.user;
+        HOME = cfg.stateDir;
+        GOGS_WORK_DIR = cfg.stateDir;
+      };
+    };
+
+    users = {
+      extraUsers.gogs = {
+        description = "Go Git Service";
+        uid = config.ids.uids.gogs;
+        group = "gogs";
+        home = cfg.stateDir;
+        createHome = true;
+        shell = pkgs.bash;
+      };
+      extraGroups.gogs.gid = config.ids.gids.gogs;
+    };
+
+    warnings = optional (cfg.database.password != "")
+      ''config.services.gogs.database.password will be stored as plaintext
+        in the Nix store. Use database.passwordFile instead.'';
+
+    # Create database passwordFile default when password is configured.
+    services.gogs.database.passwordFile = mkIf (cfg.database.password != "")
+      (mkDefault (toString (pkgs.writeTextFile {
+        name = "gogs-database-password";
+        text = cfg.database.password;
+      })));
+  };
+}
diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix
index d0e9b839e754..e07a4a44613a 100644
--- a/nixos/modules/services/misc/ihaskell.nix
+++ b/nixos/modules/services/misc/ihaskell.nix
@@ -16,22 +16,9 @@ in
     services.ihaskell = {
       enable = mkOption {
         default = false;
-        example = true;
         description = "Autostart an IHaskell notebook service.";
       };
 
-      haskellPackages = mkOption {
-        default = pkgs.haskellPackages;
-        defaultText = "pkgs.haskellPackages";
-        example = literalExample "pkgs.haskell.packages.ghc784";
-        description = ''
-          haskellPackages used to build IHaskell and other packages.
-          This can be used to change the GHC version used to build
-          IHaskell and the packages listed in
-          <varname>extraPackages</varname>.
-        '';
-      };
-
       extraPackages = mkOption {
         default = self: [];
         example = literalExample ''
diff --git a/nixos/modules/services/misc/irkerd.nix b/nixos/modules/services/misc/irkerd.nix
new file mode 100644
index 000000000000..993d77ba424c
--- /dev/null
+++ b/nixos/modules/services/misc/irkerd.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.irkerd;
+  ports = [ 6659 ];
+in
+{
+  options.services.irkerd = {
+    enable = mkOption {
+      description = "Whether to enable irker, an IRC notification daemon.";
+      default = false;
+      type = types.bool;
+    };
+
+    openPorts = mkOption {
+      description = "Open ports in the firewall for irkerd";
+      default = false;
+      type = types.bool;
+    };
+
+    listenAddress = mkOption {
+      default = "localhost";
+      example = "0.0.0.0";
+      type = types.str;
+      description = ''
+        Specifies the bind address on which the irker daemon listens.
+        The default is localhost.
+
+        Irker authors strongly warn about the risks of running this on
+        a publicly accessible interface, so change this with caution.
+      '';
+    };
+
+    nick = mkOption {
+      default = "irker";
+      type = types.str;
+      description = "Nick to use for irker";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.irkerd = {
+      description = "Internet Relay Chat (IRC) notification daemon";
+      documentation = [ "man:irkerd(8)" "man:irkerhook(1)" "man:irk(1)" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.irker}/bin/irkerd -H ${cfg.listenAddress} -n ${cfg.nick}";
+        User = "irkerd";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.irker ];
+
+    users.users.irkerd = {
+      description = "Irker daemon user";
+      isSystemUser = true;
+      group = "irkerd";
+    };
+    users.groups.irkerd = {};
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openPorts ports;
+    networking.firewall.allowedUDPPorts = mkIf cfg.openPorts ports;
+  };
+}
diff --git a/nixos/modules/services/misc/jackett.nix b/nixos/modules/services/misc/jackett.nix
new file mode 100644
index 000000000000..87a41ee70b54
--- /dev/null
+++ b/nixos/modules/services/misc/jackett.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, lib, mono, ... }:
+
+with lib;
+
+let
+  cfg = config.services.jackett;
+in
+{
+  options = {
+    services.jackett = {
+      enable = mkEnableOption "Jackett";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.jackett = {
+      description = "Jackett";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        test -d /var/lib/jackett/ || {
+          echo "Creating jackett data directory in /var/lib/jackett/"
+          mkdir -p /var/lib/jackett/
+        }
+        chown -R jackett:jackett /var/lib/jackett/
+        chmod 0700 /var/lib/jackett/
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = "jackett";
+        Group = "jackett";
+        PermissionsStartOnly = "true";
+        ExecStart = "${pkgs.jackett}/bin/Jackett";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers.jackett = {
+      uid = config.ids.uids.jackett;
+      home = "/var/lib/jackett";
+      group = "jackett";
+    };
+    users.extraGroups.jackett.gid = config.ids.gids.jackett;
+
+  };
+}
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index 4a1bea50c14e..ae9826968b1f 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -5,9 +5,8 @@ with lib;
 let
   cfg = config.services.matrix-synapse;
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
-  mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${fromBool r.compress}}'';
-  mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${fromBool l.tls}, x_forwarded: ${fromBool l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
-  fromBool = x: if x then "true" else "false";
+  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)}]}'';
   configFile = pkgs.writeText "homeserver.yaml" ''
 ${optionalString (cfg.tls_certificate_path != null) ''
 tls_certificate_path: "${cfg.tls_certificate_path}"
@@ -18,7 +17,7 @@ tls_private_key_path: "${cfg.tls_private_key_path}"
 ${optionalString (cfg.tls_dh_params_path != null) ''
 tls_dh_params_path: "${cfg.tls_dh_params_path}"
 ''}
-no_tls: ${fromBool cfg.no_tls}
+no_tls: ${boolToString cfg.no_tls}
 ${optionalString (cfg.bind_port != null) ''
 bind_port: ${toString cfg.bind_port}
 ''}
@@ -30,7 +29,7 @@ bind_host: "${cfg.bind_host}"
 ''}
 server_name: "${cfg.server_name}"
 pid_file: "/var/run/matrix-synapse.pid"
-web_client: ${fromBool cfg.web_client}
+web_client: ${boolToString cfg.web_client}
 ${optionalString (cfg.public_baseurl != null) ''
 public_baseurl: "${cfg.public_baseurl}"
 ''}
@@ -58,8 +57,8 @@ media_store_path: "/var/lib/matrix-synapse/media"
 uploads_path: "/var/lib/matrix-synapse/uploads"
 max_upload_size: "${cfg.max_upload_size}"
 max_image_pixels: "${cfg.max_image_pixels}"
-dynamic_thumbnails: ${fromBool cfg.dynamic_thumbnails}
-url_preview_enabled: ${fromBool cfg.url_preview_enabled}
+dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails}
+url_preview_enabled: ${boolToString cfg.url_preview_enabled}
 ${optionalString (cfg.url_preview_enabled == true) ''
 url_preview_ip_range_blacklist: ${builtins.toJSON cfg.url_preview_ip_range_blacklist}
 url_preview_ip_range_whitelist: ${builtins.toJSON cfg.url_preview_ip_range_whitelist}
@@ -67,10 +66,10 @@ url_preview_url_blacklist: ${builtins.toJSON cfg.url_preview_url_blacklist}
 ''}
 recaptcha_private_key: "${cfg.recaptcha_private_key}"
 recaptcha_public_key: "${cfg.recaptcha_public_key}"
-enable_registration_captcha: ${fromBool cfg.enable_registration_captcha}
+enable_registration_captcha: ${boolToString cfg.enable_registration_captcha}
 turn_uris: ${builtins.toJSON cfg.turn_uris}
 turn_shared_secret: "${cfg.turn_shared_secret}"
-enable_registration: ${fromBool cfg.enable_registration}
+enable_registration: ${boolToString cfg.enable_registration}
 ${optionalString (cfg.registration_shared_secret != null) ''
 registration_shared_secret: "${cfg.registration_shared_secret}"
 ''}
@@ -78,15 +77,15 @@ recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify"
 turn_user_lifetime: "${cfg.turn_user_lifetime}"
 user_creation_max_duration: ${cfg.user_creation_max_duration}
 bcrypt_rounds: ${cfg.bcrypt_rounds}
-allow_guest_access: ${fromBool cfg.allow_guest_access}
+allow_guest_access: ${boolToString cfg.allow_guest_access}
 trusted_third_party_id_servers: ${builtins.toJSON cfg.trusted_third_party_id_servers}
 room_invite_state_types: ${builtins.toJSON cfg.room_invite_state_types}
 ${optionalString (cfg.macaroon_secret_key != null) ''
   macaroon_secret_key: "${cfg.macaroon_secret_key}"
 ''}
-expire_access_token: ${fromBool cfg.expire_access_token}
-enable_metrics: ${fromBool cfg.enable_metrics}
-report_stats: ${fromBool cfg.report_stats}
+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"
 key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
diff --git a/nixos/modules/services/misc/mesos-master.nix b/nixos/modules/services/misc/mesos-master.nix
index 99583ebeebd5..0523c6549ed6 100644
--- a/nixos/modules/services/misc/mesos-master.nix
+++ b/nixos/modules/services/misc/mesos-master.nix
@@ -16,12 +16,30 @@ in {
         type = types.bool;
       };
 
+      ip = mkOption {
+        description = "IP address to listen on.";
+        default = "0.0.0.0";
+        type = types.str;
+      };
+
       port = mkOption {
         description = "Mesos Master port";
         default = 5050;
         type = types.int;
       };
 
+      advertiseIp = mkOption {
+        description = "IP address advertised to reach this master.";
+        default = null;
+        type = types.nullOr types.str;
+      };
+
+      advertisePort = mkOption {
+        description = "Port advertised to reach this Mesos master.";
+        default = null;
+        type = types.nullOr types.int;
+      };
+
       zk = mkOption {
         description = ''
           ZooKeeper URL (used for leader election amongst masters).
@@ -84,7 +102,10 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-master \
+            --ip=${cfg.ip} \
             --port=${toString cfg.port} \
+            ${optionalString (cfg.advertiseIp != null) "--advertise_ip=${cfg.advertiseIp}"} \
+            ${optionalString (cfg.advertisePort  != null) "--advertise_port=${toString cfg.advertisePort}"} \
             ${if cfg.quorum == 0
               then "--registry=in_memory"
               else "--zk=${cfg.zk} --registry=replicated_log --quorum=${toString cfg.quorum}"} \
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 9ddecb6fe30c..47be10274d3b 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -12,7 +12,23 @@ let
   attribsArg = optionalString (cfg.attributes != {})
                               "--attributes=${mkAttributes cfg.attributes}";
 
-  containerizers = [ "mesos" ] ++ (optional cfg.withDocker "docker");
+  containerizersArg = concatStringsSep "," (
+    lib.unique (
+      cfg.containerizers ++ (optional cfg.withDocker "docker")
+    )
+  );
+
+  imageProvidersArg = concatStringsSep "," (
+    lib.unique (
+      cfg.imageProviders ++ (optional cfg.withDocker "docker")
+    )
+  );
+
+  isolationArg = concatStringsSep "," (
+    lib.unique (
+      cfg.isolation ++ (optionals cfg.withDocker [ "filesystem/linux" "docker/runtime"])
+    )
+  );
 
 in {
 
@@ -27,7 +43,7 @@ in {
       ip = mkOption {
         description = "IP address to listen on.";
         default = "0.0.0.0";
-        type = types.string;
+        type = types.str;
       };
 
       port = mkOption {
@@ -36,6 +52,53 @@ in {
         type = types.int;
       };
 
+      advertiseIp = mkOption {
+        description = "IP address advertised to reach this agent.";
+        default = null;
+        type = types.nullOr types.str;
+      };
+
+      advertisePort = mkOption {
+        description = "Port advertised to reach this agent.";
+        default = null;
+        type = types.nullOr types.int;
+      };
+
+      containerizers = mkOption {
+        description = ''
+          List of containerizer implementations to compose in order to provide
+          containerization. Available options are mesos and docker.
+          The order the containerizers are specified is the order they are tried.
+        '';
+        default = [ "mesos" ];
+        type = types.listOf types.str;
+      };
+
+      imageProviders = mkOption {
+        description = "List of supported image providers, e.g., APPC,DOCKER.";
+        default = [ ];
+        type = types.listOf types.str;
+      };
+
+      imageProvisionerBackend = mkOption {
+        description = ''
+          Strategy for provisioning container rootfs from images,
+          e.g., aufs, bind, copy, overlay.
+        '';
+        default = "copy";
+        type = types.str;
+      };
+
+      isolation = mkOption {
+        description = ''
+          Isolation mechanisms to use, e.g., posix/cpu,posix/mem, or
+          cgroups/cpu,cgroups/mem, or network/port_mapping, or `gpu/nvidia` for nvidia
+          specific gpu isolation.
+        '';
+        default = [ "posix/cpu" "posix/mem" ];
+        type = types.listOf types.str;
+      };
+
       master = mkOption {
         description = ''
           May be one of:
@@ -57,6 +120,16 @@ in {
         type = types.bool;
       };
 
+      dockerRegistry = mkOption {
+        description = ''
+          The default url for pulling Docker images.
+          It could either be a Docker registry server url,
+          or a local path in which Docker image archives are stored.
+        '';
+        default = null;
+        type = types.nullOr (types.either types.str types.path);
+      };
+
       workDir = mkOption {
         description = "The Mesos work directory.";
         default = "/var/lib/mesos/slave";
@@ -96,28 +169,45 @@ in {
                     host = "aabc123";
                     os = "nixos"; };
       };
+
+      executorEnvironmentVariables = mkOption {
+        description = ''
+          The environment variables that should be passed to the executor, and thus subsequently task(s).
+        '';
+        default = {
+          PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+        };
+        type = types.attrsOf types.str;
+      };
     };
 
   };
 
-
   config = mkIf cfg.enable {
     systemd.services.mesos-slave = {
       description = "Mesos Slave";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      environment.MESOS_CONTAINERIZERS = concatStringsSep "," containerizers;
+      path = [ pkgs.stdenv.shellPackage ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-slave \
+            --containerizers=${containerizersArg} \
+            --image_providers=${imageProvidersArg} \
+            --image_provisioner_backend=${cfg.imageProvisionerBackend} \
+            --isolation=${isolationArg} \
             --ip=${cfg.ip} \
             --port=${toString cfg.port} \
+            ${optionalString (cfg.advertiseIp != null) "--advertise_ip=${cfg.advertiseIp}"} \
+            ${optionalString (cfg.advertisePort  != null) "--advertise_port=${toString cfg.advertisePort}"} \
             --master=${cfg.master} \
             --work_dir=${cfg.workDir} \
             --logging_level=${cfg.logLevel} \
             ${attribsArg} \
             ${optionalString cfg.withHadoop "--hadoop-home=${pkgs.hadoop}"} \
             ${optionalString cfg.withDocker "--docker=${pkgs.docker}/libexec/docker/docker"} \
+            ${optionalString (cfg.dockerRegistry != null) "--docker_registry=${cfg.dockerRegistry}"} \
+            --executor_environment_variables=${lib.escapeShellArg (builtins.toJSON cfg.executorEnvironmentVariables)} \
             ${toString cfg.extraCmdLineOptions}
         '';
         PermissionsStartOnly = true;
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 7101cadfeed2..bf1304ee7ac2 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -8,6 +8,8 @@ let
 
   nix = cfg.package.out;
 
+  isNix112 = versionAtLeast (getVersion nix) "1.12pre";
+
   makeNixBuildUser = nr:
     { name = "nixbld${toString nr}";
       description = "Nix build user ${toString nr}";
@@ -39,11 +41,12 @@ let
         build-users-group = nixbld
         build-max-jobs = ${toString (cfg.maxJobs)}
         build-cores = ${toString (cfg.buildCores)}
-        build-use-sandbox = ${if (builtins.isBool cfg.useSandbox) then (if cfg.useSandbox then "true" else "false") else cfg.useSandbox}
+        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}
+        auto-optimise-store = ${boolToString cfg.autoOptimiseStore}
         ${optionalString cfg.requireSignedBinaryCaches ''
           signed-binary-caches = *
         ''}
@@ -84,6 +87,18 @@ in
         '';
       };
 
+      autoOptimiseStore = mkOption {
+        type = types.bool;
+        default = false;
+        example = true;
+        description = ''
+         If set to true, Nix automatically detects files in the store that have
+         identical contents, and replaces them with hard links to a single copy.
+         This saves disk space. If set to false (the default), you can still run
+         nix-store --optimise to get rid of duplicate files.
+        '';
+      };
+
       buildCores = mkOption {
         type = types.int;
         default = 1;
@@ -162,22 +177,23 @@ in
       buildMachines = mkOption {
         type = types.listOf types.attrs;
         default = [];
-        example = [
-          { hostName = "voila.labs.cs.uu.nl";
-            sshUser = "nix";
-            sshKey = "/root/.ssh/id_buildfarm";
-            system = "powerpc-darwin";
-            maxJobs = 1;
-          }
-          { hostName = "linux64.example.org";
-            sshUser = "buildfarm";
-            sshKey = "/root/.ssh/id_buildfarm";
-            system = "x86_64-linux";
-            maxJobs = 2;
-            supportedFeatures = [ "kvm" ];
-            mandatoryFeatures = [ "perf" ];
-          }
-        ];
+        example = literalExample ''
+          [ { hostName = "voila.labs.cs.uu.nl";
+              sshUser = "nix";
+              sshKey = "/root/.ssh/id_buildfarm";
+              system = "powerpc-darwin";
+              maxJobs = 1;
+            }
+            { hostName = "linux64.example.org";
+              sshUser = "buildfarm";
+              sshKey = "/root/.ssh/id_buildfarm";
+              system = "x86_64-linux";
+              maxJobs = 2;
+              supportedFeatures = [ "kvm" ];
+              mandatoryFeatures = [ "perf" ];
+            }
+          ]
+        '';
         description = ''
           This option lists the machines to be used if distributed
           builds are enabled (see
@@ -380,7 +396,9 @@ in
 
     nix.envVars =
       { 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
@@ -389,9 +407,11 @@ in
       }
 
       // optionalAttrs cfg.distributedBuilds {
-        NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
-        NIX_REMOTE_SYSTEMS = "/etc/nix/machines";
-        NIX_CURRENT_LOAD = "/run/nix/current-load";
+        NIX_BUILD_HOOK =
+          if isNix112 then
+            "${nix}/libexec/nix/build-remote"
+          else
+            "${nix}/libexec/nix/build-remote.pl";
       };
 
     # Set up the environment variables for running Nix.
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
index 304168c65b0b..8b493041b2c9 100644
--- a/nixos/modules/services/misc/nix-gc.nix
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -26,7 +26,7 @@ in
         description = ''
           Specification (in the format described by
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
           which the garbage collector will run.
         '';
       };
diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix
index a76bfd9f1f19..295e7fb0ba03 100644
--- a/nixos/modules/services/misc/nix-optimise.nix
+++ b/nixos/modules/services/misc/nix-optimise.nix
@@ -26,7 +26,7 @@ in
         description = ''
           Specification (in the format described by
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
           which the optimiser will run.
         '';
       };
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 306ee346523d..622607f3b32d 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -41,7 +41,7 @@ let
 
   entry = "${manual.manual}/share/doc/nixos/index.html";
 
-  help = pkgs.writeScriptBin "nixos-help"
+  helpScript = pkgs.writeScriptBin "nixos-help"
     ''
       #! ${pkgs.stdenv.shell} -e
       browser="$BROWSER"
@@ -58,6 +58,15 @@ let
       exec "$browser" ${entry}
     '';
 
+  desktopItem = pkgs.makeDesktopItem {
+    name = "nixos-manual";
+    desktopName = "NixOS Manual";
+    genericName = "View NixOS documentation in a web browser";
+    # TODO: find a better icon (Nix logo + help overlay?)
+    icon = "system-help";
+    exec = "${helpScript}/bin/nixos-help";
+    categories = "System";
+  };
 in
 
 {
@@ -105,7 +114,8 @@ in
     system.build.manual = manual;
 
     environment.systemPackages =
-      [ manual.manual help ]
+      [ manual.manual helpScript ]
+      ++ optional config.services.xserver.enable desktopItem
       ++ optional config.programs.man.enable manual.manpages;
 
     boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"];
diff --git a/nixos/modules/services/misc/octoprint.nix b/nixos/modules/services/misc/octoprint.nix
index c2b3f63be7d4..6883993a893b 100644
--- a/nixos/modules/services/misc/octoprint.nix
+++ b/nixos/modules/services/misc/octoprint.nix
@@ -7,7 +7,7 @@ let
   cfg = config.services.octoprint;
 
   baseConfig = {
-    plugins.cura.cura_engine = "${pkgs.curaengine}/bin/CuraEngine";
+    plugins.cura.cura_engine = "${pkgs.curaengine_stable}/bin/CuraEngine";
     server.host = cfg.host;
     server.port = cfg.port;
     webcam.ffmpeg = "${pkgs.ffmpeg.bin}/bin/ffmpeg";
@@ -117,7 +117,7 @@ in
       '';
 
       serviceConfig = {
-        ExecStart = "${pkgs.octoprint}/bin/octoprint -b ${cfg.stateDir}";
+        ExecStart = "${pkgs.octoprint}/bin/octoprint serve -b ${cfg.stateDir}";
         User = cfg.user;
         Group = cfg.group;
         PermissionsStartOnly = true;
diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix
index f6bf2dee9865..ecd9a6f52da2 100644
--- a/nixos/modules/services/misc/plex.nix
+++ b/nixos/modules/services/misc/plex.nix
@@ -91,9 +91,11 @@ in
         # Copy the database skeleton files to /var/lib/plex/.skeleton
         # See the the Nix expression for Plex's package for more information on
         # why this is done.
-        test -d "${cfg.dataDir}/.skeleton" || mkdir "${cfg.dataDir}/.skeleton"
+        install --owner ${cfg.user} --group ${cfg.group} -d "${cfg.dataDir}/.skeleton"
         for db in "com.plexapp.plugins.library.db"; do
-            cp "${cfg.package}/usr/lib/plexmediaserver/Resources/base_$db" "${cfg.dataDir}/.skeleton/$db"
+            if [ ! -e  "${cfg.dataDir}/.skeleton/$db" ]; then
+              cp "${cfg.package}/usr/lib/plexmediaserver/Resources/base_$db" "${cfg.dataDir}/.skeleton/$db"
+            fi
             chmod u+w "${cfg.dataDir}/.skeleton/$db"
             chown ${cfg.user}:${cfg.group} "${cfg.dataDir}/.skeleton/$db"
         done
@@ -136,6 +138,7 @@ in
         Group = cfg.group;
         PermissionsStartOnly = "true";
         ExecStart = "/bin/sh -c ${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server";
+        KillSignal = "SIGQUIT";
         Restart = "on-failure";
       };
       environment = {
diff --git a/nixos/modules/services/misc/radarr.nix b/nixos/modules/services/misc/radarr.nix
new file mode 100644
index 000000000000..245ad9f9a6df
--- /dev/null
+++ b/nixos/modules/services/misc/radarr.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, lib, mono, ... }:
+
+with lib;
+
+let
+  cfg = config.services.radarr;
+in
+{
+  options = {
+    services.radarr = {
+      enable = mkEnableOption "Radarr";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.radarr = {
+      description = "Radarr";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        test -d /var/lib/radarr/ || {
+          echo "Creating radarr data directory in /var/lib/radarr/"
+          mkdir -p /var/lib/radarr/
+        }
+        chown -R radarr:radarr /var/lib/radarr/
+        chmod 0700 /var/lib/radarr/
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        User = "radarr";
+        Group = "radarr";
+        PermissionsStartOnly = "true";
+        ExecStart = "${pkgs.radarr}/bin/Radarr";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers.radarr = {
+      uid = config.ids.uids.radarr;
+      home = "/var/lib/radarr";
+      group = "radarr";
+    };
+    users.extraGroups.radarr.gid = config.ids.gids.radarr;
+
+  };
+}
diff --git a/nixos/modules/services/misc/sonarr.nix b/nixos/modules/services/misc/sonarr.nix
index 6d96daa6c3d4..ecde2c33bfa9 100644
--- a/nixos/modules/services/misc/sonarr.nix
+++ b/nixos/modules/services/misc/sonarr.nix
@@ -22,14 +22,14 @@ in
           echo "Creating sonarr data directory in /var/lib/sonarr/"
           mkdir -p /var/lib/sonarr/
         }
-        chown -R sonarr /var/lib/sonarr/
+        chown -R sonarr:sonarr /var/lib/sonarr/
         chmod 0700 /var/lib/sonarr/
       '';
 
       serviceConfig = {
         Type = "simple";
         User = "sonarr";
-        Group = "nogroup";
+        Group = "sonarr";
         PermissionsStartOnly = "true";
         ExecStart = "${pkgs.sonarr}/bin/NzbDrone --no-browser";
         Restart = "on-failure";
@@ -37,8 +37,11 @@ in
     };
 
     users.extraUsers.sonarr = {
+      uid = config.ids.uids.sonarr;
       home = "/var/lib/sonarr";
+      group = "sonarr";
     };
+    users.extraGroups.sonarr.gid = config.ids.gids.sonarr;
 
   };
 }
diff --git a/nixos/modules/services/misc/ssm-agent.nix b/nixos/modules/services/misc/ssm-agent.nix
new file mode 100644
index 000000000000..c1e1f0903539
--- /dev/null
+++ b/nixos/modules/services/misc/ssm-agent.nix
@@ -0,0 +1,46 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.services.ssm-agent;
+
+  # The SSM agent doesn't pay attention to our /etc/os-release yet, and the lsb-release tool
+  # 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}
+
+    case "$1" in
+      -i) echo "nixos";;
+      -r) echo "${config.system.nixosVersion}";;
+    esac
+  '';
+in {
+  options.services.ssm-agent = {
+    enable = mkEnableOption "AWS SSM agent";
+
+    package = mkOption {
+      type = types.path;
+      description = "The SSM agent package to use";
+      default = pkgs.ssm-agent;
+      defaultText = "pkgs.ssm-agent";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.ssm-agent = {
+      inherit (cfg.package.meta) description;
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ fake-lsb-release ];
+      serviceConfig = {
+        ExecStart = "${cfg.package.bin}/bin/agent";
+        KillMode = "process";
+        Restart = "on-failure";
+        RestartSec = "15min";
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index ca82a733f6fc..ab9a6ce32f46 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -94,44 +94,6 @@ let
     in flatten (mapAttrsToList mkSublist attrs);
   in all isNull (findPkiDefinitions [] manualPkiOptions);
 
-  configFile = pkgs.writeText "taskdrc" (''
-    # systemd related
-    daemon = false
-    log = -
-
-    # logging
-    ${mkConfLine "debug" cfg.debug}
-    ${mkConfLine "ip.log" cfg.ipLog}
-
-    # general
-    ${mkConfLine "ciphers" cfg.ciphers}
-    ${mkConfLine "confirmation" cfg.confirmation}
-    ${mkConfLine "extensions" cfg.extensions}
-    ${mkConfLine "queue.size" cfg.queueSize}
-    ${mkConfLine "request.limit" cfg.requestLimit}
-
-    # client
-    ${mkConfLine "client.allow" cfg.allowedClientIDs}
-    ${mkConfLine "client.deny" cfg.disallowedClientIDs}
-
-    # server
-    server = ${cfg.listenHost}:${toString cfg.listenPort}
-    ${mkConfLine "trust" cfg.trust}
-
-    # PKI options
-    ${if needToCreateCA then ''
-      ca.cert = ${cfg.dataDir}/keys/ca.cert
-      server.cert = ${cfg.dataDir}/keys/server.cert
-      server.key = ${cfg.dataDir}/keys/server.key
-      server.crl = ${cfg.dataDir}/keys/server.crl
-    '' else ''
-      ca.cert = ${cfg.pki.ca.cert}
-      server.cert = ${cfg.pki.server.cert}
-      server.key = ${cfg.pki.server.key}
-      server.crl = ${cfg.pki.server.crl}
-    ''}
-  '' + cfg.extraConfig);
-
   orgOptions = { name, ... }: {
     options.users = mkOption {
       type = types.uniq (types.listOf types.str);
@@ -154,9 +116,8 @@ let
 
   certtool = "${pkgs.gnutls.bin}/bin/certtool";
 
-  nixos-taskserver = pkgs.pythonPackages.buildPythonPackage {
+  nixos-taskserver = pkgs.pythonPackages.buildPythonApplication {
     name = "nixos-taskserver";
-    namePrefix = "";
 
     src = pkgs.runCommand "nixos-taskserver-src" {} ''
       mkdir -p "$out"
@@ -167,6 +128,7 @@ let
         certBits = cfg.pki.auto.bits;
         clientExpiration = cfg.pki.auto.expiration.client;
         crlExpiration = cfg.pki.auto.expiration.crl;
+        isAutoConfig = boolToString needToCreateCA;
       }}" > "$out/main.py"
       cat > "$out/setup.py" <<EOF
       from setuptools import setup
@@ -186,7 +148,6 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Whether to enable the Taskwarrior server.
 
@@ -365,20 +326,57 @@ in {
       pki.manual = manualPkiOptions;
       pki.auto = autoPkiOptions;
 
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        example = "client.cert = /tmp/debugging.cert";
+      config = mkOption {
+        type = types.attrs;
+        example.client.cert = "/tmp/debugging.cert";
         description = ''
-          Extra lines to append to the taskdrc configuration file.
+          Configuration options to pass to Taskserver.
+
+          The options here are the same as described in <citerefentry>
+            <refentrytitle>taskdrc</refentrytitle>
+            <manvolnum>5</manvolnum>
+          </citerefentry>, but with one difference:
+
+          The <literal>server</literal> option is
+          <literal>server.listen</literal> here, because the
+          <literal>server</literal> option would collide with other options
+          like <literal>server.cert</literal> and we would run in a type error
+          (attribute set versus string).
+
+          Nix types like integers or booleans are automatically converted to
+          the right values Taskserver would expect.
         '';
+        apply = let
+          mkKey = path: if path == ["server" "listen"] then "server"
+                        else concatStringsSep "." path;
+          recurse = path: attrs: let
+            mapper = name: val: let
+              newPath = path ++ [ name ];
+              scalar = if val == true then "true"
+                       else if val == false then "false"
+                       else toString val;
+            in if isAttrs val then recurse newPath val
+               else [ "${mkKey newPath}=${scalar}" ];
+          in concatLists (mapAttrsToList mapper attrs);
+        in recurse [];
       };
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule ["services" "taskserver" "extraConfig"] ''
+      This option was removed in favor of `services.taskserver.config` with
+      different semantics (it's now a list of attributes instead of lines).
+
+      Please look up the documentation of `services.taskserver.config' to get
+      more information about the new way to pass additional configuration
+      options.
+    '')
+  ];
+
   config = mkMerge [
     (mkIf cfg.enable {
-      environment.systemPackages = [ pkgs.taskserver nixos-taskserver ];
+      environment.systemPackages = [ nixos-taskserver ];
 
       users.users = optional (cfg.user == "taskd") {
         name = "taskd";
@@ -392,6 +390,44 @@ in {
         gid = config.ids.gids.taskd;
       };
 
+      services.taskserver.config = {
+        # systemd related
+        daemon = false;
+        log = "-";
+
+        # logging
+        debug = cfg.debug;
+        ip.log = cfg.ipLog;
+
+        # general
+        ciphers = cfg.ciphers;
+        confirmation = cfg.confirmation;
+        extensions = cfg.extensions;
+        queue.size = cfg.queueSize;
+        request.limit = cfg.requestLimit;
+
+        # client
+        client.allow = cfg.allowedClientIDs;
+        client.deny = cfg.disallowedClientIDs;
+
+        # server
+        trust = cfg.trust;
+        server = {
+          listen = "${cfg.listenHost}:${toString cfg.listenPort}";
+        } // (if needToCreateCA then {
+          cert = "${cfg.dataDir}/keys/server.cert";
+          key = "${cfg.dataDir}/keys/server.key";
+          crl = "${cfg.dataDir}/keys/server.crl";
+        } else {
+          cert = "${cfg.pki.manual.server.cert}";
+          key = "${cfg.pki.manual.server.key}";
+          crl = "${cfg.pki.manual.server.crl}";
+        });
+
+        ca.cert = if needToCreateCA then "${cfg.dataDir}/keys/ca.cert"
+                  else "${cfg.pki.manual.ca.cert}";
+      };
+
       systemd.services.taskserver-init = {
         wantedBy = [ "taskserver.service" ];
         before = [ "taskserver.service" ];
@@ -404,7 +440,6 @@ in {
 
         script = ''
           ${taskd} init
-          echo "include ${configFile}" > "${cfg.dataDir}/config"
           touch "${cfg.dataDir}/.is_initialized"
         '';
 
@@ -436,7 +471,10 @@ in {
         in "${helperTool} process-json '${jsonFile}'";
 
         serviceConfig = {
-          ExecStart = "@${taskd} taskd server";
+          ExecStart = let
+            mkCfgFlag = flag: escapeShellArg "--${flag}";
+            cfgFlags = concatMapStringsSep " " mkCfgFlag cfg.config;
+          in "@${taskd} taskd server ${cfgFlags}";
           ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID";
           Restart = "on-failure";
           PermissionsStartOnly = true;
diff --git a/nixos/modules/services/misc/taskserver/doc.xml b/nixos/modules/services/misc/taskserver/doc.xml
index 48591129264a..6d4d2a9b488c 100644
--- a/nixos/modules/services/misc/taskserver/doc.xml
+++ b/nixos/modules/services/misc/taskserver/doc.xml
@@ -136,9 +136,9 @@ $ ssh server nixos-taskserver user export my-company alice | sh
 
     <para>
       If you set any options within
-      <option>service.taskserver.pki.manual.*</option>, the automatic user and
-      CA management by the <command>nixos-taskserver</command> is disabled and
-      you need to create certificates and keys by yourself.
+      <option>service.taskserver.pki.manual.*</option>,
+      <command>nixos-taskserver</command> won't issue certificates, but you can
+      still use it for adding or removing user accounts.
     </para>
   </section>
 </chapter>
diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py
index 03e7cdf8987a..b97bc1df74f7 100644
--- a/nixos/modules/services/misc/taskserver/helper-tool.py
+++ b/nixos/modules/services/misc/taskserver/helper-tool.py
@@ -13,6 +13,7 @@ from tempfile import NamedTemporaryFile
 
 import click
 
+IS_AUTO_CONFIG = @isAutoConfig@ # NOQA
 CERTTOOL_COMMAND = "@certtool@"
 CERT_BITS = "@certBits@"
 CLIENT_EXPIRATION = "@clientExpiration@"
@@ -149,6 +150,12 @@ def create_template(contents):
 
 
 def generate_key(org, user):
+    if not IS_AUTO_CONFIG:
+        msg = "Automatic PKI handling is disabled, you need to " \
+              "manually issue a client certificate for user {}.\n"
+        sys.stderr.write(msg.format(user))
+        return
+
     basedir = os.path.join(TASKD_DATA_DIR, "keys", org, user)
     if os.path.exists(basedir):
         raise OSError("Keyfile directory for {} already exists.".format(user))
@@ -243,26 +250,32 @@ class User(object):
         self.key = key
 
     def export(self):
-        pubcert = getkey(self.__org, self.name, "public.cert")
-        privkey = getkey(self.__org, self.name, "private.key")
-        cacert = getkey("ca.cert")
-
-        keydir = "${TASKDATA:-$HOME/.task}/keys"
-
         credentials = '/'.join([self.__org, self.name, self.key])
         allow_unquoted = string.ascii_letters + string.digits + "/-_."
         if not all((c in allow_unquoted) for c in credentials):
             credentials = "'" + credentials.replace("'", r"'\''") + "'"
 
-        script = [
-            "umask 0077",
-            'mkdir -p "{}"'.format(keydir),
-            mktaskkey("certificate", os.path.join(keydir, "public.cert"),
-                      pubcert),
-            mktaskkey("key", os.path.join(keydir, "private.key"), privkey),
-            mktaskkey("ca", os.path.join(keydir, "ca.cert"), cacert),
+        script = []
+
+        if IS_AUTO_CONFIG:
+            pubcert = getkey(self.__org, self.name, "public.cert")
+            privkey = getkey(self.__org, self.name, "private.key")
+            cacert = getkey("ca.cert")
+
+            keydir = "${TASKDATA:-$HOME/.task}/keys"
+
+            script += [
+                "umask 0077",
+                'mkdir -p "{}"'.format(keydir),
+                mktaskkey("certificate", os.path.join(keydir, "public.cert"),
+                          pubcert),
+                mktaskkey("key", os.path.join(keydir, "private.key"), privkey),
+                mktaskkey("ca", os.path.join(keydir, "ca.cert"), cacert)
+            ]
+
+        script.append(
             "task config taskd.credentials -- {}".format(credentials)
-        ]
+        )
 
         return "\n".join(script) + "\n"
 
@@ -526,7 +539,7 @@ def export_user(organisation, user):
     userobj = organisation.get_user(user)
     if userobj is None:
         msg = "User {} doesn't exist in organisation {}."
-        sys.exit(msg.format(userobj.name, organisation.name))
+        sys.exit(msg.format(user, organisation.name))
 
     sys.stdout.write(userobj.export())
 
diff --git a/nixos/modules/services/monitoring/arbtt.nix b/nixos/modules/services/monitoring/arbtt.nix
index 27d59e367d5c..a8d5e3b7fa07 100644
--- a/nixos/modules/services/monitoring/arbtt.nix
+++ b/nixos/modules/services/monitoring/arbtt.nix
@@ -10,7 +10,6 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Enable the arbtt statistics capture service.
         '';
@@ -49,7 +48,7 @@ in {
   config = mkIf cfg.enable {
     systemd.user.services.arbtt = {
       description = "arbtt statistics capture service";
-      wantedBy = [ "multi-user.target" ];
+      wantedBy = [ "default.target" ];
 
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/monitoring/cadvisor.nix b/nixos/modules/services/monitoring/cadvisor.nix
index 8ae8b12056ce..6ca420a05b23 100644
--- a/nixos/modules/services/monitoring/cadvisor.nix
+++ b/nixos/modules/services/monitoring/cadvisor.nix
@@ -54,7 +54,29 @@ in {
       storageDriverPassword = mkOption {
         default = "root";
         type = types.str;
-        description = "Cadvisor storage driver password.";
+        description = ''
+          Cadvisor storage driver password.
+
+          Warning: this password is stored in the world-readable Nix store. It's
+          recommended to use the <option>storageDriverPasswordFile</option> option
+          since that gives you control over the security of the password.
+          <option>storageDriverPasswordFile</option> also takes precedence over <option>storageDriverPassword</option>.
+        '';
+      };
+
+      storageDriverPasswordFile = mkOption {
+        type = types.str;
+        description = ''
+          File that contains the cadvisor storage driver password.
+
+          <option>storageDriverPasswordFile</option> takes precedence over <option>storageDriverPassword</option>
+
+          Warning: when <option>storageDriverPassword</option> is non-empty this defaults to a file in the
+          world-readable Nix store that contains the value of <option>storageDriverPassword</option>.
+
+          It's recommended to override this with a path not in the Nix store.
+          Tip: use <link xlink:href='https://nixos.org/nixops/manual/#idm140737318306400'>nixops key management</link>
+        '';
       };
 
       storageDriverSecure = mkOption {
@@ -65,35 +87,44 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
-    systemd.services.cadvisor = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" "docker.service" "influxdb.service" ];
-
-      postStart = mkBefore ''
-        until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/containers/'; do
-          sleep 1;
-        done
-      '';
-
-      serviceConfig = {
-        ExecStart = ''${pkgs.cadvisor}/bin/cadvisor \
-          -logtostderr=true \
-          -listen_ip=${cfg.listenAddress} \
-          -port=${toString cfg.port} \
-          ${optionalString (cfg.storageDriver != null) ''
-            -storage_driver ${cfg.storageDriver} \
-            -storage_driver_user ${cfg.storageDriverHost} \
-            -storage_driver_db ${cfg.storageDriverDb} \
-            -storage_driver_user ${cfg.storageDriverUser} \
-            -storage_driver_password ${cfg.storageDriverPassword} \
-            ${optionalString cfg.storageDriverSecure "-storage_driver_secure"}
-          ''}
+  config = mkMerge [
+    { services.cadvisor.storageDriverPasswordFile = mkIf (cfg.storageDriverPassword != "") (
+        mkDefault (toString (pkgs.writeTextFile {
+          name = "cadvisor-storage-driver-password";
+          text = cfg.storageDriverPassword;
+        }))
+      );
+    }
+
+    (mkIf cfg.enable {
+      systemd.services.cadvisor = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" "docker.service" "influxdb.service" ];
+
+        postStart = mkBefore ''
+          until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/containers/'; do
+            sleep 1;
+          done
         '';
-        TimeoutStartSec=300;
-      };
-    };
 
-    virtualisation.docker.enable = mkDefault true;
-  };
+        script = ''
+          exec ${pkgs.cadvisor}/bin/cadvisor \
+            -logtostderr=true \
+            -listen_ip="${cfg.listenAddress}" \
+            -port="${toString cfg.port}" \
+            ${optionalString (cfg.storageDriver != null) ''
+              -storage_driver "${cfg.storageDriver}" \
+              -storage_driver_user "${cfg.storageDriverHost}" \
+              -storage_driver_db "${cfg.storageDriverDb}" \
+              -storage_driver_user "${cfg.storageDriverUser}" \
+              -storage_driver_password "$(cat "${cfg.storageDriverPasswordFile}")" \
+              ${optionalString cfg.storageDriverSecure "-storage_driver_secure"}
+            ''}
+        '';
+
+        serviceConfig.TimeoutStartSec=300;
+      };
+      virtualisation.docker.enable = mkDefault true;
+    })
+  ];
 }
diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix
index 641da60e9adc..79a8a1ff5aed 100644
--- a/nixos/modules/services/monitoring/collectd.nix
+++ b/nixos/modules/services/monitoring/collectd.nix
@@ -8,7 +8,7 @@ let
   conf = pkgs.writeText "collectd.conf" ''
     BaseDir "${cfg.dataDir}"
     PIDFile "${cfg.pidFile}"
-    AutoLoadPlugin ${if cfg.autoLoadPlugin then "true" else "false"}
+    AutoLoadPlugin ${boolToString cfg.autoLoadPlugin}
     Hostname "${config.networking.hostName}"
 
     LoadPlugin syslog
diff --git a/nixos/modules/services/monitoring/das_watchdog.nix b/nixos/modules/services/monitoring/das_watchdog.nix
index 6e2653836d5e..88ca3a9227d2 100644
--- a/nixos/modules/services/monitoring/das_watchdog.nix
+++ b/nixos/modules/services/monitoring/das_watchdog.nix
@@ -25,7 +25,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         User = "root";
-        Type = "oneshot";
+        Type = "simple";
         ExecStart = "${das_watchdog}/bin/das_watchdog";
         RemainAfterExit = true;
       };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index b9e4015c2380..4fbacef788f9 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -1,12 +1,10 @@
-{ config, lib, pkgs, ... }:
+{ options, config, lib, pkgs, ... }:
 
 with lib;
 
 let
   cfg = config.services.grafana;
 
-  b2s = val: if val then "true" else "false";
-
   envOptions = {
     PATHS_DATA = cfg.dataDir;
     PATHS_PLUGINS = "${cfg.dataDir}/plugins";
@@ -32,16 +30,16 @@ let
     SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword;
     SECURITY_SECRET_KEY = cfg.security.secretKey;
 
-    USERS_ALLOW_SIGN_UP = b2s cfg.users.allowSignUp;
-    USERS_ALLOW_ORG_CREATE = b2s cfg.users.allowOrgCreate;
-    USERS_AUTO_ASSIGN_ORG = b2s cfg.users.autoAssignOrg;
+    USERS_ALLOW_SIGN_UP = boolToString cfg.users.allowSignUp;
+    USERS_ALLOW_ORG_CREATE = boolToString cfg.users.allowOrgCreate;
+    USERS_AUTO_ASSIGN_ORG = boolToString cfg.users.autoAssignOrg;
     USERS_AUTO_ASSIGN_ORG_ROLE = cfg.users.autoAssignOrgRole;
 
-    AUTH_ANONYMOUS_ENABLED = b2s cfg.auth.anonymous.enable;
+    AUTH_ANONYMOUS_ENABLED = boolToString cfg.auth.anonymous.enable;
     AUTH_ANONYMOUS_ORG_NAME = cfg.auth.anonymous.org_name;
     AUTH_ANONYMOUS_ORG_ROLE = cfg.auth.anonymous.org_role;
 
-    ANALYTICS_REPORTING_ENABLED = b2s cfg.analytics.reporting.enable;
+    ANALYTICS_REPORTING_ENABLED = boolToString cfg.analytics.reporting.enable;
   } // cfg.extraOptions;
 
 in {
@@ -232,9 +230,10 @@ in {
   };
 
   config = mkIf cfg.enable {
-    warnings = [
-      "Grafana passwords will be stored as plaintext in the Nix store!"
-    ];
+    warnings = optional (
+      cfg.database.password != options.services.grafana.database.password.default ||
+      cfg.security.adminPassword != options.services.grafana.security.adminPassword.default
+    ) "Grafana passwords will be stored as plaintext in the Nix store!";
 
     environment.systemPackages = [ cfg.package ];
 
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index c5352e5887da..6b24ac2c7c62 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.graphite;
-  writeTextOrNull = f: t: if t == null then null else pkgs.writeTextDir f t;
+  writeTextOrNull = f: t: mapNullable (pkgs.writeTextDir f) t;
 
   dataDir = cfg.dataDir;
 
@@ -400,7 +400,8 @@ in {
 
           mkdir -p ${cfg.dataDir}/whisper
           chmod 0700 ${cfg.dataDir}/whisper
-          chown -R graphite:graphite ${cfg.dataDir}
+          chown graphite:graphite ${cfg.dataDir}
+          chown graphite:graphite ${cfg.dataDir}/whisper
         '';
       };
     })
@@ -487,9 +488,11 @@ in {
             # create index
             ${pkgs.python27Packages.graphite_web}/bin/build-index.sh
 
-            touch ${dataDir}/db-created
+            chown graphite:graphite ${cfg.dataDir}
+            chown graphite:graphite ${cfg.dataDir}/whisper
+            chown -R graphite:graphite ${cfg.dataDir}/log
 
-            chown -R graphite:graphite ${cfg.dataDir}
+            touch ${dataDir}/db-created
           fi
         '';
       };
@@ -526,9 +529,10 @@ in {
             mkdir -p ${dataDir}/cache/
             chmod 0700 ${dataDir}/cache/
 
-            touch ${dataDir}/db-created
+            chown graphite:graphite ${cfg.dataDir}
+            chown -R graphite:graphite ${cfg.dataDir}/cache
 
-            chown -R graphite:graphite ${cfg.dataDir}
+            touch ${dataDir}/db-created
           fi
         '';
       };
@@ -549,7 +553,7 @@ in {
         preStart = ''
           if ! test -e ${dataDir}/db-created; then
             mkdir -p ${dataDir}
-            chown -R graphite:graphite ${dataDir}
+            chown graphite:graphite ${dataDir}
           fi
         '';
       };
diff --git a/nixos/modules/services/monitoring/longview.nix b/nixos/modules/services/monitoring/longview.nix
index 770d56e60efb..9c38956f9ba8 100644
--- a/nixos/modules/services/monitoring/longview.nix
+++ b/nixos/modules/services/monitoring/longview.nix
@@ -5,22 +5,10 @@ with lib;
 let
   cfg = config.services.longview;
 
-  pidFile = "/run/longview.pid";
-
-  apacheConf = optionalString (cfg.apacheStatusUrl != "") ''
-    location ${cfg.apacheStatusUrl}?auto
-  '';
-  mysqlConf = optionalString (cfg.mysqlUser != "") ''
-    username ${cfg.mysqlUser}
-    password ${cfg.mysqlPassword}
-  '';
-  nginxConf = optionalString (cfg.nginxStatusUrl != "") ''
-    location ${cfg.nginxStatusUrl}
-  '';
-
-in
-
-{
+  runDir = "/run/longview";
+  configsDir = "${runDir}/longview.d";
+
+in {
   options = {
 
     services.longview = {
@@ -35,10 +23,27 @@ in
 
       apiKey = mkOption {
         type = types.str;
+        default = "";
         example = "01234567-89AB-CDEF-0123456789ABCDEF";
         description = ''
           Longview API key. To get this, look in Longview settings which
           are found at https://manager.linode.com/longview/.
+
+          Warning: this secret is stored in the world-readable Nix store!
+          Use <option>apiKeyFile</option> instead.
+        '';
+      };
+
+      apiKeyFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/longview-api-key";
+        description = ''
+          A file containing the Longview API key.
+          To get this, look in Longview settings which
+          are found at https://manager.linode.com/longview/.
+
+          <option>apiKeyFile</option> takes precedence over <option>apiKey</option>.
         '';
       };
 
@@ -77,11 +82,23 @@ in
 
       mysqlPassword = mkOption {
         type = types.str;
+        default = "";
         description = ''
-          The password corresponding to mysqlUser.  Warning: this is
-          stored in cleartext in the Nix store!
+          The password corresponding to <option>mysqlUser</option>.
+          Warning: this is stored in cleartext in the Nix store!
+          Use <option>mysqlPasswordFile</option> instead.
         '';
       };
+
+      mysqlPasswordFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/dbpassword";
+        description = ''
+          A file containing the password corresponding to <option>mysqlUser</option>.
+        '';
+      };
+
     };
 
   };
@@ -94,25 +111,50 @@ in
         serviceConfig.Type = "forking";
         serviceConfig.ExecStop = "-${pkgs.coreutils}/bin/kill -TERM $MAINPID";
         serviceConfig.ExecReload = "-${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-        serviceConfig.PIDFile = pidFile;
+        serviceConfig.PIDFile = "${runDir}/longview.pid";
         serviceConfig.ExecStart = "${pkgs.longview}/bin/longview";
+        preStart = ''
+          umask 077
+          mkdir -p ${configsDir}
+        '' + (optionalString (cfg.apiKeyFile != null) ''
+          cp --no-preserve=all "${cfg.apiKeyFile}" ${runDir}/longview.key
+        '') + (optionalString (cfg.apacheStatusUrl != "") ''
+          cat > ${configsDir}/Apache.conf <<EOF
+          location ${cfg.apacheStatusUrl}?auto
+          EOF
+        '') + (optionalString (cfg.mysqlUser != "" && cfg.mysqlPasswordFile != null) ''
+          cat > ${configsDir}/MySQL.conf <<EOF
+          username ${cfg.mysqlUser}
+          password `head -n1 "${cfg.mysqlPasswordFile}"`
+          EOF
+        '') + (optionalString (cfg.nginxStatusUrl != "") ''
+          cat > ${configsDir}/Nginx.conf <<EOF
+          location ${cfg.nginxStatusUrl}
+          EOF
+        '');
       };
 
-    environment.etc."linode/longview.key" = {
-      mode = "0400";
-      text = cfg.apiKey;
-    };
-    environment.etc."linode/longview.d/Apache.conf" = {
-      mode = "0400";
-      text = apacheConf;
-    };
-    environment.etc."linode/longview.d/MySQL.conf" = {
-      mode = "0400";
-      text = mysqlConf;
-    };
-    environment.etc."linode/longview.d/Nginx.conf" = {
-      mode = "0400";
-      text = nginxConf;
-    };
+    warnings = let warn = k: optional (cfg.${k} != "")
+                 "config.services.longview.${k} is insecure. Use ${k}File instead.";
+               in concatMap warn [ "apiKey" "mysqlPassword" ];
+
+    assertions = [
+      { assertion = cfg.apiKeyFile != null;
+        message = "Longview needs an API key configured";
+      }
+    ];
+
+    # Create API key file if not configured.
+    services.longview.apiKeyFile = mkIf (cfg.apiKey != "")
+      (mkDefault (toString (pkgs.writeTextFile {
+        name = "longview.key";
+        text = cfg.apiKey;
+      })));
+
+    # Create MySQL password file if not configured.
+    services.longview.mysqlPasswordFile = mkDefault (toString (pkgs.writeTextFile {
+      name = "mysql-password-file";
+      text = cfg.mysqlPassword;
+    }));
   };
 }
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 57df16b58d9c..b26bcba64059 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -34,7 +34,7 @@ let
         cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file)
 
         wrapProgram $file \
-          --set PATH "/var/setuid-wrappers:/run/current-system/sw/bin:/run/current-system/sw/bin" \
+          --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \
           --set MUNIN_LIBDIR "${pkgs.munin}/lib" \
           --set MUNIN_PLUGSTATE "/var/run/munin"
 
@@ -76,6 +76,7 @@ let
       # wrapped plugins by makeWrapper being with dots
       ignore_file ^\.
 
+      allow ^::1$
       allow ^127\.0\.0\.1$
 
       ${nodeCfg.extraConfig}
@@ -183,7 +184,7 @@ in
 
         mkdir -p /etc/munin/plugins
         rm -rf /etc/munin/plugins/*
-        PATH="/var/setuid-wrappers:/run/current-system/sw/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
+        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
       '';
       serviceConfig = {
         ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
@@ -192,14 +193,26 @@ in
 
   }) (mkIf cronCfg.enable {
 
-    services.cron.systemCronJobs = [
-      "*/5 * * * * munin ${pkgs.munin}/bin/munin-cron --config ${muninConf}"
-    ];
+    systemd.timers.munin-cron = {
+      description = "batch Munin master programs";
+      wantedBy = [ "timers.target" ];
+      timerConfig.OnCalendar = "*:0/5";
+    };
+
+    systemd.services.munin-cron = {
+      description = "batch Munin master programs";
+      unitConfig.Documentation = "man:munin-cron(8)";
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "munin";
+        ExecStart = "${pkgs.munin}/bin/munin-cron --config ${muninConf}";
+      };
+    };
 
     system.activationScripts.munin-cron = stringAfter [ "users" "groups" ] ''
       mkdir -p /var/{run,log,www,lib}/munin
       chown -R munin:munin /var/{run,log,www,lib}/munin
     '';
-
   })];
 }
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
new file mode 100644
index 000000000000..e1fde4fc9500
--- /dev/null
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.netdata;
+
+  configFile = pkgs.writeText "netdata.conf" cfg.configText;
+
+  defaultUser = "netdata";
+
+in {
+  options = {
+    services.netdata = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable netdata monitoring.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "netdata";
+        description = "User account under which netdata runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "netdata";
+        description = "Group under which netdata runs.";
+      };
+
+      configText = mkOption {
+        type = types.lines;
+        default = "";
+        description = "netdata.conf configuration.";
+        example = ''
+          [global]
+          debug log = syslog
+          access log = syslog
+          error log = syslog
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.netdata = {
+      description = "Real time performance monitoring";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = concatStringsSep "\n" (map (dir: ''
+        mkdir -vp ${dir}
+        chmod 750 ${dir}
+        chown -R ${cfg.user}:${cfg.group} ${dir}
+        '') [ "/var/cache/netdata"
+              "/var/log/netdata"
+              "/var/lib/netdata" ]);
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PermissionsStartOnly = true;
+        ExecStart = "${pkgs.netdata}/bin/netdata -D -c ${configFile}";
+        TimeoutStopSec = 60;
+      };
+    };
+
+    users.extraUsers = optional (cfg.user == defaultUser) {
+      name = defaultUser;
+    };
+
+    users.extraGroups = optional (cfg.group == defaultUser) {
+      name = defaultUser;
+    };
+
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
index 7a343299c315..388e4d4ac01d 100644
--- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
+++ b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
@@ -54,6 +54,7 @@ in {
         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} \
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index a07445ce167c..b9435c02b1de 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -116,6 +116,13 @@ let
           The URL scheme with which to fetch metrics from targets.
         '';
       };
+      params = mkOption {
+        type = types.attrsOf (types.listOf types.str);
+        default = {};
+        description = ''
+          Optional HTTP URL parameters.
+        '';
+      };
       basic_auth = mkOption {
         type = types.nullOr (types.submodule {
           options = {
@@ -134,6 +141,7 @@ let
           };
         });
         default = null;
+        apply = x: mapNullable _filter x;
         description = ''
           Optional http login credentials for metrics scraping.
         '';
diff --git a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
new file mode 100644
index 000000000000..6da39b6519cb
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
@@ -0,0 +1,76 @@
+{ 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/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
new file mode 100644
index 000000000000..e3059e485098
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
@@ -0,0 +1,104 @@
+{ 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" ];
+      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/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index f2834f288f90..4d10299a9879 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -124,7 +124,7 @@ in
           };
 
           mailer = mkOption {
-            default = "/var/setuid-wrappers/sendmail";
+            default = "/run/wrappers/bin/sendmail";
             type = types.path;
             description = ''
               Sendmail-compatible binary to be used to send the messages.
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index c4c4ed227b35..29dc68f90cc9 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -80,7 +80,7 @@ let
     };
 
     config = {
-      directives = mkHeader ([
+      directives = mkOrder 10 ([
         "driver = ${config.driver}"
         "port = ${config.port}"
         ''desc = "${config.description}"''
diff --git a/nixos/modules/services/monitoring/vnstat.nix b/nixos/modules/services/monitoring/vnstat.nix
new file mode 100644
index 000000000000..ca56e4a7b958
--- /dev/null
+++ b/nixos/modules/services/monitoring/vnstat.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.vnstat;
+in {
+  options.services.vnstat = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable update of network usage statistics via vnstatd.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.vnstatd = {
+      isSystemUser = true;
+      description = "vnstat daemon user";
+      home = "/var/lib/vnstat";
+      createHome = true;
+    };
+
+    systemd.services.vnstat = {
+      description = "vnStat network traffic monitor";
+      path = [ pkgs.coreutils ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      unitConfig.documentation = "man:vnstatd(1) man:vnstat(1) man:vnstat.conf(5)";
+      preStart = "chmod 755 /var/lib/vnstat";
+      serviceConfig = {
+        ExecStart = "${pkgs.vnstat}/bin/vnstatd -n";
+        ExecReload = "${pkgs.procps}/bin/kill -HUP $MAINPID";
+        ProtectHome = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        User = "vnstatd";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/glusterfs.nix b/nixos/modules/services/network-filesystems/glusterfs.nix
new file mode 100644
index 000000000000..a2f2c0339515
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/glusterfs.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (pkgs) glusterfs;
+
+  cfg = config.services.glusterfs;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.glusterfs = {
+
+      enable = mkEnableOption "GlusterFS Daemon";
+
+      logLevel = mkOption {
+        type = types.enum ["DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL" "TRACE" "NONE"];
+        description = "Log level used by the GlusterFS daemon";
+        default = "INFO";
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        description = "Extra flags passed to the GlusterFS daemon";
+        default = [];
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.glusterfs ];
+
+    services.rpcbind.enable = true;
+
+    systemd.services.glusterd = {
+
+      description = "GlusterFS, a clustered file-system server";
+
+      wantedBy = [ "multi-user.target" ];
+
+      requires = [ "rpcbind.service" ];
+      after = [ "rpcbind.service" "network.target" "local-fs.target" ];
+      before = [ "network-online.target" ];
+
+      preStart = ''
+        install -m 0755 -d /var/log/glusterfs
+      '';
+
+      serviceConfig = {
+        Type="forking";
+        PIDFile="/run/glusterd.pid";
+        LimitNOFILE=65536;
+        ExecStart="${glusterfs}/sbin/glusterd -p /run/glusterd.pid --log-level=${cfg.logLevel} ${toString cfg.extraFlags}";
+        KillMode="process";
+      };
+    };
+
+    systemd.services.glustereventsd = {
+
+      description = "Gluster Events Notifier";
+
+      wantedBy = [ "multi-user.target" ];
+
+      after = [ "syslog.target" "network.target" ];
+
+      serviceConfig = {
+        Type="simple";
+        Environment="PYTHONPATH=${glusterfs}/usr/lib/python2.7/site-packages";
+        PIDFile="/run/glustereventsd.pid";
+        ExecStart="${glusterfs}/sbin/glustereventsd --pid-file /run/glustereventsd.pid";
+        ExecReload="/bin/kill -SIGUSR2 $MAINPID";
+        KillMode="control-group";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index 104b5b92620e..e6e04248854e 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -67,6 +67,14 @@ in
         '';
       };
 
+      emptyRepo = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If set to true, the repo won't be initialized with help files
+        '';
+      };
+
       extraFlags = mkOption {
         type = types.listOf types.str;
         description = "Extra flags passed to the IPFS daemon";
@@ -96,29 +104,72 @@ in
       };
     };
 
+    systemd.services.ipfs-init = {
+      description = "IPFS Initializer";
+
+      after = [ "local-fs.target" ];
+      before = [ "ipfs.service" "ipfs-offline.service" ];
+
+      path  = [ pkgs.ipfs pkgs.su pkgs.bash ];
+
+      preStart = ''
+        install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.dataDir}
+      '';
+
+      script =  ''
+        if [[ ! -d ${cfg.dataDir}/.ipfs ]]; then
+          cd ${cfg.dataDir}
+          ${ipfs}/bin/ipfs init ${optionalString cfg.emptyRepo "-e"}
+        fi
+        ${ipfs}/bin/ipfs --local config Addresses.API ${cfg.apiAddress}
+        ${ipfs}/bin/ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "oneshot";
+        RemainAfterExit = true;
+        PermissionsStartOnly = true;
+      };
+    };
+
     systemd.services.ipfs = {
       description = "IPFS Daemon";
 
       wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" "local-fs.target" ];
-      path  = [ pkgs.ipfs pkgs.su pkgs.bash ];
+      after = [ "network.target" "local-fs.target" "ipfs-init.service" ];
 
-      preStart =
-        ''
-          install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.dataDir}
-          if [[ ! -d ${cfg.dataDir}/.ipfs ]]; then
-            cd ${cfg.dataDir}
-            ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs init"
-          fi
-          ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs config Addresses.API ${cfg.apiAddress}"
-          ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c "${ipfs}/bin/ipfs config Addresses.Gateway ${cfg.gatewayAddress}"
-        '';
+      conflicts = [ "ipfs-offline.service" ];
+      wants = [ "ipfs-init.service" ];
+
+      path  = [ pkgs.ipfs ];
 
       serviceConfig = {
         ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags}";
         User = cfg.user;
         Group = cfg.group;
-        PermissionsStartOnly = true;
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
+    };
+
+    systemd.services.ipfs-offline = {
+      description = "IPFS Daemon (offline mode)";
+
+      after = [ "local-fs.target" "ipfs-init.service" ];
+
+      conflicts = [ "ipfs.service" ];
+      wants = [ "ipfs-init.service" ];
+
+      path  = [ pkgs.ipfs ];
+
+      serviceConfig = {
+        ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags} --offline";
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 1;
       };
     };
   };
diff --git a/nixos/modules/services/network-filesystems/netatalk.nix b/nixos/modules/services/network-filesystems/netatalk.nix
index bff54406a2b0..1dd869043f0c 100644
--- a/nixos/modules/services/network-filesystems/netatalk.nix
+++ b/nixos/modules/services/network-filesystems/netatalk.nix
@@ -9,7 +9,7 @@ let
   extmapFile = pkgs.writeText "extmap.conf" cfg.extmap;
 
   afpToString = x: if builtins.typeOf x == "bool"
-                   then (if x then "true" else "false")
+                   then boolToString x
                    else toString x;
 
   volumeConfig = name:
diff --git a/nixos/modules/services/network-filesystems/nfsd.nix b/nixos/modules/services/network-filesystems/nfsd.nix
index ddc7258ce0b4..7d127145101b 100644
--- a/nixos/modules/services/network-filesystems/nfsd.nix
+++ b/nixos/modules/services/network-filesystems/nfsd.nix
@@ -20,6 +20,7 @@ in
 
       server = {
         enable = mkOption {
+          type = types.bool;
           default = false;
           description = ''
             Whether to enable the kernel's NFS server.
@@ -27,6 +28,7 @@ in
         };
 
         exports = mkOption {
+          type = types.lines;
           default = "";
           description = ''
             Contents of the /etc/exports file.  See
@@ -36,6 +38,7 @@ in
         };
 
         hostName = mkOption {
+          type = types.nullOr types.str;
           default = null;
           description = ''
             Hostname or address on which NFS requests will be accepted.
@@ -46,6 +49,7 @@ in
         };
 
         nproc = mkOption {
+          type = types.int;
           default = 8;
           description = ''
             Number of NFS server threads.  Defaults to the recommended value of 8.
@@ -53,11 +57,13 @@ in
         };
 
         createMountPoints = mkOption {
+          type = types.bool;
           default = false;
           description = "Whether to create the mount points in the exports file at startup time.";
         };
 
         mountdPort = mkOption {
+          type = types.nullOr types.int;
           default = null;
           example = 4002;
           description = ''
@@ -66,11 +72,26 @@ in
         };
 
         lockdPort = mkOption {
-          default = 0;
+          type = types.nullOr types.int;
+          default = null;
+          example = 4001;
+          description = ''
+            Use a fixed port for the NFS lock manager kernel module
+            (<literal>lockd/nlockmgr</literal>).  This is useful if the
+            NFS server is behind a firewall.
+          '';
+        };
+
+        statdPort = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          example = 4000;
           description = ''
-            Fix the lockd port number. This can help setting firewall rules for NFS.
+            Use a fixed port for <command>rpc.statd</command>. This is
+            useful if the NFS server is behind a firewall.
           '';
         };
+
       };
 
     };
@@ -82,60 +103,47 @@ in
 
   config = mkIf cfg.enable {
 
-    services.rpcbind.enable = true;
+    services.nfs.extraConfig = ''
+      [nfsd]
+      threads=${toString cfg.nproc}
+      ${optionalString (cfg.hostName != null) "host=${cfg.hostName}"}
 
-    boot.supportedFilesystems = [ "nfs" ]; # needed for statd and idmapd
-
-    environment.systemPackages = [ pkgs.nfs-utils ];
+      [mountd]
+      ${optionalString (cfg.mountdPort != null) "port=${toString cfg.mountdPort}"}
 
-    environment.etc.exports.source = exports;
+      [statd]
+      ${optionalString (cfg.statdPort != null) "port=${toString cfg.statdPort}"}
 
-    boot.kernelModules = [ "nfsd" ];
+      [lockd]
+      ${optionalString (cfg.lockdPort != null) ''
+        port=${toString cfg.lockdPort}
+        udp-port=${toString cfg.lockdPort}
+      ''}
+    '';
 
-    systemd.services.nfsd =
-      { description = "NFS Server";
+    services.rpcbind.enable = true;
 
-        wantedBy = [ "multi-user.target" ];
+    boot.supportedFilesystems = [ "nfs" ]; # needed for statd and idmapd
 
-        requires = [ "rpcbind.service" "mountd.service" ];
-        after = [ "rpcbind.service" "mountd.service" "idmapd.service" ];
-        before = [ "statd.service" ];
+    environment.etc.exports.source = exports;
 
-        path = [ pkgs.nfs-utils ];
+    systemd.services.nfs-server =
+      { enable = true;
+        wantedBy = [ "multi-user.target" ];
 
-        script =
+        preStart =
           ''
-            # Create a state directory required by NFSv4.
             mkdir -p /var/lib/nfs/v4recovery
-
-            ${pkgs.procps}/sbin/sysctl -w fs.nfs.nlm_tcpport=${builtins.toString cfg.lockdPort}
-            ${pkgs.procps}/sbin/sysctl -w fs.nfs.nlm_udpport=${builtins.toString cfg.lockdPort}
-
-            rpc.nfsd \
-              ${if cfg.hostName != null then "-H ${cfg.hostName}" else ""} \
-              ${builtins.toString cfg.nproc}
           '';
-
-        postStop = "rpc.nfsd 0";
-
-        serviceConfig.Type = "oneshot";
-        serviceConfig.RemainAfterExit = true;
       };
 
-    systemd.services.mountd =
-      { description = "NFSv3 Mount Daemon";
-
-        requires = [ "rpcbind.service" ];
-        after = [ "rpcbind.service" "local-fs.target" ];
-
-        path = [ pkgs.nfs-utils pkgs.sysvtools pkgs.utillinux ];
+    systemd.services.nfs-mountd =
+      { enable = true;
+        restartTriggers = [ exports ];
 
         preStart =
           ''
             mkdir -p /var/lib/nfs
-            touch /var/lib/nfs/rmtab
-
-            mountpoint -q /proc/fs/nfsd || mount -t nfsd none /proc/fs/nfsd
 
             ${optionalString cfg.createMountPoints
               ''
@@ -146,18 +154,7 @@ in
                 | xargs -d '\n' mkdir -p
               ''
             }
-
-            exportfs -rav
           '';
-
-        restartTriggers = [ exports ];
-
-        serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = ''
-          @${pkgs.nfs-utils}/sbin/rpc.mountd rpc.mountd \
-              ${if cfg.mountdPort != null then "-p ${toString cfg.mountdPort}" else ""}
-        '';
-        serviceConfig.Restart = "always";
       };
 
   };
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
index 6f51e287910a..f7c58c928633 100644
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix
@@ -76,6 +76,7 @@ in
       description = "AFS client";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
+      serviceConfig = { RemainAfterExit = true; };
 
       preStart = ''
         mkdir -p -m 0755 /afs
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 7de85b59e2af..87c4f7a8ebcb 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -5,7 +5,7 @@ with lib;
 let
 
   smbToString = x: if builtins.typeOf x == "bool"
-                   then (if x then "true" else "false")
+                   then boolToString x
                    else toString x;
 
   cfg = config.services.samba;
@@ -30,7 +30,7 @@ let
     ''
       [ global ]
       security = ${cfg.securityType}
-      passwd program = /var/setuid-wrappers/passwd %u
+      passwd program = /run/wrappers/bin/passwd %u
       pam password change = ${smbToString cfg.syncPasswordsByPam}
       invalid users = ${smbToString cfg.invalidUsers}
 
@@ -91,6 +91,26 @@ in
         '';
       };
 
+      enableNmbd = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable Samba's nmbd, which replies to NetBIOS over IP name
+          service requests. It also participates in the browsing protocols
+          which make up the Windows "Network Neighborhood" view.
+        '';
+      };
+
+      enableWinbindd = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable Samba's winbindd, which provides a number of services
+          to the Name Service Switch capability found in most modern C libraries,
+          to arbitrary applications via PAM and ntlm_auth and to Samba itself.
+        '';
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.samba;
@@ -185,7 +205,12 @@ in
   ###### implementation
 
   config = mkMerge
-    [ { # Always provide a smb.conf to shut up programs like smbclient and smbspool.
+    [ { assertions =
+          [ { assertion = cfg.nsswins -> cfg.enableWinbindd;
+              message   = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled";
+            }
+          ];
+        # Always provide a smb.conf to shut up programs like smbclient and smbspool.
         environment.etc = singleton
           { source =
               if cfg.enable then configFile
@@ -194,7 +219,7 @@ in
           };
       }
 
-      (mkIf config.services.samba.enable {
+      (mkIf cfg.enable {
 
         system.nssModules = optional cfg.nsswins samba;
 
@@ -207,9 +232,9 @@ in
           };
 
           services = {
-            "samba-nmbd" = daemonService "nmbd" "-F";
             "samba-smbd" = daemonService "smbd" "-F";
-            "samba-winbindd" = daemonService "winbindd" "-F";
+            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F");
+            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F");
             "samba-setup" = {
               description = "Samba Setup Task";
               script = setupScript;
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index ab9eac3829fb..9815a5434ee3 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -8,7 +8,7 @@ in
     options.services.tahoe = {
       introducers = mkOption {
         default = {};
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
             nickname = mkOption {
               type = types.str;
@@ -49,7 +49,7 @@ in
       };
       nodes = mkOption {
         default = {};
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
             nickname = mkOption {
               type = types.str;
@@ -290,14 +290,14 @@ in
                 shares.total = ${toString settings.client.shares.total}
 
                 [storage]
-                enabled = ${if settings.storage.enable then "true" else "false"}
+                enabled = ${boolToString settings.storage.enable}
                 reserved_space = ${settings.storage.reservedSpace}
 
                 [helper]
-                enabled = ${if settings.helper.enable then "true" else "false"}
+                enabled = ${boolToString settings.helper.enable}
 
                 [sftpd]
-                enabled = ${if settings.sftpd.enable then "true" else "false"}
+                enabled = ${boolToString settings.sftpd.enable}
                 ${optionalString (settings.sftpd.port != null)
                   "port = ${toString settings.sftpd.port}"}
                 ${optionalString (settings.sftpd.hostPublicKeyFile != null)
@@ -343,7 +343,7 @@ in
             preStart = ''
               if [ \! -d ${nodedir} ]; then
                 mkdir -p /var/db/tahoe-lafs
-                tahoe create-node ${nodedir}
+                tahoe create-node --hostname=localhost ${nodedir}
               fi
 
               # Tahoe has created a predefined tahoe.cfg which we must now
diff --git a/nixos/modules/services/networking/aiccu.nix b/nixos/modules/services/networking/aiccu.nix
index 4301da288814..ac755270951b 100644
--- a/nixos/modules/services/networking/aiccu.nix
+++ b/nixos/modules/services/networking/aiccu.nix
@@ -5,7 +5,6 @@ with lib;
 let
 
   cfg = config.services.aiccu;
-  showBool = b: if b then "true" else "false";
   notNull = a: ! isNull a;
   configFile = pkgs.writeText "aiccu.conf" ''
     ${if notNull cfg.username then "username " + cfg.username else ""}
@@ -13,16 +12,16 @@ let
     protocol ${cfg.protocol}
     server ${cfg.server}
     ipv6_interface ${cfg.interfaceName}
-    verbose ${showBool cfg.verbose}
+    verbose ${boolToString cfg.verbose}
     daemonize true
-    automatic ${showBool cfg.automatic}
-    requiretls ${showBool cfg.requireTLS}
+    automatic ${boolToString cfg.automatic}
+    requiretls ${boolToString cfg.requireTLS}
     pidfile ${cfg.pidFile}
-    defaultroute ${showBool cfg.defaultRoute}
+    defaultroute ${boolToString cfg.defaultRoute}
     ${if notNull cfg.setupScript then cfg.setupScript else ""}
-    makebeats ${showBool cfg.makeHeartBeats}
-    noconfigure ${showBool cfg.noConfigure}
-    behindnat ${showBool cfg.behindNAT}
+    makebeats ${boolToString cfg.makeHeartBeats}
+    noconfigure ${boolToString cfg.noConfigure}
+    behindnat ${boolToString cfg.behindNAT}
     ${if cfg.localIPv4Override then "local_ipv4_override" else ""}
   '';
 
@@ -35,7 +34,6 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Enable aiccu IPv6 over IPv4 SiXXs tunnel";
       };
 
@@ -88,21 +86,18 @@ in {
       verbose = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Be verbose?";
       };
 
       automatic = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = "Automatic Login and Tunnel activation";
       };
 
       requireTLS = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           When set to true, if TLS is not supported on the server
           the TIC transaction will fail.
@@ -124,7 +119,6 @@ in {
       defaultRoute = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = "Add a default route";
       };
 
@@ -138,7 +132,6 @@ in {
       makeHeartBeats = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = ''
           In general you don't want to turn this off
           Of course only applies to AYIYA and heartbeat tunnels not to static ones
@@ -148,21 +141,18 @@ in {
       noConfigure = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Don't configure anything";
       };
 
       behindNAT = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Notify the user that a NAT-kind network is detected";
       };
 
       localIPv4Override = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Overrides the IPv4 parameter received from TIC
           This allows one to configure a NAT into "DMZ" mode and then
diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix
new file mode 100644
index 000000000000..ad4ac9bf45e3
--- /dev/null
+++ b/nixos/modules/services/networking/aria2.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.aria2;
+
+  homeDir = "/var/lib/aria2";
+
+  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}
+    listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)}
+    rpc-listen-port=${toString cfg.rpcListenPort}
+    rpc-secret=${cfg.rpcSecret}
+  '';
+
+in
+{
+  options = {
+    services.aria2 = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether or not to enable the headless Aria2 daemon service.
+
+          Aria2 daemon can be controlled via the RPC interface using
+          one of many WebUI (http://localhost:6800/ by default).
+
+          Targets are downloaded to ${downloadDir} by default and are
+          accessible to users in the "aria2" group.
+        '';
+      };
+      openPorts = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open listen and RPC ports found in listenPortRange and rpcListenPort
+          options in the firewall.
+        '';
+      };
+      downloadDir = mkOption {
+        type = types.string;
+        default = "${downloadDir}";
+        description = ''
+          Directory to store downloaded files.
+        '';
+      };
+      listenPortRange = mkOption {
+        type = types.listOf types.attrs;
+        default = [ { from = 6881; to = 6999; } ];
+        description = ''
+          Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker.
+        '';
+      };
+      rpcListenPort = mkOption {
+        type = types.int;
+        default = 6800;
+        description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535";
+      };
+      rpcSecret = mkOption {
+        type = types.string;
+        default = "aria2rpc";
+        description = ''
+          Set RPC secret authorization token.
+          Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used.
+        '';
+      };
+      extraArguments = mkOption {
+        type = types.string;
+        example = "--rpc-listen-all --remote-time=true";
+        default = "";
+        description = ''
+          Additional arguments to be passed to Aria2.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    # Need to open ports for proper functioning
+    networking.firewall = mkIf cfg.openPorts {
+      allowedUDPPortRanges = config.services.aria2.listenPortRange;
+      allowedTCPPorts = [ config.services.aria2.rpcListenPort ];
+    };
+
+    users.extraUsers.aria2 = {
+      group = "aria2";
+      uid = config.ids.uids.aria2;
+      description = "aria2 user";
+      home = homeDir;
+      createHome = false;
+    };
+
+    users.extraGroups.aria2.gid = config.ids.gids.aria2;
+
+    systemd.services.aria2 = {
+      description = "aria2 Service";
+      after = [ "local-fs.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -m 0770 -p "${homeDir}"
+        chown aria2:aria2 "${homeDir}"
+        if [[ ! -d "${config.services.aria2.downloadDir}" ]]
+        then 
+          mkdir -m 0770 -p "${config.services.aria2.downloadDir}"
+          chown aria2:aria2 "${config.services.aria2.downloadDir}"
+        fi
+        if [[ ! -e "${sessionFile}" ]]
+        then 
+          touch "${sessionFile}"
+          chown aria2:aria2 "${sessionFile}"
+        fi
+        cp -f "${settingsFile}" "${settingsDir}/aria2.conf"
+      '';
+
+      serviceConfig = {
+        Restart = "on-abort";
+        ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        User = "aria2";
+        Group = "aria2";
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/networking/asterisk.nix b/nixos/modules/services/networking/asterisk.nix
index 5c71a1d8ddae..514204db33fa 100644
--- a/nixos/modules/services/networking/asterisk.nix
+++ b/nixos/modules/services/networking/asterisk.nix
@@ -17,7 +17,7 @@ let
   allConfFiles =
     cfg.confFiles //
     builtins.listToAttrs (map (x: { name = x;
-                                    value = builtins.readFile (pkgs.asterisk + "/etc/asterisk/" + x); })
+                                    value = builtins.readFile (cfg.package + "/etc/asterisk/" + x); })
                               defaultConfFiles);
 
   asteriskEtc = pkgs.stdenv.mkDerivation
@@ -38,7 +38,7 @@ let
     asteriskConf = ''
       [directories]
       astetcdir => /etc/asterisk
-      astmoddir => ${pkgs.asterisk}/lib/asterisk/modules
+      astmoddir => ${cfg.package}/lib/asterisk/modules
       astvarlibdir => /var/lib/asterisk
       astdbdir => /var/lib/asterisk
       astkeydir => /var/lib/asterisk
@@ -47,7 +47,7 @@ let
       astspooldir => /var/spool/asterisk
       astrundir => /var/run/asterisk
       astlogdir => /var/log/asterisk
-      astsbindir => ${pkgs.asterisk}/sbin
+      astsbindir => ${cfg.package}/sbin
     '';
     extraConf = cfg.extraConfig;
 
@@ -197,11 +197,17 @@ in
           Additional command line arguments to pass to Asterisk.
         '';
       };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.asterisk;
+        defaultText = "pkgs.asterisk";
+        description = "The Asterisk package to use.";
+      };
     };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.asterisk ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc.asterisk.source = asteriskEtc;
 
@@ -234,7 +240,7 @@ in
           # TODO: Make exceptions for /var directories that likely should be updated
           if [ ! -e "$d" ]; then
             mkdir -p "$d"
-            cp --recursive ${pkgs.asterisk}/"$d"/* "$d"/
+            cp --recursive ${cfg.package}/"$d"/* "$d"/
             chown --recursive ${asteriskUser}:${asteriskGroup} "$d"
             find "$d" -type d | xargs chmod 0755
           fi
@@ -247,8 +253,8 @@ in
             # FIXME: This doesn't account for arguments with spaces
             argString = concatStringsSep " " cfg.extraArguments;
           in
-          "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
-        ExecReload = ''${pkgs.asterisk}/bin/asterisk -x "core reload"
+          "${cfg.package}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
+        ExecReload = ''${cfg.package}/bin/asterisk -x "core reload"
           '';
         Type = "forking";
         PIDFile = "/var/run/asterisk/asterisk.pid";
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 6a786e75bbc8..f4d0fc822dea 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -7,31 +7,35 @@ let
 
   cfg = config.services.avahi;
 
+  yesNo = yes : if yes then "yes" else "no";
+
   avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
     [server]
     ${# Users can set `networking.hostName' to the empty string, when getting
       # a host name from DHCP.  In that case, let Avahi take whatever the
       # current host name is; setting `host-name' to the empty string in
       # `avahi-daemon.conf' would be invalid.
-      if hostName != ""
-      then "host-name=${hostName}"
-      else ""}
+      optionalString (hostName != "") "host-name=${hostName}"}
     browse-domains=${concatStringsSep ", " browseDomains}
-    use-ipv4=${if ipv4 then "yes" else "no"}
-    use-ipv6=${if ipv6 then "yes" else "no"}
+    use-ipv4=${yesNo ipv4}
+    use-ipv6=${yesNo ipv6}
     ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
     ${optionalString (domainName!=null) "domain-name=${domainName}"}
+    allow-point-to-point=${yesNo allowPointToPoint}
 
     [wide-area]
-    enable-wide-area=${if wideArea then "yes" else "no"}
+    enable-wide-area=${yesNo wideArea}
 
     [publish]
-    disable-publishing=${if publish.enable then "no" else "yes"}
-    disable-user-service-publishing=${if publish.userServices then "no" else "yes"}
-    publish-addresses=${if publish.userServices || publish.addresses then "yes" else "no"}
-    publish-hinfo=${if publish.hinfo then "yes" else "no"}
-    publish-workstation=${if publish.workstation then "yes" else "no"}
-    publish-domain=${if publish.domain then "yes" else "no"}
+    disable-publishing=${yesNo (!publish.enable)}
+    disable-user-service-publishing=${yesNo (!publish.userServices)}
+    publish-addresses=${yesNo (publish.userServices || publish.addresses)}
+    publish-hinfo=${yesNo publish.hinfo}
+    publish-workstation=${yesNo publish.workstation}
+    publish-domain=${yesNo publish.domain}
+
+    [reflector]
+    enable-reflector=${yesNo reflector}
   '';
 
 in
@@ -98,11 +102,25 @@ in
         '';
       };
 
+      allowPointToPoint = mkOption {
+        default = false;
+        description= ''
+          Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large
+          latencies with such links and opens a potential security hole by allowing mDNS access from Internet
+          connections. Use with care and YMMV!
+        '';
+      };
+
       wideArea = mkOption {
         default = true;
         description = ''Whether to enable wide-area service discovery.'';
       };
 
+      reflector = mkOption {
+        default = false;
+        description = ''Reflect incoming mDNS requests to all allowed network interfaces.'';
+      };
+
       publish = {
         enable = mkOption {
           default = false;
diff --git a/nixos/modules/services/networking/btsync.nix b/nixos/modules/services/networking/btsync.nix
index 572a7387316b..6e479a5860ac 100644
--- a/nixos/modules/services/networking/btsync.nix
+++ b/nixos/modules/services/networking/btsync.nix
@@ -9,7 +9,6 @@ let
 
   listenAddr = cfg.httpListenAddr + ":" + (toString cfg.httpListenPort);
 
-  boolStr = x: if x then "true" else "false";
   optionalEmptyStr = b: v: optionalString (b != "") v;
 
   webUIConfig = optionalString cfg.enableWebUI
@@ -31,7 +30,7 @@ let
   sharedFoldersRecord =
     concatStringsSep "," (map (entry:
       let helper = attr: v:
-        if (entry ? attr) then boolStr entry.attr else boolStr v;
+        if (entry ? attr) then boolToString entry.attr else boolToString v;
       in
       ''
         {
@@ -65,11 +64,11 @@ let
         "listening_port":  ${toString cfg.listeningPort},
         "use_gui":         false,
 
-        "check_for_updates": ${boolStr cfg.checkForUpdates},
-        "use_upnp":          ${boolStr cfg.useUpnp},
+        "check_for_updates": ${boolToString cfg.checkForUpdates},
+        "use_upnp":          ${boolToString cfg.useUpnp},
         "download_limit":    ${toString cfg.downloadLimit},
         "upload_limit":      ${toString cfg.uploadLimit},
-        "lan_encrypt_data":  ${boolStr cfg.encryptLAN},
+        "lan_encrypt_data":  ${boolToString cfg.encryptLAN},
 
         ${webUIConfig}
         ${sharedFoldersConfig}
@@ -208,7 +207,6 @@ in
       storagePath = mkOption {
         type = types.path;
         default = "/var/lib/btsync/";
-        example = "/var/lib/btsync/";
         description = ''
           Where BitTorrent Sync will store it's database files (containing
           things like username info and licenses). Generally, you should not
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
index f2ff11633b1b..9bf266b38054 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/chrony.nix
@@ -12,6 +12,25 @@ let
 
   cfg = config.services.chrony;
 
+  configFile = pkgs.writeText "chrony.conf" ''
+    ${concatMapStringsSep "\n" (server: "server " + server) cfg.servers}
+
+    ${optionalString
+      cfg.initstepslew.enabled
+      "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.initstepslew.servers}"
+    }
+
+    driftfile ${stateDir}/chrony.drift
+
+    keyfile ${keyFile}
+
+    ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
+
+    ${cfg.extraConfig}
+  '';
+
+  chronyFlags = "-n -m -u chrony -f ${configFile} ${toString cfg.extraFlags}";
+
 in
 
 {
@@ -58,6 +77,13 @@ in
           <literal>chrony.conf</literal>
         '';
       };
+
+      extraFlags = mkOption {
+        default = [];
+        example = [ "-s" ];
+        type = types.listOf types.str;
+        description = "Extra flags passed to the chronyd command.";
+      };
     };
 
   };
@@ -70,25 +96,6 @@ in
     # Make chronyc available in the system path
     environment.systemPackages = [ pkgs.chrony ];
 
-    environment.etc."chrony.conf".text =
-      ''
-        ${concatMapStringsSep "\n" (server: "server " + server) cfg.servers}
-
-        ${optionalString
-          cfg.initstepslew.enabled
-          "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.initstepslew.servers}"
-        }
-
-        driftfile ${stateDir}/chrony.drift
-
-        keyfile ${keyFile}
-        generatecommandkey
-
-        ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
-
-        ${cfg.extraConfig}
-      '';
-
     users.extraGroups = singleton
       { name = "chrony";
         gid = config.ids.gids.chrony;
@@ -124,7 +131,7 @@ in
           '';
 
         serviceConfig =
-          { ExecStart = "${pkgs.chrony}/bin/chronyd -n -m -u chrony";
+          { ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}";
           };
       };
 
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index a10851c16523..12c2677c3368 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -258,9 +258,8 @@ in
         Restart = "always";
         StartLimitInterval = 0;
         RestartSec = 1;
-        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
-        AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW";
-        ProtectSystem = "full";
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID";
+        ProtectSystem = true;
         MemoryDenyWriteExecute = true;
         ProtectHome = true;
         PrivateTmp = true;
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index d1900deceaf6..28c96a9baefc 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -7,7 +7,7 @@ let
 
   stateDir = "/var/spool/ddclient";
   ddclientUser = "ddclient";
-  ddclientFlags = "-foreground -verbose -noquiet -file ${config.services.ddclient.configFile}";
+  ddclientFlags = "-foreground -file ${config.services.ddclient.configFile}";
   ddclientPIDFile = "${stateDir}/ddclient.pid";
 
 in
@@ -102,6 +102,22 @@ in
           Method to determine the IP address to send to the dynamic DNS provider.
         '';
       };
+
+      verbose = mkOption {
+        default = true;
+        type = bool;
+        description = ''
+          Print verbose information.
+        '';
+      };
+
+      quiet = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Print no messages for unnecessary updates.
+        '';
+      };
     };
   };
 
@@ -132,9 +148,12 @@ in
         login=${config.services.ddclient.username}
         password=${config.services.ddclient.password}
         protocol=${config.services.ddclient.protocol}
-        server=${config.services.ddclient.server}
+        ${let server = config.services.ddclient.server; in
+          lib.optionalString (server != "") "server=${server}"}
         ssl=${if config.services.ddclient.ssl then "yes" else "no"}
         wildcard=YES
+        quiet=${if config.services.ddclient.quiet then "yes" else "no"}
+        verbose=${if config.services.ddclient.verbose then "yes" else "no"}
         ${config.services.ddclient.domain}
         ${config.services.ddclient.extraConfig}
       '';
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 87c0aa50a1ff..cdba14be21f0 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -71,8 +71,7 @@ let
           # anything ever again ("couldn't resolve ..., giving up on
           # it"), so we silently lose time synchronisation. This also
           # applies to openntpd.
-          ${config.systemd.package}/bin/systemctl try-restart ntpd.service
-          ${config.systemd.package}/bin/systemctl try-restart openntpd.service
+          ${config.systemd.package}/bin/systemctl try-reload-or-restart ntpd.service openntpd.service || true
       fi
 
       ${cfg.runHook}
@@ -157,7 +156,7 @@ in
     systemd.services.dhcpcd =
       { description = "DHCP Client";
 
-        wantedBy = [ "multi-user.target" ];
+        wantedBy = [ "network-online.target" ];
         after = [ "network.target" ];
         wants = [ "network.target" ];
 
@@ -173,7 +172,7 @@ in
         serviceConfig =
           { Type = "forking";
             PIDFile = "/run/dhcpcd.pid";
-            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
+            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd -w --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
             ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
             Restart = "always";
           };
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index d2cd00e74a1f..2eac6dfec5b7 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -4,11 +4,10 @@ with lib;
 
 let
 
-  cfg = config.services.dhcpd;
+  cfg4 = config.services.dhcpd4;
+  cfg6 = config.services.dhcpd6;
 
-  stateDir = "/var/lib/dhcp"; # Don't use /var/state/dhcp; not FHS-compliant.
-
-  configFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "dhcpd.conf"
+  writeConfig = cfg: pkgs.writeText "dhcpd.conf"
     ''
       default-lease-time 600;
       max-lease-time 7200;
@@ -29,131 +28,181 @@ let
       }
     '';
 
-in
-
-{
-
-  ###### interface
+  dhcpdService = postfix: cfg: optionalAttrs cfg.enable {
+    "dhcpd${postfix}" = {
+      description = "DHCPv${postfix} server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      preStart = ''
+        mkdir -m 755 -p ${cfg.stateDir}
+        touch ${cfg.stateDir}/dhcpd.leases
+      '';
+
+      serviceConfig =
+        let
+          configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg;
+          args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}"
+                   "-pf" "/run/dhcpd${postfix}/dhcpd.pid"
+                   "-cf" "${configFile}"
+                   "-lf" "${cfg.stateDir}/dhcpd.leases"
+                   "-user" "dhcpd" "-group" "nogroup"
+                 ] ++ cfg.extraFlags
+                   ++ cfg.interfaces;
+
+        in {
+          ExecStart = concatMapStringsSep " " escapeShellArg args;
+          Type = "forking";
+          Restart = "always";
+          RuntimeDirectory = [ "dhcpd${postfix}" ];
+          PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
+        };
+    };
+  };
 
-  options = {
+  machineOpts = { ... }: {
 
-    services.dhcpd = {
+    options = {
 
-      enable = mkOption {
-        default = false;
-        description = "
-          Whether to enable the DHCP server.
-        ";
+      hostName = mkOption {
+        type = types.str;
+        example = "foo";
+        description = ''
+          Hostname which is assigned statically to the machine.
+        '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        example = ''
-          option subnet-mask 255.255.255.0;
-          option broadcast-address 192.168.1.255;
-          option routers 192.168.1.5;
-          option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
-          option domain-name "example.org";
-          subnet 192.168.1.0 netmask 255.255.255.0 {
-            range 192.168.1.100 192.168.1.200;
-          }
+      ethernetAddress = mkOption {
+        type = types.str;
+        example = "00:16:76:9a:32:1d";
+        description = ''
+          MAC address of the machine.
         '';
-        description = "
-          Extra text to be appended to the DHCP server configuration
-          file.  Currently, you almost certainly need to specify
-          something here, such as the options specifying the subnet
-          mask, DNS servers, etc.
-        ";
       };
 
-      extraFlags = mkOption {
-        default = "";
-        example = "-6";
-        description = "
-          Additional command line flags to be passed to the dhcpd daemon.
-        ";
+      ipAddress = mkOption {
+        type = types.str;
+        example = "192.168.1.10";
+        description = ''
+          IP address of the machine.
+        '';
       };
 
-      configFile = mkOption {
-        default = null;
-        description = "
-          The path of the DHCP server configuration file.  If no file
-          is specified, a file is generated using the other options.
-        ";
-      };
+    };
+  };
 
-      interfaces = mkOption {
-        default = ["eth0"];
-        description = "
-          The interfaces on which the DHCP server should listen.
-        ";
-      };
+  dhcpConfig = postfix: {
 
-      machines = mkOption {
-        default = [];
-        example = [
-          { hostName = "foo";
-            ethernetAddress = "00:16:76:9a:32:1d";
-            ipAddress = "192.168.1.10";
-          }
-          { hostName = "bar";
-            ethernetAddress = "00:19:d1:1d:c4:9a";
-            ipAddress = "192.168.1.11";
-          }
-        ];
-        description = "
-          A list mapping ethernet addresses to IP addresses for the
-          DHCP server.
-        ";
-      };
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable the DHCPv${postfix} server.
+      '';
+    };
 
+    stateDir = mkOption {
+      type = types.path;
+      # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility.
+      default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}";
+      description = ''
+        State directory for the DHCP server.
+      '';
     };
 
-  };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        option subnet-mask 255.255.255.0;
+        option broadcast-address 192.168.1.255;
+        option routers 192.168.1.5;
+        option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
+        option domain-name "example.org";
+        subnet 192.168.1.0 netmask 255.255.255.0 {
+          range 192.168.1.100 192.168.1.200;
+        }
+      '';
+      description = ''
+        Extra text to be appended to the DHCP server configuration
+        file. Currently, you almost certainly need to specify something
+        there, such as the options specifying the subnet mask, DNS servers,
+        etc.
+      '';
+    };
 
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Additional command line flags to be passed to the dhcpd daemon.
+      '';
+    };
 
-  ###### implementation
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        The path of the DHCP server configuration file.  If no file
+        is specified, a file is generated using the other options.
+      '';
+    };
 
-  config = mkIf config.services.dhcpd.enable {
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = ["eth0"];
+      description = ''
+        The interfaces on which the DHCP server should listen.
+      '';
+    };
 
-    users = {
-      extraUsers.dhcpd = {
-        uid = config.ids.uids.dhcpd;
-        description = "DHCP daemon user";
-      };
+    machines = mkOption {
+      type = with types; listOf (submodule machineOpts);
+      default = [];
+      example = [
+        { hostName = "foo";
+          ethernetAddress = "00:16:76:9a:32:1d";
+          ipAddress = "192.168.1.10";
+        }
+        { hostName = "bar";
+          ethernetAddress = "00:19:d1:1d:c4:9a";
+          ipAddress = "192.168.1.11";
+        }
+      ];
+      description = ''
+        A list mapping Ethernet addresses to IPv${postfix} addresses for the
+        DHCP server.
+      '';
     };
 
-    systemd.services.dhcpd =
-      { description = "DHCP server";
+  };
+
+in
+
+{
+
+  ###### interface
 
-        wantedBy = [ "multi-user.target" ];
+  options = {
 
-        after = [ "network.target" ];
+    services.dhcpd4 = dhcpConfig "4";
+    services.dhcpd6 = dhcpConfig "6";
 
-        path = [ pkgs.dhcp ];
+  };
 
-        preStart =
-          ''
-            mkdir -m 755 -p ${stateDir}
 
-            touch ${stateDir}/dhcpd.leases
+  ###### implementation
 
-            mkdir -m 755 -p /run/dhcpd
-            chown dhcpd /run/dhcpd
-          '';
+  config = mkIf (cfg4.enable || cfg6.enable) {
 
-        serviceConfig =
-          { ExecStart = "@${pkgs.dhcp}/sbin/dhcpd dhcpd"
-              + " -pf /run/dhcpd/dhcpd.pid -cf ${configFile}"
-              + " -lf ${stateDir}/dhcpd.leases -user dhcpd -group nogroup"
-              + " ${cfg.extraFlags}"
-              + " ${toString cfg.interfaces}";
-            Restart = "always";
-            Type = "forking";
-            PIDFile = "/run/dhcpd/dhcpd.pid";
-          };
+    users = {
+      extraUsers.dhcpd = {
+        uid = config.ids.uids.dhcpd;
+        description = "DHCP daemon user";
       };
+    };
+
+    systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
 
   };
 
diff --git a/nixos/modules/services/networking/dnschain.nix b/nixos/modules/services/networking/dnschain.nix
index f17f8c832ee4..b64929960576 100644
--- a/nixos/modules/services/networking/dnschain.nix
+++ b/nixos/modules/services/networking/dnschain.nix
@@ -3,23 +3,28 @@
 with lib;
 
 let
-  cfg = config.services;
+  cfgs = config.services;
+  cfg  = cfgs.dnschain;
 
-  dnschainConf = pkgs.writeText "dnschain.conf" ''
+  dataDir  = "/var/lib/dnschain";
+  username = "dnschain";
+
+  configFile = pkgs.writeText "dnschain.conf" ''
     [log]
-    level=info
+    level = info
 
     [dns]
-    host = 127.0.0.1
-    port = 5333
+    host = ${cfg.dns.address}
+    port = ${toString cfg.dns.port}
     oldDNSMethod = NO_OLD_DNS
-    # TODO: check what that address is acutally used for
-    externalIP = 127.0.0.1
+    externalIP = ${cfg.dns.address}
 
     [http]
-    host = 127.0.0.1
-    port=8088
-    tlsPort=4443
+    host = ${cfg.api.hostname}
+    port = ${toString cfg.api.port}
+    tlsPort = ${toString cfg.api.tlsPort}
+
+    ${cfg.extraConfig}
   '';
 
 in
@@ -32,28 +37,81 @@ in
 
     services.dnschain = {
 
-      enable = mkOption {
-        type = types.bool;
-        default = false;
+      enable = mkEnableOption ''
+        DNSChain, a blockchain based DNS + HTTP server.
+        To resolve .bit domains set <literal>services.namecoind.enable = true;</literal>
+        and an RPC username/password.
+      '';
+
+      dns.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
         description = ''
-          Whether to run dnschain. That implies running
-          namecoind as well, so make sure to configure
-          it appropriately.
+          The IP address that will be used to reach this machine.
+          Leave this unchanged if you do not wish to directly expose the DNSChain resolver.
         '';
       };
 
-    };
+      dns.port = mkOption {
+        type = types.int;
+        default = 5333;
+        description = ''
+          The port the DNSChain resolver will bind to.
+        '';
+      };
+
+      api.hostname = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          The hostname (or IP address) the DNSChain API server will bind to.
+        '';
+      };
+
+      api.port = mkOption {
+        type = types.int;
+        default = 8080;
+        description = ''
+          The port the DNSChain API server (HTTP) will bind to.
+        '';
+      };
 
-    services.dnsmasq = {
-      resolveDnschainQueries = mkOption {
-        type = types.bool;
-        default = false;
+      api.tlsPort = mkOption {
+        type = types.int;
+        default = 4433;
         description = ''
-          Resolve <literal>.bit</literal> top-level domains
-          with dnschain and namecoind.
+          The port the DNSChain API server (HTTPS) will bind to.
         '';
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          [log]
+          level = debug
+        '';
+        description = ''
+          Additional options that will be appended to the configuration file.
+        '';
+      };
+
+    };
+
+    services.dnsmasq.resolveDNSChainQueries = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin.
+      '';
+    };
+
+    services.pdns-recursor.resolveDNSChainQueries = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin.
+      '';
     };
 
   };
@@ -61,48 +119,47 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.dnschain.enable {
-
-    services.namecoind.enable = true;
+  config = mkIf cfg.enable {
 
-    services.dnsmasq.servers = optionals cfg.dnsmasq.resolveDnschainQueries [ "/.bit/127.0.0.1#5333" ];
+    services.dnsmasq.servers = optionals cfgs.dnsmasq.resolveDNSChainQueries
+      [ "/.bit/127.0.0.1#${toString cfg.dns.port}"
+        "/.dns/127.0.0.1#${toString cfg.dns.port}"
+      ];
 
-    users.extraUsers = singleton
-      { name = "dnschain";
-        uid = config.ids.uids.dnschain;
-        extraGroups = [ "namecoin" ];
-        description = "Dnschain daemon user";
-        home = "/var/lib/dnschain";
-        createHome = true;
+    services.pdns-recursor.forwardZones = mkIf cfgs.pdns-recursor.resolveDNSChainQueries
+      { bit = "127.0.0.1:${toString cfg.dns.port}";
+        dns = "127.0.0.1:${toString cfg.dns.port}";
       };
 
+    users.extraUsers = singleton {
+      name = username;
+      description = "DNSChain daemon user";
+      home = dataDir;
+      createHome = true;
+      uid = config.ids.uids.dnschain;
+      extraGroups = optional cfgs.namecoind.enable "namecoin";
+    };
+
     systemd.services.dnschain = {
-        description = "Dnschain Daemon";
-        after = [ "namecoind.target" ];
-        wantedBy = [ "multi-user.target" ];
-        path = [ pkgs.openssl ];
-        preStart = ''
-          # Link configuration file into dnschain HOME directory
-          if [ "$(${pkgs.coreutils}/bin/realpath /var/lib/dnschain/.dnschain.conf)" != "${dnschainConf}" ]; then
-              rm -rf /var/lib/dnschain/.dnschain.conf
-              ln -s ${dnschainConf} /var/lib/dnschain/.dnschain.conf
-          fi
-
-          # Create empty namecoin.conf so that dnschain is not
-          # searching for /etc/namecoin/namecoin.conf
-          if [ ! -e /var/lib/dnschain/.namecoin/namecoin.conf ]; then
-              mkdir -p /var/lib/dnschain/.namecoin
-              touch /var/lib/dnschain/.namecoin/namecoin.conf
-          fi
-        '';
-        serviceConfig = {
-          Type = "simple";
-          User = "dnschain";
-          EnvironmentFile = config.services.namecoind.userFile;
-          ExecStart = "${pkgs.dnschain}/bin/dnschain --rpcuser=\${USER} --rpcpassword=\${PASSWORD} --rpcport=8336";
-          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-          ExecStop = "${pkgs.coreutils}/bin/kill -KILL $MAINPID";
-        };
+      description = "DNSChain daemon";
+      after    = optional cfgs.namecoind.enable "namecoind.target";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "dnschain";
+        Restart = "on-failure";
+        ExecStart = "${pkgs.dnschain}/bin/dnschain";
+      };
+
+      preStart = ''
+        # Link configuration file into dnschain home directory
+        configPath=${dataDir}/.dnschain/dnschain.conf
+        mkdir -p ${dataDir}/.dnschain
+        if [ "$(realpath $configPath)" != "${configFile}" ]; then
+          rm -f $configPath
+          ln -s ${configFile} $configPath
+        fi
+      '';
     };
 
   };
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 462039803f80..077aeca457ea 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -2,12 +2,9 @@
 with lib;
 
 let
-  apparmorEnabled = config.security.apparmor.enable;
-  dnscrypt-proxy = pkgs.dnscrypt-proxy;
   cfg = config.services.dnscrypt-proxy;
-  stateDirectory = "/var/lib/dnscrypt-proxy";
 
-  localAddress = "${cfg.localAddress}:${toString cfg.localPort}";
+  stateDirectory = "/var/lib/dnscrypt-proxy";
 
   # The minisign public key used to sign the upstream resolver list.
   # This is somewhat more flexible than preloading the key as an
@@ -17,31 +14,33 @@ let
     sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
   };
 
-  # Internal flag indicating whether the upstream resolver list is used
-  useUpstreamResolverList = cfg.resolverList == null && cfg.customResolver == null;
-
-  resolverList =
-    if (cfg.resolverList != null)
-      then cfg.resolverList
-      else "${stateDirectory}/dnscrypt-resolvers.csv";
-
-  resolverArgs = if (cfg.customResolver != null)
-    then
-      [ "--resolver-address=${cfg.customResolver.address}:${toString cfg.customResolver.port}"
-        "--provider-name=${cfg.customResolver.name}"
-        "--provider-key=${cfg.customResolver.key}"
-      ]
-    else
-      [ "--resolvers-list=${resolverList}"
-        "--resolver-name=${cfg.resolverName}"
-      ];
-
-  # The final command line arguments passed to the daemon
+  # Internal flag indicating whether the upstream resolver list is used.
+  useUpstreamResolverList = cfg.customResolver == null;
+
+  # The final local address.
+  localAddress = "${cfg.localAddress}:${toString cfg.localPort}";
+
+  # The final resolvers list path.
+  resolverList = "${stateDirectory}/dnscrypt-resolvers.csv";
+
+  # Build daemon command line
+
+  resolverArgs =
+    if (cfg.customResolver == null)
+      then
+        [ "-L ${resolverList}"
+          "-R ${cfg.resolverName}"
+        ]
+      else with cfg.customResolver;
+        [ "-N ${name}"
+          "-k ${key}"
+          "-r ${address}:${toString port}"
+        ];
+
   daemonArgs =
-    [ "--local-address=${localAddress}" ]
-    ++ optional cfg.tcpOnly "--tcp-only"
-    ++ optional cfg.ephemeralKeys "-E"
-    ++ resolverArgs;
+       [ "-a ${localAddress}" ]
+    ++ resolverArgs
+    ++ cfg.extraArgs;
 in
 
 {
@@ -51,6 +50,9 @@ in
   };
 
   options = {
+    # Before adding another option, consider whether it could
+    # equally well be passed via extraArgs.
+
     services.dnscrypt-proxy = {
       enable = mkOption {
         default = false;
@@ -83,19 +85,11 @@ in
         default = "dnscrypt.eu-nl";
         type = types.nullOr types.str;
         description = ''
-          The name of the upstream DNSCrypt resolver to use, taken from
-          <filename>${resolverList}</filename>.  The default resolver is
-          located in Holland, supports DNS security extensions, and
-          <emphasis>claims</emphasis> to not keep logs.
-        '';
-      };
-
-      resolverList = mkOption {
-        default = null;
-        type = types.nullOr types.path;
-        description = ''
-          List of DNSCrypt resolvers.  The default is to use the list of
-          public resolvers provided by upstream.
+          The name of the DNSCrypt resolver to use, taken from
+          <filename>${resolverList}</filename>.  The default
+          resolver is located in Holland, supports DNS security
+          extensions, and <emphasis>claims</emphasis> to not
+          keep logs.
         '';
       };
 
@@ -121,7 +115,7 @@ in
           name = mkOption {
             type = types.str;
             description = "Fully qualified domain name";
-            example = "2.dnscrypt-cert.opendns.com";
+            example = "2.dnscrypt-cert.example.com";
           };
 
           key = mkOption {
@@ -132,39 +126,72 @@ in
         }; }));
       };
 
-      tcpOnly = mkOption {
-        default = false;
-        type = types.bool;
+      extraArgs = mkOption {
+        default = [];
+        type = types.listOf types.str;
         description = ''
-          Force sending encrypted DNS queries to the upstream resolver over
-          TCP instead of UDP (on port 443). Use only if the UDP port is blocked.
-        '';
-      };
-
-      ephemeralKeys = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Compute a new key pair for every query.  Enabling this option
-          increases CPU usage, but makes it more difficult for the upstream
-          resolver to track your usage of their service across IP addresses.
-          The default is to re-use the public key pair for all queries, making
-          tracking trivial.
+          Additional command-line arguments passed verbatim to the daemon.
+          See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry> for details.
         '';
+        example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ];
       };
     };
   };
 
-  config = mkIf cfg.enable {
-
+  config = mkIf cfg.enable (mkMerge [{
     assertions = [
       { assertion = (cfg.customResolver != null) || (cfg.resolverName != null);
         message   = "please configure upstream DNSCrypt resolver";
       }
     ];
 
-    security.apparmor.profiles = optional apparmorEnabled (pkgs.writeText "apparmor-dnscrypt-proxy" ''
-      ${dnscrypt-proxy}/bin/dnscrypt-proxy {
+    users.users.dnscrypt-proxy = {
+      description = "dnscrypt-proxy daemon user";
+      isSystemUser = true;
+      group = "dnscrypt-proxy";
+    };
+    users.groups.dnscrypt-proxy = {};
+
+    systemd.sockets.dnscrypt-proxy = {
+      description = "dnscrypt-proxy listening socket";
+      documentation = [ "man:dnscrypt-proxy(8)" ];
+
+      wantedBy = [ "sockets.target" ];
+
+      socketConfig = {
+        ListenStream = localAddress;
+        ListenDatagram = localAddress;
+      };
+    };
+
+    systemd.services.dnscrypt-proxy = {
+      description = "dnscrypt-proxy daemon";
+      documentation = [ "man:dnscrypt-proxy(8)" ];
+
+      before = [ "nss-lookup.target" ];
+      after = [ "network.target" ];
+      requires = [ "dnscrypt-proxy.socket "];
+
+      serviceConfig = {
+        NonBlocking = "true";
+        ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+
+        User = "dnscrypt-proxy";
+
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHome = true;
+      };
+    };
+    }
+
+    (mkIf config.security.apparmor.enable {
+    systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ];
+
+    security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" ''
+      ${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy {
         /dev/null rw,
         /dev/urandom r,
 
@@ -180,6 +207,8 @@ in
         network inet dgram,
         network inet6 dgram,
 
+        ${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr,
+
         ${getLib pkgs.gcc.cc}/lib/libssp.so.* mr,
         ${getLib pkgs.libsodium}/lib/libsodium.so.* mr,
         ${getLib pkgs.systemd}/lib/libsystemd.so.* mr,
@@ -188,102 +217,106 @@ in
         ${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr,
         ${getLib pkgs.libcap}/lib/libcap.so.* mr,
         ${getLib pkgs.lz4}/lib/liblz4.so.* mr,
-        ${getLib pkgs.attr}/lib/libattr.so.* mr,
+        ${getLib pkgs.attr}/lib/libattr.so.* mr, # */
 
         ${resolverList} r,
+
+        /run/systemd/notify rw,
       }
     '');
+    })
 
-    users.users.dnscrypt-proxy = {
-      description = "dnscrypt-proxy daemon user";
-      isSystemUser = true;
-      group = "dnscrypt-proxy";
-    };
-    users.groups.dnscrypt-proxy = {};
-
-    systemd.services.init-dnscrypt-proxy-statedir = optionalAttrs useUpstreamResolverList {
+    (mkIf useUpstreamResolverList {
+    systemd.services.init-dnscrypt-proxy-statedir = {
       description = "Initialize dnscrypt-proxy state directory";
+
+      wantedBy = [ "dnscrypt-proxy.service" ];
+      before = [ "dnscrypt-proxy.service" ];
+
       script = ''
         mkdir -pv ${stateDirectory}
         chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory}
-        cp --preserve=timestamps -uv \
+        cp -uv \
           ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \
           ${stateDirectory}
       '';
+
       serviceConfig = {
         Type = "oneshot";
         RemainAfterExit = true;
       };
     };
 
-    systemd.services.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+    systemd.services.update-dnscrypt-resolvers = {
       description = "Update list of DNSCrypt resolvers";
 
       requires = [ "init-dnscrypt-proxy-statedir.service" ];
       after = [ "init-dnscrypt-proxy-statedir.service" ];
 
-      path = with pkgs; [ curl minisign ];
+      path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ];
       script = ''
         cd ${stateDirectory}
-        curl -fSsL -o dnscrypt-resolvers.csv.tmp \
-          https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv
-        curl -fSsL -o dnscrypt-resolvers.csv.minisig.tmp \
-          https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv.minisig
+        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
+        $get -o dnscrypt-resolvers.csv.minisig.tmp \
+          https://$domain/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
         mv dnscrypt-resolvers.csv.minisig{.tmp,}
-        minisign -q -V -p ${upstreamResolverListPubKey} \
-          -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig
+        if ! minisign -q -V -p ${upstreamResolverListPubKey} \
+          -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then
+          echo "failed to verify resolver list!" >&2
+          exit 1
+        fi
+        [[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old}
         mv dnscrypt-resolvers.csv{.tmp,}
+        if cmp dnscrypt-resolvers.csv{,.old} ; then
+          echo "no change"
+        else
+          echo "resolver list updated"
+        fi
       '';
 
       serviceConfig = {
         PrivateTmp = true;
         PrivateDevices = true;
         ProtectHome = true;
-        ProtectSystem = true;
+        ProtectSystem = "strict";
+        ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}";
+        SystemCallFilter = "~@mount";
       };
     };
 
-    systemd.timers.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+    systemd.timers.update-dnscrypt-resolvers = {
+      wantedBy = [ "timers.target" ];
       timerConfig = {
         OnBootSec = "5min";
         OnUnitActiveSec = "6h";
       };
-      wantedBy = [ "timers.target" ];
-    };
-
-    systemd.sockets.dnscrypt-proxy = {
-      description = "dnscrypt-proxy listening socket";
-      socketConfig = {
-        ListenStream = localAddress;
-        ListenDatagram = localAddress;
-      };
-      wantedBy = [ "sockets.target" ];
     };
-
-    systemd.services.dnscrypt-proxy = {
-      description = "dnscrypt-proxy daemon";
-
-      before = [ "nss-lookup.target" ];
-
-      after = [ "network.target" ]
-        ++ optional apparmorEnabled "apparmor.service"
-        ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service";
-
-      requires = [ "dnscrypt-proxy.socket "]
-        ++ optional apparmorEnabled "apparmor.service"
-        ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service";
-
-      serviceConfig = {
-        Type = "simple";
-        NonBlocking = "true";
-        ExecStart = "${dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}";
-
-        User = "dnscrypt-proxy";
-
-        PrivateTmp = true;
-        PrivateDevices = true;
-        ProtectHome = true;
-      };
-    };
-  };
+    })
+    ]);
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ])
+
+    (mkChangedOptionModule
+      [ "services" "dnscrypt-proxy" "tcpOnly" ]
+      [ "services" "dnscrypt-proxy" "extraArgs" ]
+      (config:
+        let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in
+        optional val "-T"))
+
+    (mkChangedOptionModule
+      [ "services" "dnscrypt-proxy" "ephemeralKeys" ]
+      [ "services" "dnscrypt-proxy" "extraArgs" ]
+      (config:
+        let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in
+        optional val "-E"))
+
+    (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] ''
+      The current resolver listing from upstream is always used
+      unless a custom resolver is specified.
+    '')
+  ];
 }
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml
index 982961833ad2..555c6df4d551 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.xml
+++ b/nixos/modules/services/networking/dnscrypt-proxy.xml
@@ -31,15 +31,12 @@
 
   </sect1>
 
-  <sect1><title>As a forwarder for a caching DNS client</title>
+  <sect1><title>As a forwarder for another DNS client</title>
 
   <para>
-    By default, DNSCrypt proxy acts as a transparent proxy for the
-    system stub resolver. Because the client does not cache lookups, this
-    setup can significantly slow down e.g., web browsing. The recommended
-    configuration is to run DNSCrypt proxy as a forwarder for a caching DNS
-    client. To achieve this, change the default proxy listening port to
-    a non-standard value and point the caching client to it:
+    To run the DNSCrypt proxy client as a forwarder for another
+    DNS client, change the default proxy listening port to a
+    non-standard value and point the other client to it:
     <programlisting>
       services.dnscrypt-proxy.localPort = 43;
     </programlisting>
@@ -60,7 +57,6 @@
   <para>
     <programlisting>
       {
-        networking.nameservers = [ "127.0.0.1" ];
         services.unbound.enable = true;
         services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
       }
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
new file mode 100644
index 000000000000..85fac660d52e
--- /dev/null
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -0,0 +1,187 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg     = config.services.dnscrypt-wrapper;
+  dataDir = "/var/lib/dnscrypt-wrapper";
+
+  daemonArgs = with cfg; [
+    "--listen-address=${address}:${toString port}"
+    "--resolver-address=${upstream.address}:${toString upstream.port}"
+    "--provider-name=${providerName}"
+    "--provider-publickey-file=public.key"
+    "--provider-secretkey-file=secret.key"
+    "--provider-cert-file=${providerName}.crt"
+    "--crypt-secretkey-file=${providerName}.key"
+  ];
+
+  genKeys = ''
+    # generates time-limited keypairs
+    keyGen() {
+      dnscrypt-wrapper --gen-crypt-keypair \
+        --crypt-secretkey-file=${cfg.providerName}.key
+
+      dnscrypt-wrapper --gen-cert-file \
+        --crypt-secretkey-file=${cfg.providerName}.key \
+        --provider-cert-file=${cfg.providerName}.crt \
+        --provider-publickey-file=public.key \
+        --provider-secretkey-file=secret.key \
+        --cert-file-expire-days=${toString cfg.keys.expiration}
+    }
+
+    cd ${dataDir}
+
+    # generate provider keypair (first run only)
+    if [ ! -f public.key ] || [ ! -f secret.key ]; then
+      dnscrypt-wrapper --gen-provider-keypair
+    fi
+
+    # generate new keys for rotation
+    if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
+      keyGen
+    fi
+  '';
+
+  rotateKeys = ''
+    # check if keys are not expired
+    keyValid() {
+      fingerprint=$(dnscrypt-wrapper --show-provider-publickey-fingerprint | awk '{print $(NF)}')
+      dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \
+        --resolver-address=127.0.0.1:${toString cfg.port} \
+        --provider-name=${cfg.providerName} \
+        --provider-key=$fingerprint
+    }
+
+    cd ${dataDir}
+
+    # archive old keys and restart the service
+    if ! keyValid; then
+      mkdir -p oldkeys
+      mv ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key
+      mv ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt
+      systemctl restart dnscrypt-wrapper
+    fi
+  '';
+
+in {
+
+
+  ###### interface
+
+  options.services.dnscrypt-wrapper = {
+    enable = mkEnableOption "DNSCrypt wrapper";
+
+    address = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = ''
+        The DNSCrypt wrapper will bind to this IP address.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 5353;
+      description = ''
+        The DNSCrypt wrapper will listen for DNS queries on this port.
+      '';
+    };
+
+    providerName = mkOption {
+      type = types.str;
+      default = "2.dnscrypt-cert.${config.networking.hostName}";
+      example = "2.dnscrypt-cert.myresolver";
+      description = ''
+        The name that will be given to this DNSCrypt resolver.
+        Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>.
+      '';
+    };
+
+    upstream.address = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = ''
+        The IP address of the upstream DNS server DNSCrypt will "wrap".
+      '';
+    };
+
+    upstream.port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        The port of the upstream DNS server DNSCrypt will "wrap".
+      '';
+    };
+
+    keys.expiration = mkOption {
+      type = types.int;
+      default = 30;
+      description = ''
+        The duration (in days) of the time-limited secret key.
+        This will be automatically rotated before expiration.
+      '';
+    };
+
+    keys.checkInterval = mkOption {
+      type = types.int;
+      default = 1440;
+      description = ''
+        The time interval (in minutes) between key expiration checks.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.users.dnscrypt-wrapper = {
+      description = "dnscrypt-wrapper daemon user";
+      home = "${dataDir}";
+      createHome = true;
+    };
+    users.groups.dnscrypt-wrapper = { };
+
+
+    systemd.services.dnscrypt-wrapper = {
+      description = "dnscrypt-wrapper daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path     = [ pkgs.dnscrypt-wrapper ];
+
+      serviceConfig = {
+        User = "dnscrypt-wrapper";
+        WorkingDirectory = dataDir;
+        Restart   = "on-failure";
+        ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}";
+      };
+
+      preStart = genKeys;
+    };
+
+
+    systemd.services.dnscrypt-wrapper-rotate = {
+      after    = [ "network.target" ];
+      requires = [ "dnscrypt-wrapper.service" ];
+      description = "Rotates DNSCrypt wrapper keys if soon to expire";
+
+      path   = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ];
+      script = rotateKeys;
+    };
+
+
+    systemd.timers.dnscrypt-wrapper-rotate = {
+      description = "Periodically check DNSCrypt wrapper keys for expiration";
+      wantedBy = [ "multi-user.target" ];
+
+      timerConfig = {
+        Unit = "dnscrypt-wrapper-rotate.service";
+        OnBootSec = "1min";
+        OnUnitActiveSec = cfg.keys.checkInterval * 60;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/ferm.nix b/nixos/modules/services/networking/ferm.nix
index 8933e166f59f..07338ccf4d9c 100644
--- a/nixos/modules/services/networking/ferm.nix
+++ b/nixos/modules/services/networking/ferm.nix
@@ -19,7 +19,6 @@ in {
     services.ferm = {
       enable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Whether to enable Ferm Firewall.
diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix
index 70d2d72ca8b7..3a95b9c4ec94 100644
--- a/nixos/modules/services/networking/firefox/sync-server.nix
+++ b/nixos/modules/services/networking/firefox/sync-server.nix
@@ -19,7 +19,7 @@ let
     [syncserver]
     public_url = ${cfg.publicUrl}
     ${optionalString (cfg.sqlUri != "") "sqluri = ${cfg.sqlUri}"}
-    allow_new_users = ${if cfg.allowNewUsers then "true" else "false"}
+    allow_new_users = ${boolToString cfg.allowNewUsers}
 
     [browserid]
     backend = tokenserver.verifiers.LocalVerifier
@@ -33,7 +33,6 @@ in
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = ''
           Whether to enable a Firefox Sync Server, this give the opportunity to
           Firefox users to store all synchronized data on their own server. To use this
@@ -78,7 +77,6 @@ in
       allowNewUsers = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = ''
           Whether to allow new-user signups on the server. Only request by
           existing accounts will be honored.
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 1c0ea5034df3..243cd04c96c2 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -4,17 +4,29 @@
    ‘networking.firewall.extraCommands’.  For modularity, the firewall
    uses several chains:
 
-   - ‘nixos-fw-input’ is the main chain for input packet processing.
+   - ‘nixos-fw’ is the main chain for input packet processing.
+
+   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
+     additional logging, or want to reject certain packets anyway, you
+     can insert rules at the start of this chain.
 
    - ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for
      refused packets.  (The former jumps to the latter after logging
      the packet.)  If you want additional logging, or want to accept
      certain packets anyway, you can insert rules at the start of
-     these chain.
+     this chain.
 
-   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
-     additional logging, or want to reject certain packets anyway, you
-     can insert rules at the start of this chain.
+   - ‘nixos-fw-rpfilter’ is used as the main chain in the raw table,
+     called from the built-in ‘PREROUTING’ chain.  If the kernel
+     supports it and `cfg.checkReversePath` is set this chain will
+     perform a reverse path filter test.
+
+   - ‘nixos-drop’ is used while reloading the firewall in order to drop
+     all traffic.  Since reloading isn't implemented in an atomic way
+     this'll prevent any traffic from leaking through while reloading
+     the firewall.  However, if the reloading fails, the ‘firewall-stop’
+     script will be called which in return will effectively disable the
+     complete firewall (in the default configuration).
 
 */
 
@@ -26,6 +38,10 @@ let
 
   cfg = config.networking.firewall;
 
+  inherit (config.boot.kernelPackages) kernel;
+
+  kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false);
+
   helpers =
     ''
       # Helper command to manipulate both the IPv4 and IPv6 tables.
@@ -49,7 +65,7 @@ let
     # firewall would be atomic.  Apparently that's possible
     # with iptables-restore.
     ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
-    for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse FW_REFUSE; do
+    for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse; do
       ip46tables -F "$chain" 2> /dev/null || true
       ip46tables -X "$chain" 2> /dev/null || true
     done
@@ -172,13 +188,16 @@ let
       }-j nixos-fw-accept
     ''}
 
-    # Accept all ICMPv6 messages except redirects and node
-    # information queries (type 139).  See RFC 4890, section
-    # 4.4.
     ${optionalString config.networking.enableIPv6 ''
+      # Accept all ICMPv6 messages except redirects and node
+      # information queries (type 139).  See RFC 4890, section
+      # 4.4.
       ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
       ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
       ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
+
+      # Allow this host to act as a DHCPv6 client
+      ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -j nixos-fw-accept
     ''}
 
     ${cfg.extraCommands}
@@ -228,11 +247,6 @@ let
     fi
   '';
 
-  kernelPackages = config.boot.kernelPackages;
-
-  kernelHasRPFilter = kernelPackages.kernel.features.netfilterRPFilter or false;
-  kernelCanDisableHelpers = kernelPackages.kernel.features.canDisableNetfilterConntrackHelpers or false;
-
 in
 
 {
@@ -290,26 +304,30 @@ in
       default = false;
       description =
         ''
-          If set, forbidden packets are rejected rather than dropped
+          If set, refused packets are rejected rather than dropped
           (ignored).  This means that an ICMP "port unreachable" error
-          message is sent back to the client.  Rejecting packets makes
+          message is sent back to the client (or a TCP RST packet in
+          case of an existing connection).  Rejecting packets makes
           port scanning somewhat easier.
         '';
     };
 
     networking.firewall.trustedInterfaces = mkOption {
       type = types.listOf types.str;
+      default = [ ];
+      example = [ "enp0s2" ];
       description =
         ''
           Traffic coming in from these interfaces will be accepted
-          unconditionally.
+          unconditionally.  Traffic from the loopback (lo) interface
+          will always be accepted.
         '';
     };
 
     networking.firewall.allowedTCPPorts = mkOption {
-      default = [];
-      example = [ 22 80 ];
       type = types.listOf types.int;
+      default = [ ];
+      example = [ 22 80 ];
       description =
         ''
           List of TCP ports on which incoming connections are
@@ -318,9 +336,9 @@ in
     };
 
     networking.firewall.allowedTCPPortRanges = mkOption {
-      default = [];
-      example = [ { from = 8999; to = 9003; } ];
       type = types.listOf (types.attrsOf types.int);
+      default = [ ];
+      example = [ { from = 8999; to = 9003; } ];
       description =
         ''
           A range of TCP ports on which incoming connections are
@@ -329,9 +347,9 @@ in
     };
 
     networking.firewall.allowedUDPPorts = mkOption {
-      default = [];
-      example = [ 53 ];
       type = types.listOf types.int;
+      default = [ ];
+      example = [ 53 ];
       description =
         ''
           List of open UDP ports.
@@ -339,9 +357,9 @@ in
     };
 
     networking.firewall.allowedUDPPortRanges = mkOption {
-      default = [];
-      example = [ { from = 60000; to = 61000; } ];
       type = types.listOf (types.attrsOf types.int);
+      default = [ ];
+      example = [ { from = 60000; to = 61000; } ];
       description =
         ''
           Range of open UDP ports.
@@ -349,8 +367,8 @@ in
     };
 
     networking.firewall.allowPing = mkOption {
-      default = true;
       type = types.bool;
+      default = true;
       description =
         ''
           Whether to respond to incoming ICMPv4 echo requests
@@ -361,36 +379,43 @@ in
     };
 
     networking.firewall.pingLimit = mkOption {
-      default = null;
       type = types.nullOr (types.separatedString " ");
+      default = null;
+      example = "--limit 1/minute --limit-burst 5";
       description =
         ''
           If pings are allowed, this allows setting rate limits
-          on them. If non-null, this option should be in the form
-          of flags like "--limit 1/minute --limit-burst 5"
+          on them.  If non-null, this option should be in the form of
+          flags like "--limit 1/minute --limit-burst 5"
         '';
     };
 
     networking.firewall.checkReversePath = mkOption {
-      default = kernelHasRPFilter;
       type = types.either types.bool (types.enum ["strict" "loose"]);
+      default = kernelHasRPFilter;
+      example = "loose";
       description =
         ''
-          Performs a reverse path filter test on a packet.
-          If a reply to the packet would not be sent via the same interface
-          that the packet arrived on, it is refused.
+          Performs a reverse path filter test on a packet.  If a reply
+          to the packet would not be sent via the same interface that
+          the packet arrived on, it is refused.
+
+          If using asymmetric routing or other complicated routing, set
+          this option to loose mode or disable it and setup your own
+          counter-measures.
 
-          If using asymmetric routing or other complicated routing,
-          set this option to loose mode or disable it and setup your
-          own counter-measures.
+          This option can be either true (or "strict"), "loose" (only
+          drop the packet if the source address is not reachable via any
+          interface) or false.  Defaults to the value of
+          kernelHasRPFilter.
 
           (needs kernel 3.3+)
         '';
     };
 
     networking.firewall.logReversePathDrops = mkOption {
-      default = false;
       type = types.bool;
+      default = false;
       description =
         ''
           Logs dropped packets failing the reverse path filter test if
@@ -399,9 +424,9 @@ in
     };
 
     networking.firewall.connectionTrackingModules = mkOption {
-      default = [ "ftp" ];
-      example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
       type = types.listOf types.str;
+      default = [ ];
+      example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
       description =
         ''
           List of connection-tracking helpers that are auto-loaded.
@@ -409,17 +434,19 @@ in
 
           As helpers can pose as a security risk, it is advised to
           set this to an empty list and disable the setting
-          networking.firewall.autoLoadConntrackHelpers
+          networking.firewall.autoLoadConntrackHelpers unless you
+          know what you are doing. Connection tracking is disabled
+          by default.
 
-          Loading of helpers is recommended to be done through the new
-          CT target. More info:
+          Loading of helpers is recommended to be done through the
+          CT target.  More info:
           https://home.regit.org/netfilter-en/secure-use-of-helpers/
         '';
     };
 
     networking.firewall.autoLoadConntrackHelpers = mkOption {
-      default = true;
       type = types.bool;
+      default = false;
       description =
         ''
           Whether to auto-load connection-tracking helpers.
@@ -461,7 +488,8 @@ in
         ''
           Additional shell commands executed as part of the firewall
           shutdown script.  These are executed just after the removal
-          of the nixos input rule, or if the service enters a failed state.
+          of the NixOS input rule, or if the service enters a failed
+          state.
         '';
     };
 
@@ -478,15 +506,14 @@ in
 
     environment.systemPackages = [ pkgs.iptables ] ++ cfg.extraPackages;
 
-    boot.kernelModules = map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
-    boot.extraModprobeConfig = optionalString (!cfg.autoLoadConntrackHelpers) ''
-      options nf_conntrack nf_conntrack_helper=0
+    boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack")
+      ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
+    boot.extraModprobeConfig = optionalString cfg.autoLoadConntrackHelpers ''
+      options nf_conntrack nf_conntrack_helper=1
     '';
 
     assertions = [ { assertion = (cfg.checkReversePath != false) || kernelHasRPFilter;
                      message = "This kernel does not support rpfilter"; }
-                   { assertion = cfg.autoLoadConntrackHelpers || kernelCanDisableHelpers;
-                     message = "This kernel does not support disabling conntrack helpers"; }
                  ];
 
     systemd.services.firewall = {
@@ -499,7 +526,7 @@ in
       path = [ pkgs.iptables ] ++ cfg.extraPackages;
 
       # FIXME: this module may also try to load kernel modules, but
-      # containers don't have CAP_SYS_MODULE. So the host system had
+      # containers don't have CAP_SYS_MODULE.  So the host system had
       # better have all necessary modules already loaded.
       unitConfig.ConditionCapability = "CAP_NET_ADMIN";
       unitConfig.DefaultDependencies = false;
diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix
index ca47a18bc1f6..b93e28e34efd 100644
--- a/nixos/modules/services/networking/flannel.nix
+++ b/nixos/modules/services/networking/flannel.nix
@@ -149,6 +149,6 @@ in {
       serviceConfig.ExecStart = "${cfg.package}/bin/flannel";
     };
 
-    services.etcd.enable = mkDefault cfg.etcd.endpoints == ["http://127.0.0.1:2379"];
+    services.etcd.enable = mkDefault (cfg.etcd.endpoints == ["http://127.0.0.1:2379"]);
   };
 }
diff --git a/nixos/modules/services/networking/gale.nix b/nixos/modules/services/networking/gale.nix
index bc975159cdfd..fd83f9e3c1b7 100644
--- a/nixos/modules/services/networking/gale.nix
+++ b/nixos/modules/services/networking/gale.nix
@@ -141,7 +141,7 @@ in
          setgid = false;
        };
 
-       security.setuidOwners = [ cfg.setuidWrapper ];
+       security.wrappers.gksign = cfg.setuidWrapper;
 
        systemd.services.gale-galed = {
          description = "Gale messaging daemon";
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index abb7a4e9137c..4e176353fc28 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -8,10 +8,6 @@ let
 
   homeDir = "/var/lib/i2pd";
 
-  extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')";
-
-  toYesNo = b: if b then "true" else "false";
-
   mkEndpointOpt = name: addr: port: {
     enable = mkEnableOption name;
     name = mkOption {
@@ -76,10 +72,10 @@ let
 
   i2pdConf = pkgs.writeText "i2pd.conf"
   ''
-  ipv4 = ${toYesNo cfg.enableIPv4}
-  ipv6 = ${toYesNo cfg.enableIPv6}
-  notransit = ${toYesNo cfg.notransit}
-  floodfill = ${toYesNo cfg.floodfill}
+  ipv4 = ${boolToString cfg.enableIPv4}
+  ipv6 = ${boolToString cfg.enableIPv6}
+  notransit = ${boolToString cfg.notransit}
+  floodfill = ${boolToString cfg.floodfill}
   netid = ${toString cfg.netid}
   ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" }
   ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
@@ -88,14 +84,14 @@ let
   transittunnels = ${toString cfg.limits.transittunnels}
 
   [upnp]
-  enabled = ${toYesNo cfg.upnp.enable}
+  enabled = ${boolToString cfg.upnp.enable}
   name = ${cfg.upnp.name}
 
   [precomputation]
-  elgamal = ${toYesNo cfg.precomputation.elgamal}
+  elgamal = ${boolToString cfg.precomputation.elgamal}
 
   [reseed]
-  verify = ${toYesNo cfg.reseed.verify}
+  verify = ${boolToString cfg.reseed.verify}
   file = ${cfg.reseed.file}
   urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
 
@@ -107,11 +103,11 @@ let
       (proto: let portStr = toString proto.port; in
         ''
           [${proto.name}]
-          enabled = ${toYesNo proto.enable}
+          enabled = ${boolToString proto.enable}
           address = ${proto.address}
           port = ${toString proto.port}
           ${if proto ? keys then "keys = ${proto.keys}" else ""}
-          ${if proto ? auth then "auth = ${toYesNo proto.auth}" else ""}
+          ${if proto ? auth then "auth = ${boolToString proto.auth}" else ""}
           ${if proto ? user then "user = ${proto.user}" else ""}
           ${if proto ? pass then "pass = ${proto.pass}" else ""}
           ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
@@ -154,9 +150,8 @@ let
 
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
-    ${if isNull cfg.extIp then extip else ""}
     ${pkgs.i2pd}/bin/i2pd \
-      --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \
+      ${if isNull cfg.extIp then "" else "--host="+cfg.extIp} \
       --conf=${i2pdConf} \
       --tunconf=${i2pdTunnelConf}
   '';
diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix
index ede57c5046d3..bd583fb020ec 100644
--- a/nixos/modules/services/networking/ircd-hybrid/default.nix
+++ b/nixos/modules/services/networking/ircd-hybrid/default.nix
@@ -12,7 +12,7 @@ let
     substFiles = [ "=>/conf" ./ircd.conf ];
     inherit (pkgs) ircdHybrid coreutils su iproute gnugrep procps;
 
-    ipv6Enabled = if config.networking.enableIPv6 then "true" else "false";
+    ipv6Enabled = boolToString config.networking.enableIPv6;
 
     inherit (cfg) serverName sid description adminEmail
             extraPort;
diff --git a/nixos/modules/services/networking/keepalived/default.nix b/nixos/modules/services/networking/keepalived/default.nix
new file mode 100644
index 000000000000..378cd9365848
--- /dev/null
+++ b/nixos/modules/services/networking/keepalived/default.nix
@@ -0,0 +1,245 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.keepalived;
+
+  keepalivedConf = pkgs.writeText "keepalived.conf" ''
+    global_defs {
+      ${snmpGlobalDefs}
+      ${cfg.extraGlobalDefs}
+    }
+
+    ${vrrpInstancesStr}
+    ${cfg.extraConfig}
+  '';
+
+  snmpGlobalDefs = with cfg.snmp; optionalString enable (
+    optionalString (socket != null) "snmp_socket ${socket}\n"
+    + optionalString enableKeepalived "enable_snmp_keepalived\n"
+    + optionalString enableChecker "enable_snmp_checker\n"
+    + optionalString enableRfc "enable_snmp_rfc\n"
+    + optionalString enableRfcV2 "enable_snmp_rfcv2\n"
+    + optionalString enableRfcV3 "enable_snmp_rfcv3\n"
+    + optionalString enableTraps "enable_traps"
+  );
+
+  vrrpInstancesStr = concatStringsSep "\n" (map (i:
+    ''
+      vrrp_instance ${i.name} {
+        interface ${i.interface}
+        state ${i.state}
+        virtual_router_id ${toString i.virtualRouterId}
+        priority ${toString i.priority}
+        ${optionalString i.noPreempt "nopreempt"}
+
+        ${optionalString i.useVmac (
+          "use_vmac" + optionalString (i.vmacInterface != null) " ${i.vmacInterface}"
+        )}
+        ${optionalString i.vmacXmitBase "vmac_xmit_base"}
+
+        ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"}
+        unicast_peer {
+          ${concatStringsSep "\n" i.unicastPeers}
+        }
+
+        virtual_ipaddress {
+          ${concatMapStringsSep "\n" virtualIpLine i.virtualIps}
+        }
+
+        ${i.extraConfig}
+      }
+    ''
+  ) vrrpInstances);
+
+  virtualIpLine = (ip:
+    ip.addr
+    + optionalString (notNullOrEmpty ip.brd) " brd ${ip.brd}"
+    + optionalString (notNullOrEmpty ip.dev) " dev ${ip.dev}"
+    + optionalString (notNullOrEmpty ip.scope) " scope ${ip.scope}"
+    + optionalString (notNullOrEmpty ip.label) " label ${ip.label}"
+  );
+
+  notNullOrEmpty = s: !(s == null || s == "");
+
+  vrrpInstances = mapAttrsToList (iName: iConfig:
+    {
+      name = iName;
+    } // iConfig
+  ) cfg.vrrpInstances;
+
+  vrrpInstanceAssertions = i: [
+    { assertion = i.interface != "";
+      message = "services.keepalived.vrrpInstances.${i.name}.interface option cannot be empty.";
+    }
+    { assertion = i.virtualRouterId >= 0 && i.virtualRouterId <= 255;
+      message = "services.keepalived.vrrpInstances.${i.name}.virtualRouterId must be an integer between 0..255.";
+    }
+    { assertion = i.priority >= 0 && i.priority <= 255;
+      message = "services.keepalived.vrrpInstances.${i.name}.priority must be an integer between 0..255.";
+    }
+    { assertion = i.vmacInterface == null || i.useVmac;
+      message = "services.keepalived.vrrpInstances.${i.name}.vmacInterface has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
+    }
+    { assertion = !i.vmacXmitBase || i.useVmac;
+      message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set.";
+    }
+  ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps);
+
+  virtualIpAssertions = vrrpName: ip: [
+    { assertion = ip.addr != "";
+      message = "The 'addr' option for an services.keepalived.vrrpInstances.${vrrpName}.virtualIps entry cannot be empty.";
+    }
+  ];
+
+  pidFile = "/run/keepalived.pid";
+
+in
+{
+
+  options = {
+    services.keepalived = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Keepalived.
+        '';
+      };
+
+      snmp = {
+
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable the builtin AgentX subagent.
+          '';
+        };
+
+        socket = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Socket to use for connecting to SNMP master agent. If this value is
+            set to null, keepalived's default will be used, which is
+            unix:/var/agentx/master, unless using a network namespace, when the
+            default is udp:localhost:705.
+          '';
+        };
+
+        enableKeepalived = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of vrrp element of KEEPALIVED MIB.
+          '';
+        };
+
+        enableChecker = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of checker element of KEEPALIVED MIB.
+          '';
+        };
+
+        enableRfc = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs.
+          '';
+        };
+
+        enableRfcV2 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC2787 VRRP MIB.
+          '';
+        };
+
+        enableRfcV3 = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP handling of RFC6527 VRRP MIB.
+          '';
+        };
+
+        enableTraps = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable SNMP traps.
+          '';
+        };
+
+      };
+
+      vrrpInstances = mkOption {
+        type = types.attrsOf (types.submodule (import ./vrrp-options.nix {
+          inherit lib;
+        }));
+        default = {};
+        description = "Declarative vhost config";
+      };
+
+      extraGlobalDefs = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the 'global_defs' block of the
+          configuration file
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to the configuration file.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = flatten (map vrrpInstanceAssertions vrrpInstances);
+
+    systemd.timers.keepalived-boot-delay = {
+      description = "Keepalive Daemon delay to avoid instant transition to MASTER state";
+      after = [ "network.target" "network-online.target" "syslog.target" ];
+      requires = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      timerConfig = {
+        OnActiveSec = "5s";
+        Unit = "keepalived.service";
+      };
+    };
+
+    systemd.services.keepalived = {
+      description = "Keepalive Daemon (LVS and VRRP)";
+      after = [ "network.target" "network-online.target" "syslog.target" ];
+      wants = [ "network-online.target" ];
+      serviceConfig = {
+        Type = "forking";
+        PIDFile = pidFile;
+        KillMode = "process";
+        ExecStart = "${pkgs.keepalived}/sbin/keepalived"
+          + " -f ${keepalivedConf}"
+          + " -p ${pidFile}"
+          + optionalString cfg.snmp.enable " --snmp";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart = "always";
+        RestartSec = "1s";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/keepalived/virtual-ip-options.nix b/nixos/modules/services/networking/keepalived/virtual-ip-options.nix
new file mode 100644
index 000000000000..1b8889b1b472
--- /dev/null
+++ b/nixos/modules/services/networking/keepalived/virtual-ip-options.nix
@@ -0,0 +1,50 @@
+{ lib } :
+
+with lib;
+{
+  options = {
+
+    addr = mkOption {
+      type = types.str;
+      description = ''
+        IP address, optionally with a netmask: IPADDR[/MASK]
+      '';
+    };
+
+    brd = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The broadcast address on the interface.
+      '';
+    };
+
+    dev = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The name of the device to add the address to.
+      '';
+    };
+
+    scope = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The scope of the area where this address is valid.
+      '';
+    };
+
+    label = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Each address may be tagged with a label string. In order to preserve
+        compatibility with Linux-2.0 net aliases, this string must coincide with
+        the name of the device or must be prefixed with the device name followed
+        by colon.
+      '';
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/keepalived/vrrp-options.nix b/nixos/modules/services/networking/keepalived/vrrp-options.nix
new file mode 100644
index 000000000000..79eff3ae5419
--- /dev/null
+++ b/nixos/modules/services/networking/keepalived/vrrp-options.nix
@@ -0,0 +1,121 @@
+{ lib } :
+
+with lib;
+{
+  options = {
+
+    interface = mkOption {
+      type = types.str;
+      description = ''
+        Interface for inside_network, bound by vrrp.
+      '';
+    };
+
+    state = mkOption {
+      type = types.enum [ "MASTER" "BACKUP" ];
+      default = "BACKUP";
+      description = ''
+        Initial state. As soon as the other machine(s) come up, an election will
+        be held and the machine with the highest "priority" will become MASTER.
+        So the entry here doesn't matter a whole lot.
+      '';
+    };
+
+    virtualRouterId = mkOption {
+      type = types.int;
+      description = ''
+        Arbitrary unique number 0..255. Used to differentiate multiple instances
+        of vrrpd running on the same NIC (and hence same socket).
+      '';
+    };
+
+    priority = mkOption {
+      type = types.int;
+      default = 100;
+      description = ''
+        For electing MASTER, highest priority wins. To be MASTER, make 50 more
+        than other machines.
+      '';
+    };
+
+    noPreempt = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        VRRP will normally preempt a lower priority machine when a higher
+        priority machine comes online. "nopreempt" allows the lower priority
+        machine to maintain the master role, even when a higher priority machine
+        comes back online. NOTE: For this to work, the initial state of this
+        entry must be BACKUP.
+      '';
+    };
+
+    useVmac = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Use VRRP Virtual MAC.
+      '';
+    };
+
+    vmacInterface = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+         Name of the vmac interface to use. keepalived will come up with a name
+         if you don't specify one.
+      '';
+    };
+
+    vmacXmitBase = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Send/Recv VRRP messages from base interface instead of VMAC interface.
+      '';
+    };
+
+    unicastSrcIp = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+         Default IP for binding vrrpd is the primary IP on interface. If you
+         want to hide location of vrrpd, use this IP as src_addr for unicast
+         vrrp packets.
+      '';
+    };
+
+    unicastPeers = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Do not send VRRP adverts over VRRP multicast group. Instead it sends
+        adverts to the following list of ip addresses using unicast design
+        fashion. It can be cool to use VRRP FSM and features in a networking
+        environment where multicast is not supported! IP Addresses specified can
+        IPv4 as well as IPv6.
+      '';
+    };
+
+    virtualIps = mkOption {
+      type = types.listOf (types.submodule (import ./virtual-ip-options.nix {
+        inherit lib;
+      }));
+      default = [];
+      example = literalExample ''
+        TODO: Example
+      '';
+      description = "Declarative vhost config";
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra lines to be added verbatim to the vrrp_instance section.
+      '';
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
new file mode 100644
index 000000000000..18e2ab9aebf1
--- /dev/null
+++ b/nixos/modules/services/networking/kresd.nix
@@ -0,0 +1,119 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.kresd;
+  package = pkgs.knot-resolver;
+
+  configFile = pkgs.writeText "kresd.conf" cfg.extraConfig;
+in
+
+{
+  meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
+
+  ###### interface
+  options.services.kresd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable knot-resolver domain name server.
+        DNSSEC validation is turned on by default.
+        You can run <literal>sudo nc -U /run/kresd/control</literal>
+        and give commands interactively to kresd.
+      '';
+    };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra lines to be added verbatim to the generated configuration file.
+      '';
+    };
+    cacheDir = mkOption {
+      type = types.path;
+      default = "/var/cache/kresd";
+      description = ''
+        Directory for caches.  They are intended to survive reboots.
+      '';
+    };
+    interfaces = mkOption {
+      type = with types; listOf str;
+      default = [ "::1" "127.0.0.1" ];
+      description = ''
+        What addresses the server should listen on.
+      '';
+    };
+    # TODO: perhaps options for more common stuff like cache size or forwarding
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.etc."kresd.conf".source = configFile; # not required
+
+    users.extraUsers = singleton
+      { name = "kresd";
+        uid = config.ids.uids.kresd;
+        group = "kresd";
+        description = "Knot-resolver daemon user";
+      };
+    users.extraGroups = singleton
+      { name = "kresd";
+        gid = config.ids.gids.kresd;
+      };
+
+    systemd.sockets.kresd = rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      listenStreams = map
+        # Syntax depends on being IPv6 or IPv4.
+        (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
+        cfg.interfaces;
+      socketConfig.ListenDatagram = listenStreams;
+    };
+
+    systemd.sockets.kresd-control = rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      partOf = [ "kresd.socket" ];
+      listenStreams = [ "/run/kresd/control" ];
+      socketConfig = {
+        FileDescriptorName = "control";
+        Service = "kresd.service";
+        SocketMode = "0660"; # only root user/group may connect
+      };
+    };
+
+    # 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.services.kresd = {
+      description = "Knot-resolver daemon";
+
+      serviceConfig = {
+        User = "kresd";
+        Type = "notify";
+        WorkingDirectory = cfg.cacheDir;
+      };
+
+      script = ''
+        exec '${package}/bin/kresd' --config '${configFile}' \
+          -k '${cfg.cacheDir}/root.key'
+      '';
+
+      after = [ "kresd-cachedir.service" ];
+      requires = [ "kresd.socket" "kresd-cachedir.service" ];
+      wantedBy = [ "sockets.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/libreswan.nix b/nixos/modules/services/networking/libreswan.nix
index 3866b216f8ef..c87e738d2a23 100644
--- a/nixos/modules/services/networking/libreswan.nix
+++ b/nixos/modules/services/networking/libreswan.nix
@@ -102,7 +102,7 @@ in
       serviceConfig = {
         Type = "simple";
         Restart = "always";
-        EnvironmentFile = "${pkgs.libreswan}/etc/sysconfig/pluto";
+        EnvironmentFile = "-${pkgs.libreswan}/etc/sysconfig/pluto";
         ExecStartPre = [
           "${libexec}/addconn --config ${configFile} --checkconfig"
           "${libexec}/_stackmanager start"
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index f926cd710c8d..5451500b56f6 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -16,7 +16,7 @@ let
     pid_file /run/mosquitto/pid
     acl_file ${aclFile}
     persistence true
-    allow_anonymous ${if cfg.allowAnonymous then "true" else "false"}
+    allow_anonymous ${boolToString cfg.allowAnonymous}
     bind_address ${cfg.host}
     port ${toString cfg.port}
     ${listenerConf}
@@ -147,7 +147,6 @@ in
 
       allowAnonymous = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Allow clients to connect without authentication.
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
index 81f968ae9fe1..13d7c3254f9d 100644
--- a/nixos/modules/services/networking/murmur.nix
+++ b/nixos/modules/services/networking/murmur.nix
@@ -26,21 +26,21 @@ let
 
     textmessagelength=${toString cfg.textMsgLength}
     imagemessagelength=${toString cfg.imgMsgLength}
-    allowhtml=${if cfg.allowHtml then "true" else "false"}
+    allowhtml=${boolToString cfg.allowHtml}
     logdays=${toString cfg.logDays}
-    bonjour=${if cfg.bonjour then "true" else "false"}
-    sendversion=${if cfg.sendVersion then "true" else "false"}
+    bonjour=${boolToString cfg.bonjour}
+    sendversion=${boolToString cfg.sendVersion}
 
     ${if cfg.registerName     == "" then "" else "registerName="+cfg.registerName}
     ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword}
     ${if cfg.registerUrl      == "" then "" else "registerUrl="+cfg.registerUrl}
     ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname}
 
-    certrequired=${if cfg.clientCertRequired then "true" else "false"}
+    certrequired=${boolToString cfg.clientCertRequired}
     ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert}
     ${if cfg.sslKey  == "" then "" else "sslKey="+cfg.sslKey}
     ${if cfg.sslCa   == "" then "" else "sslCA="+cfg.sslCa}
-    
+
     ${cfg.extraConfig}
   '';
 in
diff --git a/nixos/modules/services/networking/namecoind.nix b/nixos/modules/services/networking/namecoind.nix
index 83fc1ec66679..9df9f67cde83 100644
--- a/nixos/modules/services/networking/namecoind.nix
+++ b/nixos/modules/services/networking/namecoind.nix
@@ -3,25 +3,35 @@
 with lib;
 
 let
-  cfg = config.services.namecoind;
+  cfg     = config.services.namecoind;
+  dataDir = "/var/lib/namecoind";
+  useSSL  = (cfg.rpc.certificate != null) && (cfg.rpc.key != null);
+  useRPC  = (cfg.rpc.user != null) && (cfg.rpc.password != null);
 
-  namecoinConf =
-  let
-    useSSL = (cfg.rpcCertificate != null) && (cfg.rpcKey != null);
-  in
-  pkgs.writeText "namecoin.conf" ''
+  listToConf = option: list:
+    concatMapStrings (value :"${option}=${value}\n") list;
+
+  configFile = pkgs.writeText "namecoin.conf" (''
     server=1
     daemon=0
-    rpcallowip=127.0.0.1
-    walletpath=${cfg.wallet}
-    gen=${if cfg.generate then "1" else "0"}
-    rpcssl=${if useSSL then "1" else "0"}
-    ${optionalString useSSL "rpcsslcertificatechainfile=${cfg.rpcCertificate}"}
-    ${optionalString useSSL "rpcsslprivatekeyfile=${cfg.rpcKey}"}
-    ${optionalString useSSL "rpcsslciphers=TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"}
     txindex=1
     txprevcache=1
-  '';
+    walletpath=${cfg.wallet}
+    gen=${if cfg.generate then "1" else "0"}
+    ${listToConf "addnode" cfg.extraNodes}
+    ${listToConf "connect" cfg.trustedNodes}
+  '' + optionalString useRPC ''
+    rpcbind=${cfg.rpc.address}
+    rpcport=${toString cfg.rpc.port}
+    rpcuser=${cfg.rpc.user}
+    rpcpassword=${cfg.rpc.password}
+    ${listToConf "rpcallowip" cfg.rpc.allowFrom}
+  '' + optionalString useSSL ''
+    rpcssl=1
+    rpcsslcertificatechainfile=${cfg.rpc.certificate}
+    rpcsslprivatekeyfile=${cfg.rpc.key}
+    rpcsslciphers=TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH
+  '');
 
 in
 
@@ -33,66 +43,102 @@ in
 
     services.namecoind = {
 
-      enable = mkOption {
+      enable = mkEnableOption "namecoind, Namecoin client.";
+
+      wallet = mkOption {
+        type = types.path;
+        default = "${dataDir}/wallet.dat";
+        description = ''
+          Wallet file. The ownership of the file has to be
+          namecoin:namecoin, and the permissions must be 0640.
+        '';
+      };
+
+      generate = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Whether to run namecoind.
+          Whether to generate (mine) Namecoins.
         '';
       };
 
-      wallet = mkOption {
-        type = types.path;
-        example = "/etc/namecoin/wallet.dat";
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
         description = ''
-          Wallet file. The ownership of the file has to be
-          namecoin:namecoin, and the permissions must be 0640.
+          List of additional peer IP addresses to connect to.
         '';
       };
 
-      userFile = mkOption {
-        type = types.nullOr types.path;
+      trustedNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of the only peer IP addresses to connect to. If specified
+          no other connection will be made.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
         default = null;
-        example = "/etc/namecoin/user";
         description = ''
-          File containing the user name and user password to
-          authenticate RPC connections to namecoind.
-          The content of the file is of the form:
-          <literal>
-          USER=namecoin
-          PASSWORD=secret
-          </literal>
-          The ownership of the file has to be namecoin:namecoin,
-          and the permissions must be 0640.
+          User name for RPC connections.
         '';
       };
 
-      generate = mkOption {
-        type = types.bool;
-        default = false;
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
         description = ''
-          Whether to generate (mine) Namecoins.
+          Password for RPC connections.
         '';
       };
 
-      rpcCertificate = mkOption {
+      rpc.address = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 8332;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.certificate = mkOption {
         type = types.nullOr types.path;
         default = null;
-        example = "/etc/namecoin/server.cert";
+        example = "/var/lib/namecoind/server.cert";
         description = ''
           Certificate file for securing RPC connections.
         '';
       };
 
-      rpcKey = mkOption {
+      rpc.key = mkOption {
         type = types.nullOr types.path;
         default = null;
-        example = "/etc/namecoin/server.pem";
+        example = "/var/lib/namecoind/server.pem";
         description = ''
           Key file for securing RPC connections.
         '';
       };
 
+
+      rpc.allowFrom = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" ];
+        description = ''
+          List of IP address ranges allowed to use the RPC API.
+          Wiledcards (*) can be user to specify a range.
+        '';
+      };
+
     };
 
   };
@@ -102,47 +148,54 @@ in
 
   config = mkIf cfg.enable {
 
-    users.extraUsers = singleton
-      { name = "namecoin";
-        uid = config.ids.uids.namecoin;
-        description = "Namecoin daemon user";
-        home = "/var/lib/namecoin";
-        createHome = true;
-      };
+    services.dnschain.extraConfig = ''
+      [namecoin]
+      config = ${configFile}
+    '';
+
+    users.extraUsers = singleton {
+      name = "namecoin";
+      uid  = config.ids.uids.namecoin;
+      description = "Namecoin daemon user";
+      home = dataDir;
+      createHome = true;
+    };
 
-    users.extraGroups = singleton
-      { name = "namecoin";
-        gid = config.ids.gids.namecoin;
-      };
+    users.extraGroups = singleton {
+      name = "namecoin";
+      gid  = config.ids.gids.namecoin;
+    };
 
     systemd.services.namecoind = {
-        description = "Namecoind Daemon";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-        preStart = ''
-          if [  "$(stat --printf '%u' ${cfg.userFile})" != "${toString config.ids.uids.namecoin}" \
-             -o "$(stat --printf '%g' ${cfg.userFile})" != "${toString config.ids.gids.namecoin}" \
-             -o "$(stat --printf '%a' ${cfg.userFile})" != "640" ]; then
-             echo "ERROR: bad ownership or rights on ${cfg.userFile}" >&2
-             exit 1
-          fi
-          if [  "$(stat --printf '%u' ${cfg.wallet})" != "${toString config.ids.uids.namecoin}" \
-             -o "$(stat --printf '%g' ${cfg.wallet})" != "${toString config.ids.gids.namecoin}" \
-             -o "$(stat --printf '%a' ${cfg.wallet})" != "640" ]; then
-             echo "ERROR: bad ownership or rights on ${cfg.wallet}" >&2
-             exit 1
-          fi
-        '';
-        serviceConfig = {
-          Type = "simple";
-          User = "namecoin";
-          EnvironmentFile = cfg.userFile;
-          ExecStart = "${pkgs.altcoins.namecoind}/bin/namecoind -conf=${namecoinConf} -rpcuser=\${USER} -rpcpassword=\${PASSWORD} -printtoconsole";
-          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-          ExecStop = "${pkgs.coreutils}/bin/kill -KILL $MAINPID";
-          StandardOutput = "null";
-          Nice = "10";
-        };
+      description = "Namecoind daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "namecoin";
+        Griup = "namecoin";
+        ExecStart  = "${pkgs.altcoins.namecoind}/bin/namecoind -conf=${configFile} -datadir=${dataDir} -printtoconsole";
+        ExecStop   = "${pkgs.coreutils}/bin/kill -KILL $MAINPID";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Nice = "10";
+        PrivateTmp = true;
+        TimeoutStopSec     = "60s";
+        TimeoutStartSec    = "2s";
+        Restart            = "always";
+        StartLimitInterval = "120s";
+        StartLimitBurst    = "5";
+      };
+
+      preStart = optionalString (cfg.wallet != "${dataDir}/wallet.dat")  ''
+        # check wallet file permissions
+        if [ "$(stat --printf '%u' ${cfg.wallet})" != "${toString config.ids.uids.namecoin}" \
+           -o "$(stat --printf '%g' ${cfg.wallet})" != "${toString config.ids.gids.namecoin}" \
+           -o "$(stat --printf '%a' ${cfg.wallet})" != "640" ]; then
+           echo "ERROR: bad ownership or rights on ${cfg.wallet}" >&2
+           exit 1
+        fi
+      '';
+
     };
 
   };
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 8f353979d3fc..0c12b2c1dfd4 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -12,6 +12,7 @@ let
   configFile = writeText "NetworkManager.conf" ''
     [main]
     plugins=keyfile
+    dns=${if cfg.useDnsmasq then "dnsmasq" else "default"}
 
     [keyfile]
     ${optionalString (config.networking.hostName != "")
@@ -24,6 +25,8 @@ let
 
     [connection]
     ipv6.ip6-privacy=2
+    ethernet.cloned-mac-address=${cfg.ethernet.macAddress}
+    wifi.cloned-mac-address=${cfg.wifi.macAddress}
   '';
 
   /*
@@ -73,6 +76,19 @@ let
     "pre-down" = "pre-down.d/";
   };
 
+  macAddressOpt = mkOption {
+    type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]);
+    default = "preserve";
+    example = "00:11:22:33:44:55";
+    description = ''
+      "XX:XX:XX:XX:XX:XX": MAC address of the interface.
+      <literal>permanent</literal>: use the permanent MAC address of the device.
+      <literal>preserve</literal>: don’t change the MAC address of the device upon activation.
+      <literal>random</literal>: generate a randomized value upon each connect.
+      <literal>stable</literal>: generate a stable, hashed MAC address.
+    '';
+  };
+
 in {
 
   ###### interface
@@ -140,13 +156,27 @@ in {
         '';
       };
 
+      ethernet.macAddress = macAddressOpt;
+      wifi.macAddress = macAddressOpt;
+
+      useDnsmasq = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable NetworkManager's dnsmasq integration. NetworkManager will run
+          dnsmasq as a local caching nameserver, using a "split DNS"
+          configuration if you are connected to a VPN, and then update
+          resolv.conf to point to the local nameserver.
+        '';
+      };
+
       dispatcherScripts = mkOption {
         type = types.listOf (types.submodule {
           options = {
             source = mkOption {
-              type = types.str;
+              type = types.path;
               description = ''
-                A script source.
+                A script.
               '';
             };
 
@@ -174,7 +204,7 @@ in {
 
     assertions = [{
       assertion = config.networking.wireless.enable == false;
-      message = "You can not use networking.networkmanager with services.networking.wireless";
+      message = "You can not use networking.networkmanager with networking.wireless";
     }];
 
     boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
@@ -206,7 +236,7 @@ in {
              target = "NetworkManager/dispatcher.d/02overridedns";
            }
       ++ lib.imap (i: s: {
-        text = s.source;
+        inherit (s) source;
         target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
       }) cfg.dispatcherScripts;
 
@@ -223,12 +253,14 @@ in {
     users.extraUsers = [{
       name = "nm-openvpn";
       uid = config.ids.uids.nm-openvpn;
+      extraGroups = [ "networkmanager" ];
     }];
 
     systemd.packages = cfg.packages;
 
     systemd.services."network-manager" = {
       wantedBy = [ "network.target" ];
+      restartTriggers = [ configFile ];
 
       preStart = ''
         mkdir -m 700 -p /etc/NetworkManager/system-connections
@@ -239,7 +271,8 @@ in {
     # Turn off NixOS' network management
     networking = {
       useDHCP = false;
-      wireless.enable = false;
+      # use mkDefault to trigger the assertion about the conflict above
+      wireless.enable = lib.mkDefault false;
     };
 
     powerManagement.resumeCommands = ''
diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix
new file mode 100644
index 000000000000..56b942054140
--- /dev/null
+++ b/nixos/modules/services/networking/nftables.nix
@@ -0,0 +1,136 @@
+{ config, pkgs, lib, ... }:
+with lib;
+let
+  cfg = config.networking.nftables;
+in
+{
+  ###### interface
+
+  options = {
+    networking.nftables.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable nftables.  nftables is a Linux-based packet
+          filtering framework intended to replace frameworks like iptables.
+
+          This conflicts with the standard networking firewall, so make sure to
+          disable it before using nftables.
+
+          Note that if you have Docker enabled you will not be able to use
+          nftables without intervention. Docker uses iptables internally to
+          setup NAT for containers. This module disables the ip_tables kernel
+          module, however Docker automatically loads the module. Please see [1]
+          for more information.
+
+          There are other programs that use iptables internally too, such as
+          libvirt.
+
+          [1]: https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273
+        '';
+    };
+    networking.nftables.ruleset = mkOption {
+      type = types.lines;
+      example = ''
+        # Check out https://wiki.nftables.org/ for better documentation.
+        # Table for both IPv4 and IPv6.
+        table inet filter {
+          # Block all incomming connections traffic except SSH and "ping".
+          chain input {
+            type filter hook input priority 0;
+
+            # accept any localhost traffic
+            iifname lo accept
+
+            # accept traffic originated from us
+            ct state {established, related} accept
+
+            # ICMP
+            # routers may also want: mld-listener-query, nd-router-solicit
+            ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
+            ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept
+
+            # allow "ping"
+            ip6 nexthdr icmp icmpv6 type echo-request accept
+            ip protocol icmp icmp type echo-request accept
+
+            # accept SSH connections (required for a server)
+            tcp dport 22 accept
+
+            # count and drop any other traffic
+            counter drop
+          }
+
+          # Allow all outgoing connections.
+          chain output {
+            type filter hook output priority 0;
+            accept
+          }
+
+          chain forward {
+            type filter hook forward priority 0;
+            accept
+          }
+        }
+      '';
+      description =
+        ''
+          The ruleset to be used with nftables.  Should be in a format that
+          can be loaded using "/bin/nft -f".  The ruleset is updated atomically.
+        '';
+    };
+    networking.nftables.rulesetFile = mkOption {
+      type = types.path;
+      default = pkgs.writeTextFile {
+        name = "nftables-rules";
+        text = cfg.ruleset;
+      };
+      description =
+        ''
+          The ruleset file to be used with nftables.  Should be in a format that
+          can be loaded using "nft -f".  The ruleset is updated atomically.
+        '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = config.networking.firewall.enable == false;
+      message = "You can not use nftables with services.networking.firewall.";
+    }];
+    boot.blacklistedKernelModules = [ "ip_tables" ];
+    environment.systemPackages = [ pkgs.nftables ];
+    systemd.services.nftables = {
+      description = "nftables firewall";
+      before = [ "network-pre.target" ];
+      wants = [ "network-pre.target" ];
+      wantedBy = [ "multi-user.target" ];
+      reloadIfChanged = true;
+      serviceConfig = let
+        rulesScript = pkgs.writeScript "nftables-rules" ''
+          #! ${pkgs.nftables}/bin/nft -f
+          flush ruleset
+          include "${cfg.rulesetFile}"
+        '';
+        checkScript = pkgs.writeScript "nftables-check" ''
+          #! ${pkgs.stdenv.shell} -e
+          if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
+            echo "Unload ip_tables before using nftables!" 1>&2
+            exit 1
+          else
+            ${rulesScript}
+          fi
+        '';
+      in {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = checkScript;
+        ExecReload = checkScript;
+        ExecStop = "${pkgs.nftables}/bin/nft flush ruleset";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nylon.nix b/nixos/modules/services/networking/nylon.nix
index da6487dbd499..4864ecf3f92f 100644
--- a/nixos/modules/services/networking/nylon.nix
+++ b/nixos/modules/services/networking/nylon.nix
@@ -8,7 +8,7 @@ let
 
   homeDir = "/var/lib/nylon";
 
-  configFile = pkgs.writeText "nylon.conf" ''
+  configFile = cfg: pkgs.writeText "nylon-${cfg.name}.conf" ''
     [General]
     No-Simultaneous-Conn=${toString cfg.nrConnections}
     Log=${if cfg.logging then "1" else "0"}
@@ -22,15 +22,9 @@ let
     Deny-IP=${concatStringsSep " " cfg.deniedIPRanges}
   '';
 
-in
-
-{
-
-  ###### interface
-
-  options = {
+  nylonOpts = { name, config, ... }: {
 
-    services.nylon = {
+    options = {
 
       enable = mkOption {
         type = types.bool;
@@ -40,6 +34,12 @@ in
         '';
       };
 
+      name = mkOption {
+        type = types.str;
+        default = "";
+        description = "The name of this nylon instance.";
+      };
+
       nrConnections = mkOption {
         type = types.int;
         default = 10;
@@ -107,13 +107,51 @@ in
         '';
       };
     };
+    config = { name = mkDefault name; };
+  };
+
+  mkNamedNylon = cfg: {
+    "nylon-${cfg.name}" = {
+      description = "Nylon, a lightweight SOCKS proxy server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+      {
+        User = "nylon";
+        Group = "nylon";
+        WorkingDirectory = homeDir;
+        ExecStart = "${pkgs.nylon}/bin/nylon -f -c ${configFile cfg}";
+      };
+    };
+  };
+
+  anyNylons = collect (p: p ? enable) cfg;
+  enabledNylons = filter (p: p.enable == true) anyNylons;
+  nylonUnits = map (nylon: mkNamedNylon nylon) enabledNylons;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.nylon = mkOption {
+      default = {};
+      description = "Collection of named nylon instances";
+      type = with types; loaOf (submodule nylonOpts);
+      internal = true;
+      options = [ nylonOpts ];
+    };
+
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = mkIf (length(enabledNylons) > 0) {
 
-    users.extraUsers.nylon= {
+    users.extraUsers.nylon = {
       group = "nylon";
       description = "Nylon SOCKS Proxy";
       home = homeDir;
@@ -123,17 +161,7 @@ in
 
     users.extraGroups.nylon.gid = config.ids.gids.nylon;
 
-    systemd.services.nylon = {
-      description = "Nylon, a lightweight SOCKS proxy server";
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig =
-      {
-        User = "nylon";
-        Group = "nylon";
-        WorkingDirectory = homeDir;
-        ExecStart = "${pkgs.nylon}/bin/nylon -f -c ${configFile}";
-      };
-    };
+    systemd.services = fold (a: b: a // b) {} nylonUnits;
+
   };
 }
diff --git a/nixos/modules/services/networking/openntpd.nix b/nixos/modules/services/networking/openntpd.nix
index 13a1b5258ce0..4bb9da54fe09 100644
--- a/nixos/modules/services/networking/openntpd.nix
+++ b/nixos/modules/services/networking/openntpd.nix
@@ -11,6 +11,9 @@ let
     ${concatStringsSep "\n" (map (s: "server ${s}") cfg.servers)}
     ${cfg.extraConfig}
   '';
+
+  pidFile = "/run/openntpd.pid";
+
 in
 {
   ###### interface
@@ -67,7 +70,11 @@ in
       wants = [ "network-online.target" "time-sync.target" ];
       before = [ "time-sync.target" ];
       after = [ "dnsmasq.service" "bind.service" "network-online.target" ];
-      serviceConfig.ExecStart = "${package}/sbin/ntpd -d -f ${cfgFile} ${cfg.extraOptions}";
+      serviceConfig = {
+        ExecStart = "${package}/sbin/ntpd -f ${cfgFile} -p ${pidFile} ${cfg.extraOptions}";
+        Type = "forking";
+        PIDFile = pidFile;
+      };
     };
   };
 }
diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
new file mode 100644
index 000000000000..26be72d2a61e
--- /dev/null
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -0,0 +1,168 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  dataDir  = "/var/lib/pdns-recursor";
+  username = "pdns-recursor";
+
+  cfg   = config.services.pdns-recursor;
+  zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones;
+
+  configFile = pkgs.writeText "recursor.conf" ''
+    local-address=${cfg.dns.address}
+    local-port=${toString cfg.dns.port}
+    allow-from=${concatStringsSep "," cfg.dns.allowFrom}
+
+    webserver-address=${cfg.api.address}
+    webserver-port=${toString cfg.api.port}
+    webserver-allow-from=${concatStringsSep "," cfg.api.allowFrom}
+
+    forward-zones=${concatStringsSep "," zones}
+    export-etc-hosts=${if cfg.exportHosts then "yes" else "no"}
+    dnssec=${cfg.dnssecValidation}
+    serve-rfc1918=${if cfg.serveRFC1918 then "yes" else "no"}
+
+    ${cfg.extraConfig}
+  '';
+
+in {
+  options.services.pdns-recursor = {
+    enable = mkEnableOption "PowerDNS Recursor, a recursive DNS server";
+
+    dns.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.port = mkOption {
+      type = types.int;
+      default = 53;
+      description = ''
+        Port number Recursor DNS server will bind to.
+      '';
+    };
+
+    dns.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" ];
+      example = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make DNS queries.
+      '';
+    };
+
+    api.address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        IP address Recursor REST API server will bind to.
+      '';
+    };
+
+    api.port = mkOption {
+      type = types.int;
+      default = 8082;
+      description = ''
+        Port number Recursor REST API server will bind to.
+      '';
+    };
+
+    api.allowFrom = mkOption {
+      type = types.listOf types.str;
+      default = [ "0.0.0.0/0" ];
+      description = ''
+        IP address ranges of clients allowed to make API requests.
+      '';
+    };
+
+    exportHosts = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+       Whether to export names and IP addresses defined in /etc/hosts.
+      '';
+    };
+
+    forwardZones = mkOption {
+      type = types.attrs;
+      example = { eth = "127.0.0.1:5353"; };
+      default = {};
+      description = ''
+        DNS zones to be forwarded to other servers.
+      '';
+    };
+
+    dnssecValidation = mkOption {
+      type = types.enum ["off" "process-no-validate" "process" "log-fail" "validate"];
+      default = "validate";
+      description = ''
+        Controls the level of DNSSEC processing done by the PowerDNS Recursor.
+        See https://doc.powerdns.com/md/recursor/dnssec/ for a detailed explanation.
+      '';
+    };
+
+    serveRFC1918 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to directly resolve the RFC1918 reverse-mapping domains:
+        <literal>10.in-addr.arpa</literal>,
+        <literal>168.192.in-addr.arpa</literal>,
+        <literal>16-31.172.in-addr.arpa</literal>
+        This saves load on the AS112 servers.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra options to be appended to the configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers."${username}" = {
+      home = dataDir;
+      createHome = true;
+      uid = config.ids.uids.pdns-recursor;
+      description = "PowerDNS Recursor daemon user";
+    };
+
+    systemd.services.pdns-recursor = {
+      unitConfig.Documentation = "man:pdns_recursor(1) man:rec_control(1)";
+      description = "PowerDNS recursive server";
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" ];
+
+      serviceConfig = {
+        User = username;
+        Restart    ="on-failure";
+        RestartSec = "5";
+        PrivateTmp = true;
+        PrivateDevices = true;
+        AmbientCapabilities = "cap_net_bind_service";
+        ExecStart = ''${pkgs.pdns-recursor}/bin/pdns_recursor \
+          --config-dir=${dataDir} \
+          --socket-dir=${dataDir} \
+          --disable-syslog
+        '';
+      };
+
+      preStart = ''
+        # Link configuration file into recursor home directory
+        configPath=${dataDir}/recursor.conf
+        if [ "$(realpath $configPath)" != "${configFile}" ]; then
+          rm -f $configPath
+          ln -s ${configFile} $configPath
+        fi
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
index 9d63f549b23a..8cd4a0823534 100644
--- a/nixos/modules/services/networking/prayer.nix
+++ b/nixos/modules/services/networking/prayer.nix
@@ -18,7 +18,7 @@ let
     var_prefix = "${stateDir}"
     prayer_user = "${prayerUser}"
     prayer_group = "${prayerGroup}"
-    sendmail_path = "/var/setuid-wrappers/sendmail"
+    sendmail_path = "/run/wrappers/bin/sendmail"
 
     use_http_port ${cfg.port}
 
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 5682b506344c..fb9c9dc67f24 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -219,7 +219,7 @@ in
 
       data_path = "/var/lib/prosody"
 
-      allow_registration = ${ if cfg.allowRegistration then "true" else "false" };
+      allow_registration = ${boolToString cfg.allowRegistration};
 
       ${ optionalString cfg.modules.console "console_enabled = true;" }
 
@@ -244,7 +244,7 @@ in
 
       ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
         VirtualHost "${v.domain}"
-          enabled = ${if v.enabled then "true" else "false"};
+          enabled = ${boolToString v.enabled};
           ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) }
           ${ v.extraConfig }
         '') cfg.virtualHosts) }
@@ -265,7 +265,8 @@ in
     systemd.services.prosody = {
 
       description = "Prosody XMPP server";
-      after = [ "network.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         User = "prosody";
diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix
index ac83da920638..aab58cc77b90 100644
--- a/nixos/modules/services/networking/quagga.nix
+++ b/nixos/modules/services/networking/quagga.nix
@@ -104,7 +104,6 @@ in
         enable = mkOption {
           type = types.bool;
           default = any isEnabled services;
-          example = true;
           description = ''
             Whether to enable the Zebra routing manager.
 
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
index edcc12170b20..bc7d6912b5ce 100644
--- a/nixos/modules/services/networking/quassel.nix
+++ b/nixos/modules/services/networking/quassel.nix
@@ -25,8 +25,8 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.kde4.quasselDaemon;
-        defaultText = "pkgs.kde4.quasselDaemon";
+        default = pkgs.quasselDaemon;
+        defaultText = "pkgs.quasselDaemon";
         description = ''
           The package of the quassel daemon.
         '';
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix
index f9300fdabc57..ef860e7e5df4 100644
--- a/nixos/modules/services/networking/radicale.nix
+++ b/nixos/modules/services/networking/radicale.nix
@@ -57,4 +57,6 @@ in
       serviceConfig.Group = "radicale";
     };
   };
+
+  meta.maintainers = with lib.maintainers; [ aneeshusa ];
 }
diff --git a/nixos/modules/services/networking/redsocks.nix b/nixos/modules/services/networking/redsocks.nix
new file mode 100644
index 000000000000..a47a78f1005e
--- /dev/null
+++ b/nixos/modules/services/networking/redsocks.nix
@@ -0,0 +1,270 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.redsocks;
+in
+{
+  ##### interface
+  options = {
+    services.redsocks = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable redsocks.";
+      };
+
+      log_debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log connection progress.";
+      };
+
+      log_info = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log start and end of client sessions.";
+      };
+
+      log = mkOption {
+        type = types.str;
+        default = "stderr";
+        description =
+          ''
+            Where to send logs.
+
+            Possible values are:
+              - stderr
+              - file:/path/to/file
+              - syslog:FACILITY where FACILITY is any of "daemon", "local0",
+              etc.
+          '';
+      };
+
+      chroot = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description =
+          ''
+            Chroot under which to run redsocks. Log file is opened before
+            chroot, but if logging to syslog /etc/localtime may be required.
+          '';
+      };
+
+      redsocks = mkOption {
+        description =
+          ''
+            Local port to proxy associations to be performed.
+
+            The example shows how to configure a proxy to handle port 80 as HTTP
+            relay, and all other ports as HTTP connect.
+          '';
+        example = [
+          { port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay";
+            redirectCondition = "--dport 80";
+            doNotRedirect = [ "-d 1.2.0.0/16" ];
+          }
+          { port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect";
+            redirectCondition = true;
+            doNotRedirect = [ "-d 1.2.0.0/16" ];
+          }
+        ];
+        type = types.listOf (types.submodule { options = {
+          ip = mkOption {
+            type = types.str;
+            default = "127.0.0.1";
+            description =
+              ''
+                IP on which redsocks should listen. Defaults to 127.0.0.1 for
+                security reasons.
+              '';
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 12345;
+            description = "Port on which redsocks should listen.";
+          };
+
+          proxy = mkOption {
+            type = types.str;
+            description =
+              ''
+                Proxy through which redsocks should forward incoming traffic.
+                Example: "example.org:8080"
+              '';
+          };
+
+          type = mkOption {
+            type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ];
+            description = "Type of proxy.";
+          };
+
+          login = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = "Login to send to proxy.";
+          };
+
+          password = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description =
+              ''
+                Password to send to proxy. WARNING, this will end up
+                world-readable in the store! Awaiting
+                https://github.com/NixOS/nix/issues/8 to be able to fix.
+              '';
+          };
+
+          disclose_src = mkOption {
+            type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip"
+                                "Forwarded_ipport" ];
+            default = "false";
+            description =
+              ''
+                Way to disclose client IP to the proxy.
+                  - "false": do not disclose
+                http-connect supports the following ways:
+                  - "X-Forwarded-For": add header "X-Forwarded-For: IP"
+                  - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239)
+                  - "Forwarded_ipport": add header 'Forwarded: for="IP:port"'
+              '';
+          };
+
+          redirectInternetOnly = mkOption {
+            type = types.bool;
+            default = true;
+            description = "Exclude all non-globally-routable IPs from redsocks";
+          };
+
+          doNotRedirect = mkOption {
+            type = with types; listOf str;
+            default = [];
+            description =
+              ''
+                Iptables filters that if matched will get the packet off of
+                redsocks.
+              '';
+            example = [ "-d 1.2.3.4" ];
+          };
+
+          redirectCondition = mkOption {
+            type = with types; either bool str;
+            default = false;
+            description =
+              ''
+                Conditions to make outbound packets go through this redsocks
+                instance.
+
+                If set to false, no packet will be forwarded. If set to true,
+                all packets will be forwarded (except packets excluded by
+                redirectInternetOnly).
+
+                If set to a string, this is an iptables filter that will be
+                matched against packets before getting them into redsocks. For
+                example, setting it to "--dport 80" will only send
+                packets to port 80 to redsocks. Note "-p tcp" is always
+                implicitly added, as udp can only be proxied through redudp or
+                the like.
+              '';
+          };
+        };});
+      };
+
+      # TODO: Add support for redudp and dnstc
+    };
+  };
+
+  ##### implementation
+  config = let
+    redsocks_blocks = concatMapStrings (block:
+      let proxy = splitString ":" block.proxy; in
+      ''
+        redsocks {
+          local_ip = ${block.ip};
+          local_port = ${toString block.port};
+
+          ip = ${elemAt proxy 0};
+          port = ${elemAt proxy 1};
+          type = ${block.type};
+
+          ${optionalString (block.login != null) "login = \"${block.login}\";"}
+          ${optionalString (block.password != null) "password = \"${block.password}\";"}
+
+          disclose_src = ${block.disclose_src};
+        }
+      '') cfg.redsocks;
+    configfile = pkgs.writeText "redsocks.conf"
+      ''
+        base {
+          log_debug = ${if cfg.log_debug then "on" else "off" };
+          log_info = ${if cfg.log_info then "on" else "off" };
+          log = ${cfg.log};
+
+          daemon = off;
+          redirector = iptables;
+
+          user = redsocks;
+          group = redsocks;
+          ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"}
+        }
+
+        ${redsocks_blocks}
+      '';
+    internetOnly = [ # TODO: add ipv6-equivalent
+      "-d 0.0.0.0/8"
+      "-d 10.0.0.0/8"
+      "-d 127.0.0.0/8"
+      "-d 169.254.0.0/16"
+      "-d 172.16.0.0/12"
+      "-d 192.168.0.0/16"
+      "-d 224.168.0.0/4"
+      "-d 240.168.0.0/4"
+    ];
+    redCond = block:
+      optionalString (isString block.redirectCondition) block.redirectCondition;
+    iptables = concatImapStrings (idx: block:
+      let chain = "REDSOCKS${toString idx}"; doNotRedirect =
+        concatMapStringsSep "\n"
+          (f: "ip46tables -t nat -A ${chain} ${f} -j RETURN 2>/dev/null || true")
+          (block.doNotRedirect ++ (optionals block.redirectInternetOnly internetOnly));
+      in
+      optionalString (block.redirectCondition != false)
+        ''
+          ip46tables -t nat -F ${chain} 2>/dev/null || true
+          ip46tables -t nat -N ${chain} 2>/dev/null || true
+          ${doNotRedirect}
+          ip46tables -t nat -A ${chain} -p tcp -j REDIRECT --to-ports ${toString block.port}
+
+          # TODO: show errors, when it will be easily possible by a switch to
+          # iptables-restore
+          ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true
+        ''
+    ) cfg.redsocks;
+  in
+    mkIf cfg.enable {
+      users.groups.redsocks = {};
+      users.users.redsocks = {
+        description = "Redsocks daemon";
+        group = "redsocks";
+        isSystemUser = true;
+      };
+
+      systemd.services.redsocks = {
+        description = "Redsocks";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}";
+      };
+
+      networking.firewall.extraCommands = iptables;
+
+      networking.firewall.extraStopCommands =
+        concatImapStringsSep "\n" (idx: block:
+          let chain = "REDSOCKS${toString idx}"; in
+          optionalString (block.redirectCondition != false)
+            "ip46tables -t nat -D OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true"
+        ) cfg.redsocks;
+    };
+}
diff --git a/nixos/modules/services/networking/rpcbind.nix b/nixos/modules/services/networking/rpcbind.nix
index eef1e8e8cd88..cddcb09054e0 100644
--- a/nixos/modules/services/networking/rpcbind.nix
+++ b/nixos/modules/services/networking/rpcbind.nix
@@ -2,35 +2,6 @@
 
 with lib;
 
-let
-
-  netconfigFile = {
-    target = "netconfig";
-    source = pkgs.writeText "netconfig" ''
-      #
-      # The network configuration file. This file is currently only used in
-      # conjunction with the TI-RPC code in the libtirpc library.
-      #
-      # Entries consist of:
-      #
-      #       <network_id> <semantics> <flags> <protofamily> <protoname> \
-      #               <device> <nametoaddr_libs>
-      #
-      # The <device> and <nametoaddr_libs> fields are always empty in this
-      # implementation.
-      #
-      udp        tpi_clts      v     inet     udp     -       -
-      tcp        tpi_cots_ord  v     inet     tcp     -       -
-      udp6       tpi_clts      v     inet6    udp     -       -
-      tcp6       tpi_cots_ord  v     inet6    tcp     -       -
-      rawip      tpi_raw       -     inet      -      -       -
-      local      tpi_cots_ord  -     loopback  -      -       -
-      unix       tpi_cots_ord  -     loopback  -      -       -
-    '';
-  };
-
-in
-
 {
 
   ###### interface
@@ -58,25 +29,18 @@ in
   ###### implementation
 
   config = mkIf config.services.rpcbind.enable {
-
     environment.systemPackages = [ pkgs.rpcbind ];
 
-    environment.etc = [ netconfigFile ];
-
-    systemd.services.rpcbind =
-      { description = "ONC RPC Directory Service";
+    systemd.packages = [ pkgs.rpcbind ];
 
-        wantedBy = [ "multi-user.target" ];
-
-        requires = [ "basic.target" ];
-        after = [ "basic.target" ];
-
-        unitConfig.DefaultDependencies = false; # don't stop during shutdown
-
-        serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = "@${pkgs.rpcbind}/bin/rpcbind rpcbind";
-      };
+    systemd.services.rpcbind = {
+      wantedBy = [ "multi-user.target" ];
+    };
 
+    users.extraUsers.rpc = {
+      group = "nogroup";
+      uid = config.ids.uids.rpc;
+    };
   };
 
 }
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
index b29db58af99b..e0eef9ed96f6 100644
--- a/nixos/modules/services/networking/searx.nix
+++ b/nixos/modules/services/networking/searx.nix
@@ -18,15 +18,12 @@ in
 
     services.searx = {
 
-      enable = mkOption {
-        default = false;
-        description = "
-          Whether to enable the Searx server. See https://github.com/asciimoo/searx
-        ";
-      };
+      enable = mkEnableOption
+        "the searx server. See https://github.com/asciimoo/searx";
 
       configFile = mkOption {
-        default = "";
+        type = types.nullOr types.path;
+        default = null;
         description = "
           The path of the Searx server configuration file. If no file
           is specified, a default file is used (default config file has
@@ -34,6 +31,13 @@ in
         ";
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.pythonPackages.searx;
+        defaultText = "pkgs.pythonPackages.searx";
+        description = "searx package to use.";
+      };
+
     };
 
   };
@@ -61,14 +65,13 @@ in
         wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           User = "searx";
-          ExecStart = "${pkgs.pythonPackages.searx}/bin/searx-run";
+          ExecStart = "${cfg.package}/bin/searx-run";
         };
-      } // (optionalAttrs (configFile != "") {
+      } // (optionalAttrs (configFile != null) {
         environment.SEARX_SETTINGS_PATH = configFile;
       });
-        
 
-    environment.systemPackages = [ pkgs.pythonPackages.searx ];
+    environment.systemPackages = [ cfg.package ];
 
   };
 
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index 04312c39062f..c5c131cb4c50 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -226,7 +226,7 @@ in
       sendmail = mkOption {
         type = types.nullOr types.path;
         default = null;
-        example = "/var/setuid-wrappers/sendmail";
+        example = "/run/wrappers/bin/sendmail";
         description = "Use this sendmail compatible script to deliver alerts";
       };
       smokeMailTemplate = mkOption {
@@ -273,7 +273,10 @@ in
         message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
       }
     ];
-    security.setuidPrograms = [ "fping" ];
+    security.wrappers = {
+      fping.source = "${pkgs.fping}/bin/fping";
+      "fping6".source = "${pkgs.fping}/bin/fping6";
+    };
     environment.systemPackages = [ pkgs.fping ];
     users.extraUsers = singleton {
       name = cfg.user;
@@ -285,8 +288,11 @@ in
     };
     systemd.services.smokeping = {
       wantedBy = [ "multi-user.target"];
-      serviceConfig.User = cfg.user;
-      serviceConfig.PermissionsStartOnly = true;
+      serviceConfig = {
+        User = cfg.user;
+        PermissionsStartOnly = true;
+        Restart = "on-failure";
+      };
       preStart = ''
         mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
         rm -f ${smokepingHome}/cropper
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 80659f19c597..bebf4e145ca8 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -240,7 +240,7 @@ in
 
     systemd =
       let
-        sshd-service =
+        service =
           { description = "SSH Daemon";
 
             wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
@@ -251,8 +251,20 @@ in
 
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
-            wants = [ "sshd-keygen.service" ];
-            after = [ "sshd-keygen.service" ];
+            preStart =
+              ''
+                # Make sure we don't write to stdout, since in case of
+                # socket activation, it goes to the remote side (#19589).
+                exec >&2
+
+                mkdir -m 0755 -p /etc/ssh
+
+                ${flip concatMapStrings cfg.hostKeys (k: ''
+                  if ! [ -f "${k.path}" ]; then
+                      ssh-keygen -t "${k.type}" ${if k ? bits then "-b ${toString k.bits}" else ""} -f "${k.path}" -N ""
+                  fi
+                '')}
+              '';
 
             serviceConfig =
               { ExecStart =
@@ -262,31 +274,12 @@ in
                 KillMode = "process";
               } // (if cfg.startWhenNeeded then {
                 StandardInput = "socket";
+                StandardError = "journal";
               } else {
                 Restart = "always";
                 Type = "simple";
               });
           };
-
-        sshd-keygen-service =
-          { description = "SSH Host Key Generation";
-            path = [ cfgc.package ];
-            script =
-            ''
-              mkdir -m 0755 -p /etc/ssh
-              ${flip concatMapStrings cfg.hostKeys (k: ''
-                if ! [ -f "${k.path}" ]; then
-                  ssh-keygen -t "${k.type}" ${if k ? bits then "-b ${toString k.bits}" else ""} -f "${k.path}" -N ""
-                fi
-              '')}
-            '';
-
-            serviceConfig = {
-              Type = "oneshot";
-              RemainAfterExit = "yes";
-            };
-          };
-
       in
 
       if cfg.startWhenNeeded then {
@@ -298,13 +291,11 @@ in
             socketConfig.Accept = true;
           };
 
-        services.sshd-keygen = sshd-keygen-service;
-        services."sshd@" = sshd-service;
+        services."sshd@" = service;
 
       } else {
 
-        services.sshd-keygen = sshd-keygen-service;
-        services.sshd = sshd-service;
+        services.sshd = service;
 
       };
 
@@ -325,8 +316,6 @@ in
 
         UsePAM yes
 
-        UsePrivilegeSeparation sandbox
-
         AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
         ${concatMapStrings (port: ''
           Port ${toString port}
diff --git a/nixos/modules/services/networking/sslh.nix b/nixos/modules/services/networking/sslh.nix
index bd584a3a85d3..e3d65c49fbf2 100644
--- a/nixos/modules/services/networking/sslh.nix
+++ b/nixos/modules/services/networking/sslh.nix
@@ -5,7 +5,7 @@ with lib;
 let
   cfg = config.services.sslh;
   configFile = pkgs.writeText "sslh.conf" ''
-    verbose: ${if cfg.verbose then "true" else "false"};
+    verbose: ${boolToString cfg.verbose};
     foreground: true;
     inetd: false;
     numeric: false;
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 0c459fb1dd0c..31d11548f195 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -82,7 +82,8 @@ in
           configFile = {
   
             path = mkOption {
-              type = types.path;
+              type = types.nullOr types.path;
+              default = null;
               example = literalExample "/etc/wpa_supplicant.conf";
               description = ''
                 External <literal>wpa_supplicant.conf</literal> configuration file.
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index f8e68fda7fc2..6cb40185274d 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -18,7 +18,7 @@ in
 
       networks = mkOption {
         default = { };
-        type = with types; loaOf (submodule {
+        type = with types; attrsOf (submodule {
           options = {
 
             extraConfig = mkOption {
@@ -59,7 +59,7 @@ in
 
             hosts = mkOption {
               default = { };
-              type = types.loaOf types.lines;
+              type = types.attrsOf types.lines;
               description = ''
                 The name of the host in the network as well as the configuration for that host.
                 This name should only contain alphanumerics and underscores.
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index deff645d9bfd..6b3d658bd852 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -37,6 +37,9 @@ let
     (yesNoOption "anonymousUser" "anonymous_enable" false ''
       Whether to enable the anonymous FTP user.
     '')
+    (yesNoOption "anonymousUserNoPassword" "no_anon_password" false ''
+      Whether to disable the password for the anonymous FTP user.
+    '')
     (yesNoOption "localUsers" "local_enable" false ''
       Whether to enable FTP for local users.
     '')
diff --git a/nixos/modules/services/networking/xrdp.nix b/nixos/modules/services/networking/xrdp.nix
new file mode 100644
index 000000000000..bf59130ce5b9
--- /dev/null
+++ b/nixos/modules/services/networking/xrdp.nix
@@ -0,0 +1,153 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xrdp;
+  confDir = pkgs.runCommand "xrdp.conf" { } ''
+    mkdir $out
+
+    cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out
+
+    cat > $out/startwm.sh <<EOF
+    #!/bin/sh
+    . /etc/profile
+    ${cfg.defaultWindowManager}
+    EOF
+    chmod +x $out/startwm.sh
+
+    substituteInPlace $out/xrdp.ini \
+      --replace "#rsakeys_ini=" "rsakeys_ini=/var/run/xrdp/rsakeys.ini" \
+      --replace "certificate=" "certificate=${cfg.sslCert}" \
+      --replace "key_file=" "key_file=${cfg.sslKey}" \
+      --replace LogFile=xrdp.log LogFile=/dev/null \
+      --replace EnableSyslog=true EnableSyslog=false
+
+    substituteInPlace $out/sesman.ini \
+      --replace LogFile=xrdp-sesman.log LogFile=/dev/null \
+      --replace EnableSyslog=1 EnableSyslog=0
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.xrdp = {
+
+      enable = mkEnableOption "Whether xrdp should be run on startup.";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.xrdp;
+        defaultText = "pkgs.xrdp";
+        description = ''
+          The package to use for the xrdp daemon's binary.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 3389;
+        description = ''
+          Specifies on which port the xrdp daemon listens.
+        '';
+      };
+
+      sslKey = mkOption {
+        type = types.str;
+        default = "/etc/xrdp/key.pem";
+        example = "/path/to/your/key.pem";
+        description = ''
+          ssl private key path
+          A self-signed certificate will be generated if file not exists.
+        '';
+      };
+
+      sslCert = mkOption {
+        type = types.str;
+        default = "/etc/xrdp/cert.pem";
+        example = "/path/to/your/cert.pem";
+        description = ''
+          ssl certificate path
+          A self-signed certificate will be generated if file not exists.
+        '';
+      };
+
+      defaultWindowManager = mkOption {
+        type = types.str;
+        default = "xterm";
+        example = "xfce4-session";
+        description = ''
+          The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session"
+          This is per-user overridable, if file ~/startwm.sh exists it will be used instead.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd = {
+      services.xrdp = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        description = "xrdp daemon";
+        requires = [ "xrdp-sesman.service" ];
+        preStart = ''
+          # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
+          mkdir -p /tmp/.xrdp || true
+          chown xrdp:xrdp /tmp/.xrdp
+          chmod 3777 /tmp/.xrdp
+
+          # generate a self-signed certificate
+          if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
+            mkdir -p $(dirname ${cfg.sslCert}) || true
+            mkdir -p $(dirname ${cfg.sslKey}) || true
+            ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
+              -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
+              -config ${cfg.package}/share/xrdp/openssl.conf \
+              -keyout ${cfg.sslKey} -out ${cfg.sslCert}
+            chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
+            chmod 440 ${cfg.sslKey} ${cfg.sslCert}
+          fi
+          if [ ! -s /var/run/xrdp/rsakeys.ini ]; then
+            mkdir -p /var/run/xrdp
+            ${cfg.package}/bin/xrdp-keygen xrdp /var/run/xrdp/rsakeys.ini
+          fi
+        '';
+        serviceConfig = {
+          User = "xrdp";
+          Group = "xrdp";
+          PermissionsStartOnly = true;
+          ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini";
+        };
+      };
+
+      services.xrdp-sesman = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        description = "xrdp session manager";
+        serviceConfig = {
+          ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini";
+        };
+      };
+
+    };
+
+    users.users.xrdp = {
+      description   = "xrdp daemon user";
+      isSystemUser  = true;
+      group         = "xrdp";
+    };
+    users.groups.xrdp = {};
+
+    security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };
+  };
+
+}
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
index 76ba78ff366f..1c63d800e260 100644
--- a/nixos/modules/services/networking/znc.nix
+++ b/nixos/modules/services/networking/znc.nix
@@ -35,7 +35,7 @@ let
             Port = ${toString confOpts.port}
             IPv4 = true
             IPv6 = true
-            SSL = ${if confOpts.useSSL then "true" else "false"}
+            SSL = ${boolToString confOpts.useSSL}
     </Listener>
     
     <User ${confOpts.userName}>
@@ -132,7 +132,6 @@ in
     services.znc = {
       enable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Enable a ZNC service for a user.
@@ -208,7 +207,7 @@ in
 
         networks = mkOption {
           default = { };
-          type = with types; loaOf (submodule networkOpts);
+          type = with types; attrsOf (submodule networkOpts);
           description = ''
             IRC networks to connect the user to.
           '';
@@ -251,7 +250,6 @@ in
 
         useSSL = mkOption {
           default = true;
-          example = true;
           type = types.bool;
           description = ''
             Indicates whether the ZNC server should use SSL when listening on the specified port. A self-signed certificate will be generated.
@@ -278,7 +276,6 @@ in
  
       mutable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Indicates whether to allow the contents of the `dataDir` directory to be changed
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 3041dccfd154..7ce2ae38fb36 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -52,6 +52,7 @@ let
 
     ServerBin ${bindir}/lib/cups
     DataDir ${bindir}/share/cups
+    DocumentRoot ${cups.out}/share/doc/cups
 
     AccessLog syslog
     ErrorLog syslog
diff --git a/nixos/modules/services/scheduling/atd.nix b/nixos/modules/services/scheduling/atd.nix
index 2070b2ffa018..0216c9771c96 100644
--- a/nixos/modules/services/scheduling/atd.nix
+++ b/nixos/modules/services/scheduling/atd.nix
@@ -42,13 +42,14 @@ in
 
   config = mkIf cfg.enable {
 
-    security.setuidOwners = map (program: {
-      inherit program;
+    security.wrappers = builtins.listToAttrs (
+      map (program: { name = "${program}"; value = {
+      source = "${at}/bin/${program}";
       owner = "atd";
       group = "atd";
       setuid = true;
       setgid = true;
-    }) [ "at" "atq" "atrm" "batch" ];
+    };}) [ "at" "atq" "atrm" "batch" ]);
 
     environment.systemPackages = [ at ];
 
diff --git a/nixos/modules/services/scheduling/cron.nix b/nixos/modules/services/scheduling/cron.nix
index f5e132fd77d8..7b1a1599e141 100644
--- a/nixos/modules/services/scheduling/cron.nix
+++ b/nixos/modules/services/scheduling/cron.nix
@@ -20,7 +20,7 @@ let
   cronNixosPkg = pkgs.cron.override {
     # The mail.nix nixos module, if there is any local mail system enabled,
     # should have sendmail in this path.
-    sendmailPath = "/var/setuid-wrappers/sendmail";
+    sendmailPath = "/run/wrappers/bin/sendmail";
   };
 
   allFiles =
@@ -39,7 +39,7 @@ in
 
       enable = mkOption {
         type = types.bool;
-        example = true;
+        default = false;
         description = "Whether to enable the Vixie cron daemon.";
       };
 
@@ -61,7 +61,7 @@ in
           A list of Cron jobs to be appended to the system-wide
           crontab.  See the manual page for crontab for the expected
           format. If you want to get the results mailed you must setuid
-          sendmail. See <option>security.setuidOwners</option>
+          sendmail. See <option>security.wrappers</option>
 
           If neither /var/cron/cron.deny nor /var/cron/cron.allow exist only root
           will is allowed to have its own crontab file. The /var/cron/cron.deny file
@@ -92,13 +92,9 @@ in
   config = mkMerge [
 
     { services.cron.enable = mkDefault (allFiles != []); }
-
     (mkIf (config.services.cron.enable) {
-
-      security.setuidPrograms = [ "crontab" ];
-
+      security.wrappers.crontab.source = "${cronNixosPkg}/bin/crontab";
       environment.systemPackages = [ cronNixosPkg ];
-
       environment.etc.crontab =
         { source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; }
             ''
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
index 7b4665a82046..af4f9f41fd04 100644
--- a/nixos/modules/services/scheduling/fcron.nix
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -23,7 +23,8 @@ let
   allowdeny = target: users:
     { source = pkgs.writeText "fcron.${target}" (concatStringsSep "\n" users);
       target = "fcron.${target}";
-      mode = "600"; # fcron has some security issues.. So I guess this is most safe
+      mode = "644";
+      gid = config.ids.gids.fcron;
     };
 
 in
@@ -89,25 +90,48 @@ in
       [ (allowdeny "allow" (cfg.allow))
         (allowdeny "deny" cfg.deny)
         # see man 5 fcron.conf
-        { source = pkgs.writeText "fcon.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    =       /var/setuid-wrappers/sendmail
-            editor      =       /run/current-system/sw/bin/vi
+            sendmail    =       /run/wrappers/bin/sendmail
+            editor      =       ${pkgs.vim}/bin/vim
           '';
           target = "fcron.conf";
-          mode = "0600"; # max allowed is 644
+          gid = config.ids.gids.fcron;
+          mode = "0644";
         }
       ];
 
     environment.systemPackages = [ pkgs.fcron ];
-
-    security.setuidPrograms = [ "fcrontab" ];
-
+    users.extraUsers.fcron = {
+      uid = config.ids.uids.fcron;
+      home = "/var/spool/fcron";
+      group = "fcron";
+    };
+    users.groups.fcron.gid = config.ids.gids.fcron;
+
+    security.wrappers = {
+      fcrontab = {
+        source = "${pkgs.fcron}/bin/fcrontab";
+        owner = "fcron";
+        group = "fcron";
+        setgid = true;
+      };
+      fcrondyn = {
+        source = "${pkgs.fcron}/bin/fcrondyn";
+        owner = "fcron";
+        group = "fcron";
+        setgid = true;
+      };
+      fcronsighup = {
+        source = "${pkgs.fcron}/bin/fcronsighup";
+        group = "fcron";
+      };
+    };
     systemd.services.fcron = {
       description = "fcron daemon";
       after = [ "local-fs.target" ];
@@ -119,14 +143,19 @@ in
       };
 
       preStart = ''
-        ${pkgs.coreutils}/bin/mkdir -m 0700 -p /var/spool/fcron
+        install \
+          --mode 0770 \
+          --owner fcron \
+          --group fcron \
+          --directory /var/spool/fcron
         # load system crontab file
-        ${pkgs.fcron}/bin/fcrontab -u systab ${pkgs.writeText "systab" cfg.systab}
+        /run/wrappers/bin/fcrontab -u systab ${pkgs.writeText "systab" cfg.systab}
       '';
 
-      serviceConfig.Type = "forking";
-
-      script = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
+      };
     };
   };
 }
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index b045e140546d..f71f30fee97a 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -81,6 +81,7 @@ in
     users.extraUsers = singleton {
       name = clamavUser;
       uid = config.ids.uids.clamav;
+      group = clamavGroup;
       description = "ClamAV daemon user";
       home = stateDir;
     };
diff --git a/nixos/modules/services/security/haka.nix b/nixos/modules/services/security/haka.nix
index f48a79b1f7f1..b64a1b4d03e0 100644
--- a/nixos/modules/services/security/haka.nix
+++ b/nixos/modules/services/security/haka.nix
@@ -99,7 +99,6 @@ in
 
       pcap = mkOption {
         default = true;
-        example = false;
         type = types.bool;
         description = "Whether to enable pcap";
       };
diff --git a/nixos/modules/services/security/hologram-agent.nix b/nixos/modules/services/security/hologram-agent.nix
new file mode 100644
index 000000000000..49b5c935267b
--- /dev/null
+++ b/nixos/modules/services/security/hologram-agent.nix
@@ -0,0 +1,57 @@
+{pkgs, config, lib, ...}:
+
+with lib;
+
+let
+  cfg = config.services.hologram-agent;
+
+  cfgFile = pkgs.writeText "hologram-agent.json" (builtins.toJSON {
+    host = cfg.dialAddress;
+  });
+in {
+  options = {
+    services.hologram-agent = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Hologram agent for AWS instance credentials";
+      };
+
+      dialAddress = mkOption {
+        type        = types.str;
+        default     = "localhost:3100";
+        description = "Hologram server and port.";
+      };
+
+      httpPort = mkOption {
+        type        = types.str;
+        default     = "80";
+        description = "Port for metadata service to listen on.";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.interfaces.dummy0 = {
+      ipAddress = "169.254.169.254";
+      prefixLength = 32;
+    };
+
+    systemd.services.hologram-agent = {
+      description = "Provide EC2 instance credentials to machines outside of EC2";
+      after       = [ "network.target" ];
+      wantedBy    = [ "multi-user.target" ];
+      requires    = [ "network-link-dummy0.service" "network-addresses-dummy0.service" ]; 
+      preStart = ''
+        /run/current-system/sw/bin/rm -fv /var/run/hologram.sock
+      '';
+      serviceConfig = {
+        ExecStart = "${pkgs.hologram.bin}/bin/hologram-agent -debug -conf ${cfgFile} -port ${cfg.httpPort}";
+      };
+    };
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ nand0p ];
+}
diff --git a/nixos/modules/services/security/hologram.nix b/nixos/modules/services/security/hologram-server.nix
index e267fed27955..e267fed27955 100644
--- a/nixos/modules/services/security/hologram.nix
+++ b/nixos/modules/services/security/hologram-server.nix
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index caa7d9d50812..e292fd9851e3 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -10,9 +10,6 @@ let
   #   repeatedArgs (arg: "--arg=${arg}") args
   repeatedArgs = concatMapStringsSep " ";
 
-  # 'toString' doesn't quite do what we want for bools.
-  fromBool = x: if x then "true" else "false";
-
   # oauth2_proxy provides many options that are only relevant if you are using
   # a certain provider. This set maps from provider name to a function that
   # takes the configuration and returns a string that can be inserted into the
@@ -49,24 +46,24 @@ let
     --client-secret='${cfg.clientSecret}' \
     ${optionalString (!isNull cfg.cookie.domain) "--cookie-domain='${cfg.cookie.domain}'"} \
     --cookie-expire='${cfg.cookie.expire}' \
-    --cookie-httponly=${fromBool cfg.cookie.httpOnly} \
+    --cookie-httponly=${boolToString cfg.cookie.httpOnly} \
     --cookie-name='${cfg.cookie.name}' \
     --cookie-secret='${cfg.cookie.secret}' \
-    --cookie-secure=${fromBool cfg.cookie.secure} \
+    --cookie-secure=${boolToString cfg.cookie.secure} \
     ${optionalString (!isNull cfg.cookie.refresh) "--cookie-refresh='${cfg.cookie.refresh}'"} \
     ${optionalString (!isNull cfg.customTemplatesDir) "--custom-templates-dir='${cfg.customTemplatesDir}'"} \
     ${repeatedArgs (x: "--email-domain='${x}'") cfg.email.domains} \
     --http-address='${cfg.httpAddress}' \
-    ${optionalString (!isNull cfg.htpasswd.file) "--htpasswd-file='${cfg.htpasswd.file}' --display-htpasswd-form=${fromBool cfg.htpasswd.displayForm}"} \
+    ${optionalString (!isNull cfg.htpasswd.file) "--htpasswd-file='${cfg.htpasswd.file}' --display-htpasswd-form=${boolToString cfg.htpasswd.displayForm}"} \
     ${optionalString (!isNull cfg.loginURL) "--login-url='${cfg.loginURL}'"} \
-    --pass-access-token=${fromBool cfg.passAccessToken} \
-    --pass-basic-auth=${fromBool cfg.passBasicAuth} \
-    --pass-host-header=${fromBool cfg.passHostHeader} \
+    --pass-access-token=${boolToString cfg.passAccessToken} \
+    --pass-basic-auth=${boolToString cfg.passBasicAuth} \
+    --pass-host-header=${boolToString cfg.passHostHeader} \
     --proxy-prefix='${cfg.proxyPrefix}' \
     ${optionalString (!isNull cfg.profileURL) "--profile-url='${cfg.profileURL}'"} \
     ${optionalString (!isNull cfg.redeemURL) "--redeem-url='${cfg.redeemURL}'"} \
     ${optionalString (!isNull cfg.redirectURL) "--redirect-url='${cfg.redirectURL}'"} \
-    --request-logging=${fromBool cfg.requestLogging} \
+    --request-logging=${boolToString cfg.requestLogging} \
     ${optionalString (!isNull cfg.scope) "--scope='${cfg.scope}'"} \
     ${repeatedArgs (x: "--skip-auth-regex='${x}'") cfg.skipAuthRegexes} \
     ${optionalString (!isNull cfg.signatureKey) "--signature-key='${cfg.signatureKey}'"} \
diff --git a/nixos/modules/services/security/physlock.nix b/nixos/modules/services/security/physlock.nix
index 34d0be3b1beb..0881483967c6 100644
--- a/nixos/modules/services/security/physlock.nix
+++ b/nixos/modules/services/security/physlock.nix
@@ -26,17 +26,7 @@ in
           This will switch to a new virtual terminal, turn off console
           switching and disable SysRq mechanism (when
           <option>services.physlock.disableSysRq</option> is set)
-          until the root or <option>services.physlock.user</option>
-          password is given.
-        '';
-      };
-
-      user = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          User whose password will be used to unlock the screen on par
-          with the root password.
+          until the root or user password is given.
         '';
       };
 
@@ -105,7 +95,7 @@ in
               ++ cfg.lockOn.extraTargets;
       serviceConfig.Type = "forking";
       script = ''
-        ${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}${optionalString (cfg.user != null) " -u ${cfg.user}"}
+        ${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}
       '';
     };
 
diff --git a/nixos/modules/services/security/sshguard.nix b/nixos/modules/services/security/sshguard.nix
new file mode 100644
index 000000000000..5a183443f71d
--- /dev/null
+++ b/nixos/modules/services/security/sshguard.nix
@@ -0,0 +1,140 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.sshguard;
+in {
+
+  ###### interface
+
+  options = {
+
+    services.sshguard = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the sshguard service.";
+      };
+
+      attack_threshold = mkOption {
+        default = 30;
+        type = types.int;
+        description = ''
+            Block attackers when their cumulative attack score exceeds threshold. Most attacks have a score of 10.
+          '';
+      };
+
+      blacklist_threshold = mkOption {
+        default = null;
+        example = 120;
+        type = types.nullOr types.int;
+        description = ''
+            Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file.
+          '';
+      };
+
+      blacklist_file = mkOption {
+        default = "/var/lib/sshguard/blacklist.db";
+        type = types.path;
+        description = ''
+            Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file.
+          '';
+      };
+
+      blocktime = mkOption {
+        default = 120;
+        type = types.int;
+        description = ''
+            Block attackers for initially blocktime seconds after exceeding threshold. Subsequent blocks increase by a factor of 1.5.
+
+            sshguard unblocks attacks at random intervals, so actual block times will be longer.
+          '';
+      };
+
+      detection_time = mkOption {
+        default = 1800;
+        type = types.int;
+        description = ''
+            Remember potential attackers for up to detection_time seconds before resetting their score.
+          '';
+      };
+
+      whitelist = mkOption {
+        default = [ ];
+        example = [ "198.51.100.56" "198.51.100.2" ];
+        type = types.listOf types.str;
+        description = ''
+            Whitelist a list of addresses, hostnames, or address blocks.
+          '';
+      };
+
+      services = mkOption {
+        default = [ "sshd" ];
+        example = [ "sshd" "exim" ];
+        type = types.listOf types.str;
+        description = ''
+            Systemd services sshguard should receive logs of.
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.sshguard pkgs.iptables pkgs.ipset ];
+
+    environment.etc."sshguard.conf".text = let 
+        list_services = ( name:  "-t ${name} ");
+      in ''
+        BACKEND="${pkgs.sshguard}/libexec/sshg-fw-ipset"
+        LOGREADER="LANG=C ${pkgs.systemd}/bin/journalctl -afb -p info -n1 ${toString (map list_services cfg.services)} -o cat"
+      '';
+
+    systemd.services.sshguard =
+      { description = "SSHGuard brute-force attacks protection system";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        partOf = optional config.networking.firewall.enable "firewall.service";
+
+        path = [ pkgs.iptables pkgs.ipset pkgs.iproute pkgs.systemd ];
+
+        postStart = ''
+          mkdir -p /var/lib/sshguard
+          ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard4 hash:ip family inet
+          ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard6 hash:ip family inet6
+          ${pkgs.iptables}/bin/iptables -I INPUT -m set --match-set sshguard4 src -j DROP
+          ${pkgs.iptables}/bin/ip6tables -I INPUT -m set --match-set sshguard6 src -j DROP
+        '';
+
+        preStop = ''
+          ${pkgs.iptables}/bin/iptables -D INPUT -m set --match-set sshguard4 src -j DROP
+          ${pkgs.iptables}/bin/ip6tables -D INPUT -m set --match-set sshguard6 src -j DROP
+        '';
+
+        unitConfig.Documentation = "man:sshguard(8)";
+
+        serviceConfig = {
+            Type = "simple";
+            ExecStart = let
+                list_whitelist = ( name:  "-w ${name} ");
+              in ''
+                 ${pkgs.sshguard}/bin/sshguard -a ${toString cfg.attack_threshold} ${optionalString (cfg.blacklist_threshold != null) "-b ${toString cfg.blacklist_threshold}:${cfg.blacklist_file} "}-i /run/sshguard/sshguard.pid -p ${toString cfg.blocktime} -s ${toString cfg.detection_time} ${toString (map list_whitelist cfg.whitelist)}
+              '';
+            PIDFile = "/run/sshguard/sshguard.pid";
+            Restart = "always";
+
+            ReadOnlyDirectories = "/";
+            ReadWriteDirectories = "/run/sshguard /var/lib/sshguard";
+            RuntimeDirectory = "sshguard";
+            CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
+         };
+      };
+  };
+}
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 47f863b96df2..10596d6431d0 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -12,6 +12,10 @@ let
   torRc = ''
     User tor
     DataDirectory ${torDirectory}
+    ${optionalString cfg.enableGeoIP ''
+      GeoIPFile ${pkgs.tor.geoip}/share/tor/geoip
+      GeoIPv6File ${pkgs.tor.geoip}/share/tor/geoip6
+    ''}
 
     ${optint "ControlPort" cfg.controlPort}
   ''
@@ -58,6 +62,18 @@ in
         '';
       };
 
+      enableGeoIP = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whenever to configure Tor daemon to use GeoIP databases.
+
+          Disabling this will disable by-country statistics for
+          bridges and relays and some client and third-party software
+          functionality.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -124,6 +140,7 @@ in
         };
 
         privoxy.enable = mkOption {
+          type = types.bool;
           default = true;
           description = ''
             Whether to enable and configure the system Privoxy to use Tor's
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
index a81cdbbc172f..a29cb3f33dae 100644
--- a/nixos/modules/services/security/torify.nix
+++ b/nixos/modules/services/security/torify.nix
@@ -19,15 +19,23 @@ in
 {
 
   ###### interface
-  
+
   options = {
-  
+
     services.tor.tsocks = {
 
       enable = mkOption {
-        default = cfg.enable && cfg.client.enable;
+        default = false;
         description = ''
-          Whether to build tsocks wrapper script to relay application traffic via TOR.
+          Whether to build tsocks wrapper script to relay application traffic via Tor.
+
+          <important>
+            <para>You shouldn't use this unless you know what you're
+            doing because your installation of Tor already comes with
+            its own superior (doesn't leak DNS queries)
+            <literal>torsocks</literal> wrapper which does pretty much
+            exactly the same thing as this.</para>
+          </important>
         '';
       };
 
diff --git a/nixos/modules/services/system/dbus-session-local.conf.in b/nixos/modules/services/system/dbus-session-local.conf.in
deleted file mode 100644
index 5fd6f80a3539..000000000000
--- a/nixos/modules/services/system/dbus-session-local.conf.in
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<busconfig>
-  @extra@
-</busconfig>
diff --git a/nixos/modules/services/system/dbus-system-local.conf.in b/nixos/modules/services/system/dbus-system-local.conf.in
deleted file mode 100644
index edbb476f585a..000000000000
--- a/nixos/modules/services/system/dbus-system-local.conf.in
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<busconfig>
-  <servicehelper>@servicehelper@</servicehelper>
-  @extra@
-</busconfig>
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index a7cf74c15cc5..643bec188142 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -10,42 +10,10 @@ let
 
   homeDir = "/run/dbus";
 
-  systemExtraxml = concatStrings (flip concatMap cfg.packages (d: [
-    "<servicedir>${d}/share/dbus-1/system-services</servicedir>"
-    "<includedir>${d}/etc/dbus-1/system.d</includedir>"
-  ]));
-
-  sessionExtraxml = concatStrings (flip concatMap cfg.packages (d: [
-    "<servicedir>${d}/share/dbus-1/services</servicedir>"
-    "<includedir>${d}/etc/dbus-1/session.d</includedir>"
-  ]));
-
-  daemonArgs = "--address=systemd: --nofork --nopidfile --systemd-activation";
-
-  configDir = pkgs.runCommand "dbus-conf"
-    { preferLocalBuild = true;
-      allowSubstitutes = false;
-    }
-    ''
-      mkdir -p $out
-
-      cp ${pkgs.dbus.out}/share/dbus-1/{system,session}.conf $out
-
-      # avoid circular includes
-      sed -ri 's@(<include ignore_missing="yes">/etc/dbus-1/(system|session)\.conf</include>)@<!-- \1 -->@g' $out/{system,session}.conf
-
-      # include by full path
-      sed -ri "s@/etc/dbus-1/(system|session)-@$out/\1-@" $out/{system,session}.conf
-
-      sed '${./dbus-system-local.conf.in}' \
-        -e 's,@servicehelper@,${config.security.wrapperDir}/dbus-daemon-launch-helper,g' \
-        -e 's,@extra@,${systemExtraxml},' \
-        > "$out/system-local.conf"
-
-      sed '${./dbus-session-local.conf.in}' \
-        -e 's,@extra@,${sessionExtraxml},' \
-        > "$out/session-local.conf"
-    '';
+  configDir = pkgs.makeDBusConf {
+    suidHelper = "${config.security.wrapperDir}/dbus-daemon-launch-helper";
+    serviceDirectories = cfg.packages;
+  };
 
 in
 
@@ -114,15 +82,14 @@ in
 
     systemd.packages = [ pkgs.dbus.daemon ];
 
-    security.setuidOwners = singleton
-      { program = "dbus-daemon-launch-helper";
-        source = "${pkgs.dbus.daemon}/libexec/dbus-daemon-launch-helper";
-        owner = "root";
-        group = "messagebus";
-        setuid = true;
-        setgid = false;
-        permissions = "u+rx,g+rx,o-rx";
-      };
+    security.wrappers.dbus-daemon-launch-helper = {
+      source = "${pkgs.dbus.daemon}/libexec/dbus-daemon-launch-helper";
+      owner = "root";
+      group = "messagebus";
+      setuid = true;
+      setgid = false;
+      permissions = "u+rx,g+rx,o-rx";
+    };
 
     services.dbus.packages = [
       pkgs.dbus.out
@@ -133,10 +100,6 @@ in
       # Don't restart dbus-daemon. Bad things tend to happen if we do.
       reloadIfChanged = true;
       restartTriggers = [ configDir ];
-      serviceConfig.ExecStart = [
-        ""
-        "${lib.getBin pkgs.dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf ${daemonArgs}"
-      ];
     };
 
     systemd.user = {
@@ -144,10 +107,6 @@ in
         # Don't restart dbus-daemon. Bad things tend to happen if we do.
         reloadIfChanged = true;
         restartTriggers = [ configDir ];
-        serviceConfig.ExecStart = [
-          ""
-          "${lib.getBin pkgs.dbus}/bin/dbus-daemon --config-file=${configDir}/session.conf ${daemonArgs}"
-        ];
       };
       sockets.dbus.wantedBy = mkIf cfg.socketActivated [ "sockets.target" ];
     };
diff --git a/nixos/modules/services/system/earlyoom.nix b/nixos/modules/services/system/earlyoom.nix
new file mode 100644
index 000000000000..daa46838bfa8
--- /dev/null
+++ b/nixos/modules/services/system/earlyoom.nix
@@ -0,0 +1,96 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  ecfg = config.services.earlyoom;
+in
+{
+  options = {
+    services.earlyoom = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable early out of memory killing.
+        '';
+      };
+
+      freeMemThreshold = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Minimum of availabe memory (in percent).
+          If the free memory falls below this threshold and the analog is true for
+          <option>services.earlyoom.freeSwapThreshold</option>
+          the killing begins.
+        '';
+      };
+
+      freeSwapThreshold = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Minimum of availabe swap space (in percent).
+          If the available swap space falls below this threshold and the analog
+          is true for <option>services.earlyoom.freeMemThreshold</option>
+          the killing begins.
+        '';
+      };
+
+      useKernelOOMKiller= mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use kernel OOM killer instead of own user-space implementation.
+        '';
+      };
+
+      ignoreOOMScoreAdjust = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Ignore oom_score_adjust values of processes.
+          User-space implementation only.
+        '';
+      };
+
+      enableDebugInfo = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable debugging messages.
+        '';
+      };
+    };
+  };
+
+  config = mkIf ecfg.enable {
+    assertions = [
+      { assertion = ecfg.freeMemThreshold > 0 && ecfg.freeMemThreshold <= 100;
+        message = "Needs to be a positive percentage"; }
+      { assertion = ecfg.freeSwapThreshold > 0 && ecfg.freeSwapThreshold <= 100;
+        message = "Needs to be a positive percentage"; }
+      { assertion = !ecfg.useKernelOOMKiller || !ecfg.ignoreOOMScoreAdjust;
+        message = "Both options in conjunction do not make sense"; }
+    ];
+
+    systemd.services.earlyoom = {
+      description = "Early OOM Daemon for Linux";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        StandardOutput = "null";
+        StandardError = "syslog";
+        ExecStart = ''
+          ${pkgs.earlyoom}/bin/earlyoom \
+          -m ${toString ecfg.freeMemThreshold} \
+          -s ${toString ecfg.freeSwapThreshold} \
+          ${optionalString ecfg.useKernelOOMKiller "-k"} \
+          ${optionalString ecfg.ignoreOOMScoreAdjust "-i"} \
+          ${optionalString ecfg.enableDebugInfo "-d"}
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index a9c08b66eb82..2534f09b555a 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -13,7 +13,6 @@ in {
       deluge = {
         enable = mkOption {
           default = false;
-          example = true;
           description = "Start the Deluge daemon";
         };
 
@@ -29,7 +28,6 @@ in {
       deluge.web = {
         enable = mkOption {
           default = false;
-          example = true;
           description = ''
             Start Deluge Web daemon.
           '';
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 5b2e4ca16066..1f319df794b5 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -15,8 +15,7 @@ let
 
   # Strings must be quoted, ints and bools must not (for settings.json).
   toOption = x:
-    if x == true then "true"
-    else if x == false then "false"
+    if isBool x then boolToString x
     else if isInt x then toString x
     else toString ''"${x}"'';
 
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index ba25f9128445..8bad42927e3f 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -76,7 +76,7 @@ in {
           ln -s ${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service $out/autovt@.service
         '';
 
-    systemd.services.systemd-vconsole-setup.restartIfChanged = false;
+    systemd.services.systemd-vconsole-setup.enable = false;
 
     services.kmscon.extraConfig = mkIf cfg.hwRender ''
       drm
diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix
index 2d9287577de8..c1d7d4ea06d4 100644
--- a/nixos/modules/services/web-apps/atlassian/confluence.nix
+++ b/nixos/modules/services/web-apps/atlassian/confluence.nix
@@ -103,7 +103,7 @@ in
       requires = [ "postgresql.service" ];
       after = [ "postgresql.service" ];
 
-      path = [ cfg.jrePackage ];
+      path = [ cfg.jrePackage pkgs.bash ];
 
       environment = {
         CONF_USER = cfg.user;
diff --git a/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixos/modules/services/web-apps/atlassian/crowd.nix
index ada26f8057ba..b54c91d7de92 100644
--- a/nixos/modules/services/web-apps/atlassian/crowd.nix
+++ b/nixos/modules/services/web-apps/atlassian/crowd.nix
@@ -88,7 +88,6 @@ in
         secure = mkOption {
           type = types.bool;
           default = true;
-          example = false;
           description = "Whether the connections to the proxy should be considered secure.";
         };
       };
@@ -138,7 +137,7 @@ in
 
         sed -e 's,port="8095",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \
         '' + (lib.optionalString cfg.proxy.enable ''
-          -e 's,compression="on",compression="off" protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \
+          -e 's,compression="on",compression="off" protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${boolToString cfg.proxy.secure}",' \
         '') + ''
           ${pkg}/apache-tomcat/conf/server.xml.dist > ${cfg.home}/server.xml
       '';
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
index 6e31d20d0681..0ab94d95eee8 100644
--- a/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -78,7 +78,6 @@ in
         secure = mkOption {
           type = types.bool;
           default = true;
-          example = false;
           description = "Whether the connections to the proxy should be considered secure.";
         };
       };
diff --git a/nixos/modules/services/web-apps/frab.nix b/nixos/modules/services/web-apps/frab.nix
new file mode 100644
index 000000000000..d5329ef03c89
--- /dev/null
+++ b/nixos/modules/services/web-apps/frab.nix
@@ -0,0 +1,224 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.frab;
+
+  package = pkgs.frab;
+  ruby = package.ruby;
+
+  databaseConfig = builtins.toJSON { production = cfg.database; };
+
+  frabEnv = {
+    RAILS_ENV = "production";
+    RACK_ENV = "production";
+    SECRET_KEY_BASE = cfg.secretKeyBase;
+    FRAB_HOST = cfg.host;
+    FRAB_PROTOCOL = cfg.protocol;
+    FROM_EMAIL = cfg.fromEmail;
+    RAILS_SERVE_STATIC_FILES = "1";
+  } // cfg.extraEnvironment;
+
+  frab-rake = pkgs.stdenv.mkDerivation rec {
+    name = "frab-rake";
+    buildInputs = [ package.env pkgs.makeWrapper ];
+    phases = "installPhase fixupPhase";
+    installPhase = ''
+      mkdir -p $out/bin
+      makeWrapper ${package.env}/bin/bundle $out/bin/frab-bundle \
+          ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") frabEnv)} \
+          --set PATH '${lib.makeBinPath (with pkgs; [ nodejs file imagemagick ])}:$PATH' \
+          --set RAKEOPT '-f ${package}/share/frab/Rakefile' \
+          --run 'cd ${package}/share/frab'
+      makeWrapper $out/bin/frab-bundle $out/bin/frab-rake \
+          --add-flags "exec rake"
+     '';
+  };
+
+in
+
+{
+  options = {
+    services.frab = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the frab service.
+        '';
+      };
+
+      host = mkOption {
+        type = types.str;
+        example = "frab.example.com";
+        description = ''
+          Hostname under which this frab instance can be reached.
+        '';
+      };
+
+      protocol = mkOption {
+        type = types.str;
+        default = "https";
+        example = "http";
+        description = ''
+          Either http or https, depending on how your Frab instance
+          will be exposed to the public.
+        '';
+      };
+
+      fromEmail = mkOption {
+        type = types.str;
+        default = "frab@localhost";
+        description = ''
+          Email address used by frab.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Address or hostname frab should listen on.
+        '';
+      };
+
+      listenPort = mkOption {
+        type = types.int;
+        default = 3000;
+        description = ''
+          Port frab should listen on.
+        '';
+      };
+
+      statePath = mkOption {
+        type = types.str;
+        default = "/var/lib/frab";
+        description = ''
+          Directory where frab keeps its state.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "frab";
+        description = ''
+          User to run frab.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "frab";
+        description = ''
+          Group to run frab.
+        '';
+      };
+
+      secretKeyBase = mkOption {
+        type = types.str;
+        description = ''
+          Your secret key is used for verifying the integrity of signed cookies.
+          If you change this key, all old signed cookies will become invalid!
+
+          Make sure the secret is at least 30 characters and all random,
+          no regular words or you'll be exposed to dictionary attacks.
+        '';
+      };
+
+      database = mkOption {
+        type = types.attrs;
+        default = {
+          adapter = "sqlite3";
+          database = "/var/lib/frab/db.sqlite3";
+          pool = 5;
+          timeout = 5000;
+        };
+        example = {
+          adapter = "postgresql";
+          database = "frab";
+          host = "localhost";
+          username = "frabuser";
+          password = "supersecret";
+          encoding = "utf8";
+          pool = 5;
+        };
+        description = ''
+          Rails database configuration for Frab as Nix attribute set.
+        '';
+      };
+
+      extraEnvironment = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          FRAB_CURRENCY_UNIT = "€";
+          FRAB_CURRENCY_FORMAT = "%n%u";
+          EXCEPTION_EMAIL = "frab-owner@example.com";
+          SMTP_ADDRESS = "localhost";
+          SMTP_PORT = "587";
+          SMTP_DOMAIN = "localdomain";
+          SMTP_USER_NAME = "root";
+          SMTP_PASSWORD = "toor";
+          SMTP_AUTHENTICATION = "1";
+          SMTP_NOTLS = "1";
+        };
+        description = ''
+          Additional environment variables to set for frab for further
+          configuration. See the frab documentation for more information.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ frab-rake ];
+
+    users.extraUsers = [
+      { name = cfg.user;
+        group = cfg.group;
+        home = "${cfg.statePath}";
+      }
+    ];
+
+    users.extraGroups = [ { name = cfg.group; } ];
+
+    systemd.services.frab = {
+      after = [ "network.target" "gitlab.service" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = frabEnv;
+
+      preStart = ''
+        mkdir -p ${cfg.statePath}/system/attachments
+        chown ${cfg.user}:${cfg.group} -R ${cfg.statePath}
+
+        mkdir /run/frab -p
+        ln -sf ${pkgs.writeText "frab-database.yml" databaseConfig} /run/frab/database.yml
+        ln -sf ${cfg.statePath}/system /run/frab/system
+
+        if ! test -e "${cfg.statePath}/db-setup-done"; then
+          ${frab-rake}/bin/frab-rake db:setup
+          touch ${cfg.statePath}/db-setup-done
+        else
+          ${frab-rake}/bin/frab-rake db:migrate
+        fi
+      '';
+
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        Type = "simple";
+        User = cfg.user;
+        Group = cfg.group;
+        TimeoutSec = "300s";
+        Restart = "on-failure";
+        RestartSec = "10s";
+        WorkingDirectory = "${package}/share/frab";
+        ExecStart = "${frab-rake}/bin/frab-bundle exec rails server " +
+          "--binding=${cfg.listenAddress} --port=${toString cfg.listenPort}";
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-apps/mattermost.nix b/nixos/modules/services/web-apps/mattermost.nix
index bf3a8eed6004..8e6baf6a17e3 100644
--- a/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixos/modules/services/web-apps/mattermost.nix
@@ -202,6 +202,7 @@ in
           ExecStart = "${pkgs.mattermost}/bin/mattermost-platform";
           WorkingDirectory = "${cfg.statePath}";
           PrivateTmp = true;
+          JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service";
           Restart = "always";
           RestartSec = "10";
           LimitNOFILE = "49152";
diff --git a/nixos/modules/services/web-apps/pump.io-configure.js b/nixos/modules/services/web-apps/pump.io-configure.js
new file mode 100644
index 000000000000..1fbf346a34c4
--- /dev/null
+++ b/nixos/modules/services/web-apps/pump.io-configure.js
@@ -0,0 +1,23 @@
+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
index b7c64bc6940b..27ae68516367 100644
--- a/nixos/modules/services/web-apps/pump.io.nix
+++ b/nixos/modules/services/web-apps/pump.io.nix
@@ -5,71 +5,74 @@ 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 = {
-    driver = if cfg.driver == "disk" then null else cfg.driver;
-    params = ({ } //
-    (if cfg.driver == "disk" then {
-      dir = dataDir;
-     } else { }) //
-    (if cfg.driver == "mongodb" || cfg.driver == "redis" then {
-       host = cfg.dbHost;
-       port = cfg.dbPort;
-       dbname = cfg.dbName;
-       dbuser = cfg.dbUser;
-       dbpass = cfg.dbPassword;
-     } else { }) //
-    (if cfg.driver == "memcached" then {
-       host = cfg.dbHost;
-       port = cfg.dbPort;
-     } else { }) //
-     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;
-    uploaddir =  "${dataDir}/uploads";
-    debugClient = false;
-    firehose = cfg.firehose;
-    disableRegistration = cfg.disableRegistration;
-  } //
-  (if cfg.port < 1024 then {
-    serverUser = user;  # have pump.io listen then drop privileges
-   } else { }) //
-  cfg.extraConfig;
-
-in
-
-{
+    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 = {
@@ -77,7 +80,8 @@ in
       enable = mkEnableOption "Pump.io social streams server";
 
       secret = mkOption {
-        type = types.str;
+        type = types.nullOr types.str;
+        default = null;
         example = "my dog has fleas";
         description = ''
           A session-generating secret, server-wide password.  Warning:
@@ -85,6 +89,16 @@ in
         '';
       };
 
+      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";
@@ -125,7 +139,7 @@ in
 
       hostname = mkOption {
         type = types.nullOr types.str;
-        default = null;
+        default = "localhost";
         description = ''
           The hostname of the server, used for generating
           URLs. Defaults to "localhost" which doesn't do much for you.
@@ -152,6 +166,15 @@ in
         '';
       };
 
+      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";
@@ -253,6 +276,15 @@ in
         '';
       };
 
+      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;
@@ -291,6 +323,17 @@ in
         '';
       };
 
+      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;
@@ -332,24 +375,55 @@ in
           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 social network stream server";
+      { description = "Pump.io - stream server that does most of what people really want from a social network";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
-        serviceConfig.ExecStart = "${pkgs.pumpio}/bin/pump -c /etc/pump.io.json";
-        serviceConfig.User = if cfg.port < 1024 then "root" else user;
-        serviceConfig.Group = user;
-      };
 
-      environment.etc."pump.io.json" = {
-        mode = "0440";
-        gid = config.ids.gids.pumpio;
-        text = builtins.toJSON configOptions;
+        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;
diff --git a/nixos/modules/services/web-apps/quassel-webserver.nix b/nixos/modules/services/web-apps/quassel-webserver.nix
index d19e4bc58277..2ba5698d6cb1 100644
--- a/nixos/modules/services/web-apps/quassel-webserver.nix
+++ b/nixos/modules/services/web-apps/quassel-webserver.nix
@@ -12,11 +12,11 @@ let
         port: ${toString cfg.quasselCorePort},  // quasselcore port
         initialBacklogLimit: ${toString cfg.initialBacklogLimit},  // Amount of backlogs to fetch per buffer on connection
         backlogLimit: ${toString cfg.backlogLimit},  // Amount of backlogs to fetch per buffer after first retrieval
-        securecore: ${if cfg.secureCore then "true" else "false"},  // Connect to the core using SSL
+        securecore: ${boolToString cfg.secureCore},  // Connect to the core using SSL
         theme: '${cfg.theme}'  // Default UI theme
       },
       themes: ['default', 'darksolarized'],  //  Available themes
-      forcedefault: ${if cfg.forceHostAndPort then "true" else "false"},  // Will force default host and port to be used, and will hide the corresponding fields in the UI
+      forcedefault: ${boolToString cfg.forceHostAndPort},  // Will force default host and port to be used, and will hide the corresponding fields in the UI
       prefixpath: '${cfg.prefixPath}'  // Configure this if you use a reverse proxy
     };
   '';
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 5193814da725..76b0ee6da968 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -6,8 +6,6 @@ let
 
   configVersion = 26;
 
-  boolToString = b: if b then "true" else "false";
-
   cacheDir = "cache";
   lockDir = "lock";
   feedIconsDir = "feed-icons";
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index dc0ca501a484..ed77e0844769 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -63,6 +63,8 @@ let
       let
         svcFunction =
           if svc ? function then svc.function
+          # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix;
+          else if svc ? serviceExpression then import (toString svc.serviceExpression)
           else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
         config = (evalModules
           { modules = [ { options = res.options; config = svc.config or svc; } ];
diff --git a/nixos/modules/services/web-servers/apache-httpd/moodle.nix b/nixos/modules/services/web-servers/apache-httpd/moodle.nix
deleted file mode 100644
index d525348d5c7e..000000000000
--- a/nixos/modules/services/web-servers/apache-httpd/moodle.nix
+++ /dev/null
@@ -1,198 +0,0 @@
-{ config, lib, pkgs, serverInfo, php, ... }:
-
-with lib;
-
-let
-
-  httpd = serverInfo.serverConfig.package;
-
-  version24 = !versionOlder httpd.version "2.4";
-
-  allGranted = if version24 then ''
-    Require all granted
-  '' else ''
-    Order allow,deny
-    Allow from all
-  '';
-
-  moodleConfig = pkgs.writeText "config.php"
-    ''
-      <?php
-      unset($CFG);
-      global $CFG;
-      $CFG = new stdClass();
-      $CFG->dbtype    = '${config.dbType}';
-      $CFG->dblibrary = 'native';
-      $CFG->dbhost    = '${config.dbHost}';
-      $CFG->dbname    = '${config.dbName}';
-      $CFG->dbuser    = '${config.dbUser}';
-      $CFG->dbpass    = '${config.dbPassword}';
-      $CFG->prefix    = '${config.dbPrefix}';
-      $CFG->dboptions = array(
-          'dbpersist' => false,
-          'dbsocket'  => false,
-          'dbport'    => "${config.dbPort}",
-      );
-      $CFG->wwwroot   = '${config.wwwRoot}';
-      $CFG->dataroot  = '${config.dataRoot}';
-      $CFG->directorypermissions = 02777;
-      $CFG->admin = 'admin';
-      ${optionalString (config.debug.noEmailEver == true) ''
-        $CFG->noemailever = true;
-      ''}
-
-      ${config.extraConfig}
-      require_once(dirname(__FILE__) . '/lib/setup.php'); // Do not edit
-    '';
-  # Unpack Moodle and put the config file in its root directory.
-  moodleRoot = pkgs.stdenv.mkDerivation rec {
-    name= "moodle-2.8.10";
-
-    src = pkgs.fetchurl {
-      url = "https://download.moodle.org/stable28/${name}.tgz";
-      sha256 = "0c3r5081ipcwc9s6shakllnrkd589y2ln5z5m1q09l4h6a7cy4z2";
-    };
-
-    buildPhase =
-      ''
-      '';
-
-    installPhase =
-      ''
-        mkdir -p $out
-        cp -r * $out
-        cp ${moodleConfig} $out/config.php
-      '';
-    # Marked as broken due to needing an update for security issues.
-    # See: https://github.com/NixOS/nixpkgs/issues/18856
-    meta.broken = true;
-
-  };
-
-in
-
-{
-
-  extraConfig =
-  ''
-    # this should be config.urlPrefix instead of /
-    Alias / ${moodleRoot}/
-    <Directory ${moodleRoot}>
-      DirectoryIndex index.php
-    </Directory>
-  '';
-
-  documentRoot = moodleRoot; # TODO: fix this, should be config.urlPrefix
-
-  enablePHP = true;
-
-  options = {
-
-    id = mkOption {
-      default = "main";
-      description = ''
-        A unique identifier necessary to keep multiple Moodle server
-        instances on the same machine apart.
-      '';
-    };
-
-    dbType = mkOption {
-      default = "postgres";
-      example = "mysql";
-      description = "Database type.";
-    };
-
-    dbName = mkOption {
-      default = "moodle";
-      description = "Name of the database that holds the Moodle data.";
-    };
-
-    dbHost = mkOption {
-      default = "localhost";
-      example = "10.0.2.2";
-      description = ''
-        The location of the database server.
-      '';
-    };
-
-    dbPort = mkOption {
-      default = ""; # use the default port
-      example = "12345";
-      description = ''
-        The port that is used to connect to the database server.
-      '';
-    };
-
-    dbUser = mkOption {
-      default = "moodle";
-      description = "The user name for accessing the database.";
-    };
-
-    dbPassword = mkOption {
-      default = "";
-      example = "password";
-      description = ''
-        The password of the database user.  Warning: this is stored in
-        cleartext in the Nix store!
-      '';
-    };
-
-    dbPrefix = mkOption {
-      default = "mdl_";
-      example = "my_other_mdl_";
-      description = ''
-        A prefix for each table, if multiple moodles should run in a single database.
-      '';
-    };
-
-    wwwRoot = mkOption {
-      type = types.string;
-      example = "http://my.machine.com/my-moodle";
-      description = ''
-        The full web address where moodle has been installed.
-      '';
-    };
-
-    dataRoot = mkOption {
-      default = "/var/lib/moodledata";
-      example = "/var/lib/moodledata";
-      description = ''
-        The data directory for moodle. Needs to be writable!
-      '';
-      type = types.path;
-      };
-
-
-    extraConfig = mkOption {
-      type = types.lines;
-      default = "";
-      example =
-        ''
-        '';
-      description = ''
-        Any additional text to be appended to Moodle's
-        configuration file.  This is a PHP script.
-      '';
-    };
-
-    debug = {
-      noEmailEver = mkOption {
-        default = false;
-	example = "true";
-	description = ''
-	  Set this to true to prevent Moodle from ever sending any email.
-	'';
-	};
-    };
-  };
-
-  startupScript = pkgs.writeScript "moodle_startup.sh" ''
-  echo "Checking for existence of ${config.dataRoot}"
-  if [ ! -e "${config.dataRoot}" ]
-  then
-    mkdir -p "${config.dataRoot}"
-    chown ${serverInfo.serverConfig.user}.${serverInfo.serverConfig.group} "${config.dataRoot}"
-  fi
-  '';
-
-}
diff --git a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
index 32dd4439675a..c6f4bcd0f666 100644
--- a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
@@ -4,17 +4,12 @@
 with lib;
 
 let
-
-  # Upgrading? We have a test! nix-build ./nixos/tests/wordpress.nix
-  version = "4.6.1";
-  fullversion = "${version}";
-
   # Our bare-bones wp-config.php file using the above settings
   wordpressConfig = pkgs.writeText "wp-config.php" ''
     <?php
     define('DB_NAME',     '${config.dbName}');
     define('DB_USER',     '${config.dbUser}');
-    define('DB_PASSWORD', '${config.dbPassword}');
+    define('DB_PASSWORD', file_get_contents('${config.dbPasswordFile}'));
     define('DB_HOST',     '${config.dbHost}');
     define('DB_CHARSET',  'utf8');
     $table_prefix  = '${config.tablePrefix}';
@@ -71,12 +66,7 @@ let
   # The wordpress package itself
   wordpressRoot = pkgs.stdenv.mkDerivation rec {
     name = "wordpress";
-    src = pkgs.fetchFromGitHub {
-      owner = "WordPress";
-      repo = "WordPress";
-      rev = "${fullversion}";
-      sha256 = "0n82xgjg1ry2p73hhgpslnkdzrma5n6hxxq76s7qskkzj0qjfvpn";
-    };
+    src = config.package;
     installPhase = ''
       mkdir -p $out
       # copy all the wordpress files we downloaded
@@ -122,6 +112,14 @@ in
   enablePHP = true;
 
   options = {
+    package = mkOption {
+      type = types.path;
+      default = pkgs.wordpress;
+      description = ''
+        Path to the wordpress sources.
+        Upgrading? We have a test! nix-build ./nixos/tests/wordpress.nix
+      '';
+    };
     dbHost = mkOption {
       default = "localhost";
       description = "The location of the database server.";
@@ -139,9 +137,34 @@ in
     };
     dbPassword = mkOption {
       default = "wordpress";
-      description = "The mysql password to the respective dbUser.";
+      description = ''
+        The mysql password to the respective dbUser.
+
+        Warning: this password is stored in the world-readable Nix store. It's
+        recommended to use the $dbPasswordFile option since that gives you control over
+        the security of the password. $dbPasswordFile also takes precedence over $dbPassword.
+      '';
       example = "wordpress";
     };
+    dbPasswordFile = mkOption {
+      type = types.str;
+      default = toString (pkgs.writeTextFile {
+        name = "wordpress-dbpassword";
+        text = config.dbPassword;
+      });
+      example = "/run/keys/wordpress-dbpassword";
+      description = ''
+        Path to a file that contains the mysql password to the respective dbUser.
+        The file should be readable by the user: config.services.httpd.user.
+
+        $dbPasswordFile takes precedence over the $dbPassword option.
+
+        This defaults to a file in the world-readable Nix store that contains the value
+        of the $dbPassword option. It's recommended to override this with a path not in
+        the Nix store. Tip: use nixops key management:
+        <link xlink:href='https://nixos.org/nixops/manual/#idm140737318306400'/>
+      '';
+    };
     tablePrefix = mkOption {
       default = "wp_";
       description = ''
@@ -253,7 +276,7 @@ in
         sleep 1
       done
       ${pkgs.mysql}/bin/mysql -e 'CREATE DATABASE ${config.dbName};'
-      ${pkgs.mysql}/bin/mysql -e 'GRANT ALL ON ${config.dbName}.* TO ${config.dbUser}@localhost IDENTIFIED BY "${config.dbPassword}";'
+      ${pkgs.mysql}/bin/mysql -e "GRANT ALL ON ${config.dbName}.* TO ${config.dbUser}@localhost IDENTIFIED BY \"$(cat ${config.dbPasswordFile})\";"
     else
       echo "Good, no need to do anything database related."
     fi
diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix
index 619e0f90b124..eec285f6bc44 100644
--- a/nixos/modules/services/web-servers/caddy.nix
+++ b/nixos/modules/services/web-servers/caddy.nix
@@ -29,7 +29,6 @@ in
 
     agree = mkOption {
       default = false;
-      example = true;
       type = types.bool;
       description = "Agree to Let's Encrypt Subscriber Agreement";
     };
@@ -61,6 +60,7 @@ in
         User = "caddy";
         Group = "caddy";
         AmbientCapabilities = "cap_net_bind_service";
+        LimitNOFILE = 8192;
       };
     };
 
diff --git a/nixos/modules/services/web-servers/jboss/default.nix b/nixos/modules/services/web-servers/jboss/default.nix
index 583fe56eb5e2..d28724281a83 100644
--- a/nixos/modules/services/web-servers/jboss/default.nix
+++ b/nixos/modules/services/web-servers/jboss/default.nix
@@ -25,7 +25,7 @@ in
 
       enable = mkOption {
         default = false;
-        description = "Whether to enable jboss";
+        description = "Whether to enable JBoss. WARNING : this package is outdated and is known to have vulnerabilities.";
       };
 
       tempDir = mkOption {
diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
index f12cc9734465..c8d9836b0b68 100644
--- a/nixos/modules/services/web-servers/lighttpd/gitweb.nix
+++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -60,7 +60,8 @@ in
               "/gitweb/"        => "${pkgs.git}/share/gitweb/gitweb.cgi"
           )
           setenv.add-environment = (
-              "GITWEB_CONFIG" => "${gitwebConfigFile}"
+              "GITWEB_CONFIG" => "${gitwebConfigFile}",
+              "HOME" => "${cfg.projectroot}"
           )
       }
     '';
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 68a672c42c90..ae14aa28ae34 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -5,9 +5,16 @@ with lib;
 let
   cfg = config.services.nginx;
   virtualHosts = mapAttrs (vhostName: vhostConfig:
-    vhostConfig // (optionalAttrs vhostConfig.enableACME {
-      sslCertificate = "/var/lib/acme/${vhostName}/fullchain.pem";
-      sslCertificateKey = "/var/lib/acme/${vhostName}/key.pem";
+    let
+      serverName = if vhostConfig.serverName != null
+        then vhostConfig.serverName
+        else vhostName;
+    in
+    vhostConfig // {
+      inherit serverName;
+    } // (optionalAttrs vhostConfig.enableACME {
+      sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem";
+      sslCertificateKey = "/var/lib/acme/${serverName}/key.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -80,6 +87,8 @@ let
 
       server_tokens ${if cfg.serverTokens then "on" else "off"};
 
+      ${cfg.commonHttpConfig}
+
       ${vhosts}
 
       ${optionalString cfg.statusPage ''
@@ -112,8 +121,9 @@ let
     ${cfg.appendConfig}
   '';
 
-  vhosts = concatStringsSep "\n" (mapAttrsToList (serverName: vhost:
+  vhosts = concatStringsSep "\n" (mapAttrsToList (vhostName: vhost:
       let
+        serverName = vhost.serverName;
         ssl = vhost.enableSSL || vhost.forceSSL;
         port = if vhost.port != null then vhost.port else (if ssl then 443 else 80);
         listenString = toString port + optionalString ssl " ssl http2"
@@ -161,7 +171,7 @@ let
             ssl_certificate_key ${vhost.sslCertificateKey};
           ''}
 
-          ${optionalString (vhost.basicAuth != {}) (mkBasicAuth serverName vhost.basicAuth)}
+          ${optionalString (vhost.basicAuth != {}) (mkBasicAuth vhostName vhost.basicAuth)}
 
           ${mkLocations vhost.locations}
 
@@ -175,11 +185,12 @@ let
       ${optionalString (config.index != null) "index ${config.index};"}
       ${optionalString (config.tryFiles != null) "try_files ${config.tryFiles};"}
       ${optionalString (config.root != null) "root ${config.root};"}
+      ${optionalString (config.alias != null) "alias ${config.alias};"}
       ${config.extraConfig}
     }
   '') locations);
-  mkBasicAuth = serverName: authDef: let
-    htpasswdFile = pkgs.writeText "${serverName}.htpasswd" (
+  mkBasicAuth = vhostName: authDef: let
+    htpasswdFile = pkgs.writeText "${vhostName}.htpasswd" (
       concatStringsSep "\n" (mapAttrsToList (user: password: ''
         ${user}:{PLAIN}${password}
       '') authDef)
@@ -236,11 +247,13 @@ in
       };
 
       package = mkOption {
-        default = pkgs.nginx;
-        defaultText = "pkgs.nginx";
+        default = pkgs.nginxStable;
+        defaultText = "pkgs.nginxStable";
         type = types.package;
         description = "
-          Nginx package to use.
+          Nginx package to use. This defaults to the stable version. Note
+          that the nginx team recommends to use the mainline version which
+          available in nixpkgs as <literal>nginxMainline</literal>.
         ";
       };
 
@@ -267,6 +280,24 @@ in
         '';
       };
 
+      commonHttpConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          resolver 127.0.0.1 valid=5s;
+
+          log_format myformat '$remote_addr - $remote_user [$time_local] '
+                              '"$request" $status $body_bytes_sent '
+                              '"$http_referer" "$http_user_agent"';
+        '';
+        description = ''
+          With nginx you must provide common http context definitions before
+          they are used, e.g. log_format, resolver, etc. inside of server
+          or location contexts. Use this attribute to set these definitions
+          at the appropriate location.
+        '';
+      };
+
       httpConfig = mkOption {
         type = types.lines;
         default = "";
@@ -373,10 +404,18 @@ in
   config = mkIf cfg.enable {
     # TODO: test user supplied config file pases syntax test
 
+    assertions = let hostOrAliasIsNull = l: l.root == null || l.alias == null; in [
+      {
+        assertion = all (host: all hostOrAliasIsNull (attrValues host.locations)) (attrValues virtualHosts);
+        message = "Only one of nginx root or alias can be specified on a location.";
+      }
+    ];
+
     systemd.services.nginx = {
       description = "Nginx Web Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
+      stopIfChanged = false;
       preStart =
         ''
         mkdir -p ${cfg.stateDir}/logs
@@ -393,17 +432,20 @@ in
     };
 
     security.acme.certs = filterAttrs (n: v: v != {}) (
-      mapAttrs (vhostName: vhostConfig:
-        optionalAttrs vhostConfig.enableACME {
-          user = cfg.user;
-          group = cfg.group;
-          webroot = vhostConfig.acmeRoot;
-          extraDomains = genAttrs vhostConfig.serverAliases (alias: null);
-          postRun = ''
-            systemctl reload nginx
-          '';
-        }
-      ) virtualHosts
+      let
+        vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
+        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME) vhostsConfigs;
+        acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = {
+            user = cfg.user;
+            group = lib.mkDefault cfg.group;
+            webroot = vhostConfig.acmeRoot;
+            extraDomains = genAttrs vhostConfig.serverAliases (alias: null);
+            postRun = ''
+              systemctl reload nginx
+            '';
+          }; }) acmeEnabledVhosts;
+      in
+        listToAttrs acmePairs
     );
 
     users.extraUsers = optionalAttrs (cfg.user == "nginx") (singleton
diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix
index e1885b160664..83ce0f717341 100644
--- a/nixos/modules/services/web-servers/nginx/location-options.nix
+++ b/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -45,6 +45,15 @@ with lib;
       '';
     };
 
+    alias = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/your/alias/directory";
+      description = ''
+        Alias directory for requests.
+      '';
+    };
+
     extraConfig = mkOption {
       type = types.lines;
       default = "";
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index dcebbc9229fc..c0ea645b3dfe 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -8,6 +8,15 @@
 with lib;
 {
   options = {
+    serverName = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Name of this virtual host. Defaults to attribute name in virtualHosts.
+      '';
+      example = "example.org";
+    };
+
     serverAliases = mkOption {
       type = types.listOf types.str;
       default = [];
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index ed537e7122a2..26f546022035 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -4,30 +4,35 @@ with lib;
 
 let
   cfg = config.services.phpfpm;
+  enabled = cfg.poolConfigs != {} || cfg.pools != {};
 
   stateDir = "/run/phpfpm";
 
+  poolConfigs = cfg.poolConfigs // mapAttrs mkPool cfg.pools;
+
   mkPool = n: p: ''
-    [${n}]
     listen = ${p.listen}
     ${p.extraConfig}
   '';
 
-  cfgFile = pkgs.writeText "phpfpm.conf" ''
+  fpmCfgFile = pool: poolConfig: pkgs.writeText "phpfpm-${pool}.conf" ''
     [global]
     error_log = syslog
     daemonize = no
     ${cfg.extraConfig}
 
-    ${concatStringsSep "\n" (mapAttrsToList mkPool cfg.pools)}
-
-    ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
+    [${pool}]
+    ${poolConfig}
   '';
 
-  phpIni = pkgs.writeText "php.ini" ''
-    ${readFile "${cfg.phpPackage}/etc/php.ini"}
-
-    ${cfg.phpOptions}
+  phpIni = pkgs.runCommand "php.ini" {
+    inherit (cfg) phpPackage phpOptions;
+    nixDefaults = ''
+      sendmail_path = "/run/wrappers/bin/sendmail -t -i"
+    '';
+    passAsFile = [ "nixDefaults" "phpOptions" ];
+  } ''
+    cat $phpPackage/etc/php.ini $nixDefaultsPath $phpOptionsPath > $out
   '';
 
 in {
@@ -118,18 +123,40 @@ in {
     };
   };
 
-  config = mkIf (cfg.pools != {} || cfg.poolConfigs != {}) {
+  config = mkIf enabled {
+
+    systemd.slices.phpfpm = {
+      description = "PHP FastCGI Process manager pools slice";
+    };
 
-    systemd.services.phpfpm = {
+    systemd.targets.phpfpm = {
+      description = "PHP FastCGI Process manager pools target";
       wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -p "${stateDir}"
-      '';
-      serviceConfig = {
-        Type = "notify";
-        ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
-        ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
-      };
     };
+
+    systemd.services = flip mapAttrs' poolConfigs (pool: poolConfig:
+      nameValuePair "phpfpm-${pool}" {
+        description = "PHP FastCGI Process Manager service for pool ${pool}";
+        after = [ "network.target" ];
+        wantedBy = [ "phpfpm.target" ];
+        partOf = [ "phpfpm.target" ];
+        preStart = ''
+          mkdir -p ${stateDir}
+        '';
+        serviceConfig = let
+          cfgFile = fpmCfgFile pool poolConfig;
+        in {
+          Slice = "phpfpm.slice";
+          PrivateDevices = true;
+          ProtectSystem = "full";
+          ProtectHome = true;
+          NoNewPrivileges = true;
+          RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+          Type = "notify";
+          ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
+          ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
+        };
+      }
+   );
   };
 }
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index 8a453e015577..496e34db4a96 100644
--- a/nixos/modules/services/web-servers/zope2.nix
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -74,7 +74,7 @@ in
 
     services.zope2.instances = mkOption {
       default = {};
-      type = with types; loaOf (submodule zope2Opts);
+      type = with types; attrsOf (submodule zope2Opts);
       example = literalExample ''
         {
           plone01 = {
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index 7cbca1dcddfd..d75d24830f8d 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -43,7 +43,6 @@ in {
     enable = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Whether of not to enable Compton as the X.org composite manager.
       '';
@@ -52,7 +51,6 @@ in {
     fade = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Fade windows in and out.
       '';
@@ -85,7 +83,7 @@ in {
         "focused = 1" 
       ];
       description = ''
-        List of condition of windows that should have no shadow.
+        List of conditions of windows that should not be faded.
         See <literal>compton(1)</literal> man page for more examples.
       '';
     };
@@ -93,7 +91,6 @@ in {
     shadow = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Draw window shadows.
       '';
@@ -126,7 +123,7 @@ in {
         "focused = 1" 
       ];
       description = ''
-        List of condition of windows that should have no shadow.
+        List of conditions of windows that should have no shadow.
         See <literal>compton(1)</literal> man page for more examples.
       '';
     };
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 144e4aada277..d56050c36269 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -8,7 +8,7 @@ let
   cfg = xcfg.desktopManager;
 
   # If desktop manager `d' isn't capable of setting a background and
-  # the xserver is enabled, the `feh' program is used as a fallback.
+  # the xserver is enabled, `feh' or `xsetroot' are used as a fallback.
   needBGCond = d: ! (d ? bgSupport && d.bgSupport) && xcfg.enable;
 
 in
@@ -16,11 +16,10 @@ in
 {
   # Note: the order in which desktop manager modules are imported here
   # determines the default: later modules (if enabled) are preferred.
-  # E.g., if KDE is enabled, it supersedes xterm.
+  # E.g., if Plasma 5 is enabled, it supersedes xterm.
   imports = [
-    ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./kde5.nix
-    ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome3.nix
-    ./kodi.nix
+    ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix
+    ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix
   ];
 
   options = {
@@ -45,8 +44,11 @@ in
             manage = "desktop";
             start = d.start
             + optionalString (needBGCond d) ''
-              if test -e $HOME/.background-image; then
+              if [ -e $HOME/.background-image ]; then
                 ${pkgs.feh}/bin/feh --bg-scale $HOME/.background-image
+              else
+                # Use a solid black background as fallback
+                ${pkgs.xorg.xsetroot}/bin/xsetroot -solid black
               fi
             '';
           }) list;
@@ -81,6 +83,6 @@ in
   config = {
     services.xserver.displayManager.session = cfg.session.list;
     environment.systemPackages =
-      mkIf cfg.session.needBGPackages [ pkgs.feh ];
+      mkIf cfg.session.needBGPackages [ pkgs.feh ]; # xsetroot via xserver.enable
   };
 }
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 7ea8b30d23d1..b02eaf861a0d 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -20,7 +20,6 @@ in
 
     services.xserver.desktopManager.enlightenment.enable = mkOption {
       default = false;
-      example = true;
       description = "Enable the Enlightenment desktop environment.";
     };
 
@@ -62,10 +61,10 @@ in
       '';
     }];
 
-    security.setuidPrograms = [ "e_freqset" ];
+    security.wrappers.e_freqset.source = "${e.enlightenment.out}/bin/e_freqset";
 
     environment.etc = singleton
-      { source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
+      { source = xcfg.xkbDir;
         target = "X11/xkb";
       };
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 17e84b1d9a16..a1790ccd675c 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -53,7 +53,6 @@ in {
     services.xserver.desktopManager.gnome3 = {
       enable = mkOption {
         default = false;
-        example = true;
         description = "Enable Gnome 3 desktop manager.";
       };
 
@@ -78,7 +77,7 @@ in {
       };
 
       debug = mkEnableOption "gnome-session debug messages";
-    };  
+    };
 
     environment.gnome3.packageSet = mkOption {
       default = null;
@@ -86,7 +85,7 @@ in {
       description = "Which GNOME 3 package set to use.";
       apply = p: if p == null then pkgs.gnome3 else p;
     };
-    
+
     environment.gnome3.excludePackages = mkOption {
       default = [];
       example = literalExample "[ pkgs.gnome3.totem ]";
@@ -125,6 +124,9 @@ in {
     services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
     services.udev.packages = [ pkgs.gnome3.gnome_settings_daemon ];
 
+    # If gnome3 is installed, build vim for gtk3 too.
+    nixpkgs.config.vim.gui = "gtk3";
+
     fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ];
 
     services.xserver.desktopManager.session = singleton
diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix
deleted file mode 100644
index 3aa4821a0521..000000000000
--- a/nixos/modules/services/x11/desktop-managers/kde4.nix
+++ /dev/null
@@ -1,199 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  xcfg = config.services.xserver;
-  cfg = xcfg.desktopManager.kde4;
-  xorg = pkgs.xorg;
-  kde_workspace = config.services.xserver.desktopManager.kde4.kdeWorkspacePackage;
-
-  # Disable Nepomuk and Strigi by default.  As of KDE 4.7, they don't
-  # really work very well (e.g. searching files often fails to find
-  # files), segfault sometimes and consume significant resources.
-  # They can be re-enabled in the KDE System Settings under "Desktop
-  # Search".
-  disableNepomuk = pkgs.writeTextFile
-    { name = "nepomuk-config";
-      destination = "/share/config/nepomukserverrc";
-      text =
-        ''
-          [Basic Settings]
-          Start Nepomuk=false
-
-          [Service-nepomukstrigiservice]
-          autostart=false
-        '';
-    };
-
-  phononBackends = {
-    gstreamer = [
-      pkgs.phonon-backend-gstreamer
-      pkgs.gst_all.gstPluginsBase
-      pkgs.gst_all.gstPluginsGood
-      pkgs.gst_all.gstPluginsUgly
-      pkgs.gst_all.gstPluginsBad
-      pkgs.gst_all.gstFfmpeg # for mp3 playback
-      pkgs.gst_all.gstreamer # needed?
-    ];
-
-    vlc = [pkgs.phonon-backend-vlc];
-  };
-
-  phononBackendPackages = flip concatMap cfg.phononBackends
-    (name: attrByPath [name] (throw "unknown phonon backend `${name}'") phononBackends);
-
-in
-
-{
-  options = {
-
-    services.xserver.desktopManager.kde4 = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Enable the KDE 4 desktop environment.";
-      };
-
-      phononBackends = mkOption {
-        type = types.listOf types.str;
-        default = ["gstreamer"];
-        example = ["gstreamer" "vlc"];
-        description = "Which phonon multimedia backend kde should use";
-      };
-
-      kdeWorkspacePackage = mkOption {
-        internal = true;
-        default = pkgs.kde4.kde_workspace;
-        defaultText = "pkgs.kde4.kde_workspace";
-        type = types.package;
-        description = "Custom kde-workspace, used for NixOS rebranding.";
-      };
-
-      enablePIM = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Whether to enable PIM support. Note that enabling this pulls in Akonadi and MariaDB as dependencies.";
-      };
-
-      enableNepomuk = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable Nepomuk (deprecated).";
-      };
-    };
-  };
-
-
-  config = mkIf (xcfg.enable && cfg.enable) {
-
-    # If KDE 4 is enabled, make it the default desktop manager (unless
-    # overridden by the user's configuration).
-    # !!! doesn't work yet ("Multiple definitions. Only one is allowed
-    # for this option.")
-    # services.xserver.desktopManager.default = mkOverride 900 "kde4";
-
-    services.xserver.desktopManager.session = singleton
-      { name = "kde4";
-        bgSupport = true;
-        start =
-          ''
-            # The KDE icon cache is supposed to update itself
-            # automatically, but it uses the timestamp on the icon
-            # theme directory as a trigger.  Since in Nix the
-            # timestamp is always the same, this doesn't work.  So as
-            # a workaround, nuke the icon cache on login.  This isn't
-            # perfect, since it may require logging out after
-            # installing new applications to update the cache.
-            # See http://lists-archives.org/kde-devel/26175-what-when-will-icon-cache-refresh.html
-            rm -fv $HOME/.kde/cache-*/icon-cache.kcache
-
-            # Qt writes a weird ‘libraryPath’ line to
-            # ~/.config/Trolltech.conf that causes the KDE plugin
-            # paths of previous KDE invocations to be searched.
-            # Obviously using mismatching KDE libraries is potentially
-            # disastrous, so here we nuke references to the Nix store
-            # in Trolltech.conf.  A better solution would be to stop
-            # Qt from doing this wackiness in the first place.
-            if [ -e $HOME/.config/Trolltech.conf ]; then
-                sed -e '/nix\\store\|nix\/store/ d' -i $HOME/.config/Trolltech.conf
-            fi
-
-            # Load PulseAudio module for routing support.
-            # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/
-            ${optionalString config.hardware.pulseaudio.enable ''
-              ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
-            ''}
-
-            # Start KDE.
-            exec ${kde_workspace}/bin/startkde
-          '';
-      };
-
-    security.setuidOwners = singleton
-      { program = "kcheckpass";
-        source = "${kde_workspace}/lib/kde4/libexec/kcheckpass";
-        owner = "root";
-        group = "root";
-        setuid = true;
-      };
-
-    environment.systemPackages =
-        [ pkgs.kde4.kdelibs
-
-          pkgs.kde4.kde_baseapps # Splitted kdebase
-          kde_workspace
-          pkgs.kde4.kde_runtime
-          pkgs.kde4.konsole
-          pkgs.kde4.kate
-
-          pkgs.kde4.kde_wallpapers # contains kdm's default background
-          pkgs.kde4.oxygen_icons
-
-          # Starts KDE's Polkit authentication agent.
-          pkgs.kde4.polkit_kde_agent
-
-          # Miscellaneous runtime dependencies.
-          pkgs.kde4.qt4 # needed for qdbus
-          pkgs.shared_mime_info
-          xorg.xmessage # so that startkde can show error messages
-          xorg.xset # used by startkde, non-essential
-          xorg.xauth # used by kdesu
-        ]
-      ++ optionals cfg.enablePIM
-        [ pkgs.kde4.kdepim_runtime
-          pkgs.kde4.akonadi
-          pkgs.mysql # used by akonadi
-        ]
-      ++ (if cfg.enableNepomuk then
-        [ pkgs.shared_desktop_ontologies # used by nepomuk
-          pkgs.strigi # used by nepomuk
-          pkgs.virtuoso # to enable Nepomuk to find Virtuoso
-        ] else
-        [ disableNepomuk ])
-      ++ optional config.hardware.pulseaudio.enable pkgs.kde4.kmix  # Perhaps this should always be enabled
-      ++ optional config.hardware.bluetooth.enable pkgs.kde4.bluedevil
-      ++ optional config.networking.networkmanager.enable pkgs.kde4.plasma-nm
-      ++ phononBackendPackages;
-
-    environment.pathsToLink = [ "/share" ];
-
-    environment.profileRelativeEnvVars = mkIf (elem "gstreamer" cfg.phononBackends) {
-      GST_PLUGIN_SYSTEM_PATH = [ "/lib/gstreamer-0.10" ];
-    };
-
-    environment.etc = singleton
-      { source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
-        target = "X11/xkb";
-      };
-
-    # Enable helpful DBus services.
-    services.udisks2.enable = true;
-    services.upower.enable = config.powerManagement.enable;
-
-    security.pam.services.kde = { allowNullPassword = true; };
-
-  };
-
-}
diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix
deleted file mode 100644
index de5bfe263d49..000000000000
--- a/nixos/modules/services/x11/desktop-managers/kde5.nix
+++ /dev/null
@@ -1,253 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  xcfg = config.services.xserver;
-  cfg = xcfg.desktopManager.kde5;
-  xorg = pkgs.xorg;
-
-  kde5 = pkgs.kde5;
-
-in
-
-{
-  options = {
-
-    services.xserver.desktopManager.kde5 = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Enable the Plasma 5 (KDE 5) desktop environment.";
-      };
-
-      enableQt4Support = mkOption {
-        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.
-        '';
-      };
-
-      extraPackages = mkOption {
-        type = types.listOf types.package;
-        default = [];
-        description = ''
-          KDE packages that need to be installed system-wide.
-        '';
-      };
-
-    };
-
-  };
-
-
-  config = mkMerge [
-    (mkIf (cfg.extraPackages != []) {
-      environment.systemPackages = [ (kde5.kdeWrapper cfg.extraPackages) ];
-    })
-
-    (mkIf (xcfg.enable && cfg.enable) {
-
-      warnings = optional config.services.xserver.desktopManager.kde4.enable
-        "KDE 4 should not be enabled at the same time as KDE 5";
-
-      services.xserver.desktopManager.session = singleton {
-        name = "kde5";
-        bgSupport = true;
-        start = ''
-          # Load PulseAudio module for routing support.
-          # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/
-          ${optionalString config.hardware.pulseaudio.enable ''
-            ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
-          ''}
-
-          exec "${kde5.startkde}"
-
-        '';
-      };
-
-      security.setuidOwners = [
-        {
-          program = "kcheckpass";
-          source = "${kde5.plasma-workspace.out}/lib/libexec/kcheckpass";
-          owner = "root";
-          setuid = true;
-        }
-        {
-          program = "start_kdeinit";
-          source = "${kde5.kinit.out}/lib/libexec/kf5/start_kdeinit";
-          owner = "root";
-          setuid = true;
-        }
-      ];
-
-      environment.systemPackages =
-        [
-          kde5.frameworkintegration
-          kde5.kactivities
-          kde5.kauth
-          kde5.kcmutils
-          kde5.kconfig
-          kde5.kconfigwidgets
-          kde5.kcoreaddons
-          kde5.kdbusaddons
-          kde5.kdeclarative
-          kde5.kded
-          kde5.kdesu
-          kde5.kdnssd
-          kde5.kemoticons
-          kde5.kfilemetadata
-          kde5.kglobalaccel
-          kde5.kguiaddons
-          kde5.kiconthemes
-          kde5.kidletime
-          kde5.kimageformats
-          kde5.kinit
-          kde5.kio
-          kde5.kjobwidgets
-          kde5.knewstuff
-          kde5.knotifications
-          kde5.knotifyconfig
-          kde5.kpackage
-          kde5.kparts
-          kde5.kpeople
-          kde5.krunner
-          kde5.kservice
-          kde5.ktextwidgets
-          kde5.kwallet
-          kde5.kwayland
-          kde5.kwidgetsaddons
-          kde5.kxmlgui
-          kde5.kxmlrpcclient
-          kde5.plasma-framework
-          kde5.solid
-          kde5.sonnet
-          kde5.threadweaver
-
-          kde5.breeze-qt5
-          kde5.kactivitymanagerd
-          kde5.kde-cli-tools
-          kde5.kdecoration
-          kde5.kdeplasma-addons
-          kde5.kgamma5
-          kde5.khotkeys
-          kde5.kinfocenter
-          kde5.kmenuedit
-          kde5.kscreen
-          kde5.kscreenlocker
-          kde5.ksysguard
-          kde5.kwayland
-          kde5.kwin
-          kde5.kwrited
-          kde5.libkscreen
-          kde5.libksysguard
-          kde5.milou
-          kde5.plasma-integration
-          kde5.polkit-kde-agent
-          kde5.systemsettings
-
-          kde5.plasma-desktop
-          kde5.plasma-workspace
-          kde5.plasma-workspace-wallpapers
-
-          kde5.dolphin-plugins
-          kde5.ffmpegthumbs
-          kde5.kdegraphics-thumbnailers
-          kde5.kio-extras
-          kde5.print-manager
-
-          # Install Breeze icons if available
-          (kde5.breeze-icons or kde5.oxygen-icons5 or kde5.oxygen-icons)
-          pkgs.hicolor_icon_theme
-
-          kde5.kde-gtk-config kde5.breeze-gtk
-
-          pkgs.qt5.phonon-backend-gstreamer
-        ]
-
-        # Plasma 5.5 and later has a Breeze GTK theme.
-        # If it is not available, Orion is very similar to Breeze.
-        ++ lib.optional (!(lib.hasAttr "breeze-gtk" kde5)) pkgs.orion
-
-        # Install activity manager if available
-        ++ lib.optional (lib.hasAttr "kactivitymanagerd" kde5) kde5.kactivitymanagerd
-
-        # frameworkintegration was split with plasma-integration in Plasma 5.6
-        ++ lib.optional (lib.hasAttr "plasma-integration" kde5) kde5.plasma-integration
-
-        ++ lib.optionals cfg.enableQt4Support [ kde5.breeze-qt4 pkgs.phonon-backend-gstreamer ]
-
-        # Optional hardware support features
-        ++ lib.optional config.hardware.bluetooth.enable kde5.bluedevil
-        ++ lib.optional config.networking.networkmanager.enable kde5.plasma-nm
-        ++ lib.optional config.hardware.pulseaudio.enable kde5.plasma-pa
-        ++ lib.optional config.powerManagement.enable kde5.powerdevil
-        ++ lib.optional config.services.colord.enable pkgs.colord-kde
-        ++ lib.optionals config.services.samba.enable [ kde5.kdenetwork-filesharing pkgs.samba ];
-
-      services.xserver.desktopManager.kde5.extraPackages =
-        [
-          kde5.khelpcenter
-          kde5.oxygen
-
-          kde5.dolphin
-          kde5.konsole
-        ];
-
-      environment.pathsToLink = [ "/share" ];
-
-      environment.etc = singleton {
-        source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
-        target = "X11/xkb";
-      };
-
-      environment.variables =
-        {
-          # Enable GTK applications to load SVG icons
-          GST_PLUGIN_SYSTEM_PATH_1_0 =
-            lib.makeSearchPath "/lib/gstreamer-1.0"
-            (builtins.map (pkg: pkg.out) (with pkgs.gst_all_1; [
-              gstreamer
-              gst-plugins-base
-              gst-plugins-good
-              gst-plugins-ugly
-              gst-plugins-bad
-              gst-libav # for mp3 playback
-            ]));
-        }
-        // (if (lib.hasAttr "breeze-icons" kde5)
-            then { GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; }
-            else { });
-
-      fonts.fonts = [ (kde5.oxygen-fonts or pkgs.noto-fonts) ];
-
-      programs.ssh.askPassword = "${kde5.ksshaskpass.out}/bin/ksshaskpass";
-
-      # Enable helpful DBus services.
-      services.udisks2.enable = true;
-      services.upower.enable = config.powerManagement.enable;
-
-      # Extra UDEV rules used by Solid
-      services.udev.packages = [
-        pkgs.libmtp
-        pkgs.media-player-info
-      ];
-
-      services.xserver.displayManager.sddm = {
-        theme = "breeze";
-        themes = [
-          kde5.ecm # for the setup-hook
-          kde5.plasma-workspace
-          kde5.breeze-icons
-        ];
-      };
-
-      security.pam.services.kde = { allowNullPassword = true; };
-
-    })
-  ];
-
-}
diff --git a/nixos/modules/services/x11/desktop-managers/kodi.nix b/nixos/modules/services/x11/desktop-managers/kodi.nix
index de00ff93b17c..3ce49b9d2bf8 100644
--- a/nixos/modules/services/x11/desktop-managers/kodi.nix
+++ b/nixos/modules/services/x11/desktop-managers/kodi.nix
@@ -11,7 +11,6 @@ in
     services.xserver.desktopManager.kodi = {
       enable = mkOption {
         default = false;
-        example = true;
         description = "Enable the kodi multimedia center.";
       };
     };
diff --git a/nixos/modules/services/x11/desktop-managers/lumina.nix b/nixos/modules/services/x11/desktop-managers/lumina.nix
index f0b31a2acb01..ec5fbb13b323 100644
--- a/nixos/modules/services/x11/desktop-managers/lumina.nix
+++ b/nixos/modules/services/x11/desktop-managers/lumina.nix
@@ -32,8 +32,8 @@ in
 
     environment.systemPackages = [
       pkgs.fluxbox
-      pkgs.kde5.kwindowsystem
-      pkgs.kde5.oxygen-icons5
+      pkgs.libsForQt5.kwindowsystem
+      pkgs.kdeFrameworks.oxygen-icons5
       pkgs.lumina
       pkgs.numlockx
       pkgs.qt5.qtsvg
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
new file mode 100644
index 000000000000..2216104be31a
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -0,0 +1,228 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.plasma5;
+
+  inherit (pkgs) kdeWrapper kdeApplications plasma5 libsForQt5 qt5 xorg;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.plasma5 = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the Plasma 5 (KDE 5) desktop environment.";
+      };
+
+      enableQt4Support = mkOption {
+        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.
+        '';
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        description = ''
+          KDE packages that need to be installed system-wide.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkMerge [
+    (mkIf (cfg.extraPackages != []) {
+      environment.systemPackages = [ (kdeWrapper cfg.extraPackages) ];
+    })
+
+    (mkIf (xcfg.enable && cfg.enable) {
+      services.xserver.desktopManager.session = singleton {
+        name = "plasma5";
+        bgSupport = true;
+        start = ''
+          # Load PulseAudio module for routing support.
+          # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/
+          ${optionalString config.hardware.pulseaudio.enable ''
+            ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
+          ''}
+
+          exec "${plasma5.startkde}"
+        '';
+      };
+
+      security.wrappers = {
+        kcheckpass.source = "${plasma5.plasma-workspace.out}/lib/libexec/kcheckpass";
+        "start_kdeinit".source = "${pkgs.kinit.out}/lib/libexec/kf5/start_kdeinit";
+      };
+
+      environment.systemPackages = with pkgs; with qt5; with libsForQt5; with plasma5; with kdeApplications;
+        [
+          frameworkintegration
+          kactivities
+          kauth
+          kcmutils
+          kconfig
+          kconfigwidgets
+          kcoreaddons
+          kdbusaddons
+          kdeclarative
+          kded
+          kdesu
+          kdnssd
+          kemoticons
+          kfilemetadata
+          kglobalaccel
+          kguiaddons
+          kiconthemes
+          kidletime
+          kimageformats
+          kinit
+          kio
+          kjobwidgets
+          knewstuff
+          knotifications
+          knotifyconfig
+          kpackage
+          kparts
+          kpeople
+          krunner
+          kservice
+          ktextwidgets
+          kwallet
+          kwallet-pam
+          kwalletmanager
+          kwayland
+          kwidgetsaddons
+          kxmlgui
+          kxmlrpcclient
+          plasma-framework
+          solid
+          sonnet
+          threadweaver
+
+          breeze-qt5
+          kactivitymanagerd
+          kde-cli-tools
+          kdecoration
+          kdeplasma-addons
+          kgamma5
+          khotkeys
+          kinfocenter
+          kmenuedit
+          kscreen
+          kscreenlocker
+          ksysguard
+          kwayland
+          kwin
+          kwrited
+          libkscreen
+          libksysguard
+          milou
+          plasma-integration
+          polkit-kde-agent
+          systemsettings
+
+          plasma-desktop
+          plasma-workspace
+          plasma-workspace-wallpapers
+
+          dolphin-plugins
+          ffmpegthumbs
+          kdegraphics-thumbnailers
+          kio-extras
+          print-manager
+
+          breeze-icons
+          pkgs.hicolor_icon_theme
+
+          kde-gtk-config breeze-gtk
+
+          phonon-backend-gstreamer
+        ]
+
+        ++ lib.optionals cfg.enableQt4Support [ breeze-qt4 pkgs.phonon-backend-gstreamer ]
+
+        # Optional hardware support features
+        ++ lib.optional config.hardware.bluetooth.enable bluedevil
+        ++ lib.optional config.networking.networkmanager.enable plasma-nm
+        ++ lib.optional config.hardware.pulseaudio.enable plasma-pa
+        ++ lib.optional config.powerManagement.enable powerdevil
+        ++ lib.optional config.services.colord.enable colord-kde
+        ++ lib.optionals config.services.samba.enable [ kdenetwork-filesharing pkgs.samba ];
+
+      services.xserver.desktopManager.plasma5.extraPackages =
+        with kdeApplications; with plasma5;
+        [
+          khelpcenter
+          oxygen
+
+          dolphin
+          konsole
+        ];
+
+      environment.pathsToLink = [ "/share" ];
+
+      environment.etc = singleton {
+        source = xcfg.xkbDir;
+        target = "X11/xkb";
+      };
+
+      environment.variables = {
+        # Enable GTK applications to load SVG icons
+        GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
+        QT_PLUGIN_PATH = "/run/current-system/sw/lib/qt5/plugins";
+      };
+
+      fonts.fonts = with pkgs; [ noto-fonts hack-font ];
+      fonts.fontconfig.defaultFonts = {
+        monospace = [ "Hack" "Noto Mono" ];
+        sansSerif = [ "Noto Sans" ];
+        serif = [ "Noto Serif" ];
+      };
+
+      programs.ssh.askPassword = "${plasma5.ksshaskpass.out}/bin/ksshaskpass";
+
+      # Enable helpful DBus services.
+      services.udisks2.enable = true;
+      services.upower.enable = config.powerManagement.enable;
+      services.dbus.packages =
+        mkIf config.services.printing.enable [ pkgs.system-config-printer ];
+
+      # Extra UDEV rules used by Solid
+      services.udev.packages = [
+        pkgs.libmtp
+        pkgs.media-player-info
+      ];
+
+      services.xserver.displayManager.sddm = {
+        theme = "breeze";
+        package = pkgs.sddmPlasma5;
+      };
+
+      security.pam.services.kde = { allowNullPassword = true; };
+
+      # Doing these one by one seems silly, but we currently lack a better
+      # construct for handling common pam configs.
+      security.pam.services.gdm.enableKwallet = true;
+      security.pam.services.kdm.enableKwallet = true;
+      security.pam.services.lightdm.enableKwallet = true;
+      security.pam.services.sddm.enableKwallet = true;
+      security.pam.services.slim.enableKwallet = true;
+
+    })
+  ];
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 530468be5f96..9c42dc8781b9 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -41,6 +41,18 @@ in
           Shell commands executed just before XFCE is started.
         '';
       };
+
+      enableXfwm = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Enable the XFWM (default) window manager.";
+      };
+
+      screenLock = mkOption {
+        type = types.enum [ "xscreensaver" "xlockmore" "slock" ];
+        default = "xlockmore";
+        description = "Application used by XFCE to lock the screen.";
+      };
     };
 
   };
@@ -74,6 +86,7 @@ in
         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
@@ -87,7 +100,6 @@ in
         pkgs.xfce.xfce4volumed
         pkgs.xfce.xfce4-screenshooter
         pkgs.xfce.xfconf
-        pkgs.xfce.xfwm4
         # This supplies some "abstract" icons such as
         # "utilities-terminal" and "accessories-text-editor".
         pkgs.gnome3.defaultIconTheme
@@ -99,6 +111,7 @@ in
         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)
diff --git a/nixos/modules/services/x11/desktop-managers/xterm.nix b/nixos/modules/services/x11/desktop-managers/xterm.nix
index eab914071410..29752ff405e2 100644
--- a/nixos/modules/services/x11/desktop-managers/xterm.nix
+++ b/nixos/modules/services/x11/desktop-managers/xterm.nix
@@ -13,7 +13,6 @@ in
 
     services.xserver.desktopManager.xterm.enable = mkOption {
       default = true;
-      example = false;
       description = "Enable a xterm terminal as a desktop manager.";
     };
 
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index c0daf30d04eb..cf6efb7dae79 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -1,5 +1,5 @@
 # This module declares the options to define a *display manager*, the
-# program responsible for handling X logins (such as xdm, kdm, gdb, or
+# program responsible for handling X logins (such as xdm, gdb, or
 # SLiM).  The display manager allows the user to select a *session
 # type*.  When the user logs in, the display manager starts the
 # *session script* ("xsession" below) to launch the selected session
@@ -24,7 +24,7 @@ let
     Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter}
     Xft.hinting: ${if fontconfig.hinting.enable then "1" else "0"}
     Xft.autohint: ${if fontconfig.hinting.autohint then "1" else "0"}
-    Xft.hintstyle: hint${fontconfig.hinting.style}
+    Xft.hintstyle: hintslight
   '';
 
   # file provided by services.xserver.displayManager.session.script
@@ -32,6 +32,33 @@ let
     ''
       #! ${pkgs.bash}/bin/bash
 
+      # Expected parameters:
+      #   $1 = <desktop-manager>+<window-manager>
+
+      # Actual parameters (FIXME):
+      # SDDM is calling this script like the following:
+      #   $1 = /nix/store/xxx-xsession (= $0)
+      #   $2 = <desktop-manager>+<window-manager>
+      # SLiM is using the following parameter:
+      #   $1 = /nix/store/xxx-xsession <desktop-manager>+<window-manager>
+      # LightDM keeps the double quotes:
+      #   $1 = /nix/store/xxx-xsession "<desktop-manager>+<window-manager>"
+      # The fake/auto display manager doesn't use any parameters and GDM is
+      # broken.
+      # If you want to "debug" this script don't print the parameters to stdout
+      # or stderr because this script will be executed multiple times and the
+      # output won't be visible in the log when the script is executed for the
+      # first time (e.g. append them to a file instead)!
+
+      # All of the above cases are handled by the following hack (FIXME).
+      # Since this line is *very important* for *all display managers* it is
+      # very important to test changes to the following line with all display
+      # managers:
+      if [ "''${1:0:1}" = "/" ]; then eval exec "$1" "$2" ; fi
+
+      # 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" "$@"
@@ -55,9 +82,6 @@ let
         fi
       ''}
 
-      # Handle being called by kdm.
-      if test "''${1:0:1}" = /; then eval exec "$1"; fi
-
       # Start PulseAudio if enabled.
       ${optionalString (config.hardware.pulseaudio.enable) ''
         ${optionalString (!config.hardware.pulseaudio.systemWide)
@@ -107,15 +131,16 @@ let
           fi
       fi
 
-      # The session type is "<desktop-manager> + <window-manager>", so
-      # extract those.
-      windowManager="''${sessionType##* + }"
+      # The session type is "<desktop-manager>+<window-manager>", so
+      # extract those (see:
+      # http://wiki.bash-hackers.org/syntax/pe#substring_removal).
+      windowManager="''${sessionType##*+}"
       : ''${windowManager:=${cfg.windowManager.default}}
-      desktopManager="''${sessionType% + *}"
+      desktopManager="''${sessionType%%+*}"
       : ''${desktopManager:=${cfg.desktopManager.default}}
 
       # Start the window manager.
-      case $windowManager in
+      case "$windowManager" in
         ${concatMapStrings (s: ''
           (${s.name})
             ${s.start}
@@ -125,7 +150,7 @@ let
       esac
 
       # Start the desktop manager.
-      case $desktopManager in
+      case "$desktopManager" in
         ${concatMapStrings (s: ''
           (${s.name})
             ${s.start}
@@ -142,6 +167,9 @@ let
       exit 0
     '';
 
+  # Desktop Entry Specification:
+  # - https://standards.freedesktop.org/desktop-entry-spec/latest/
+  # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
   mkDesktops = names: pkgs.runCommand "desktops"
     { # trivial derivation
       preferLocalBuild = true;
@@ -155,7 +183,7 @@ let
         Version=1.0
         Type=XSession
         TryExec=${cfg.displayManager.session.script}
-        Exec=${cfg.displayManager.session.script} '${n}'
+        Exec=${cfg.displayManager.session.script} "${n}"
         X-GDM-BypassXsession=true
         Name=${n}
         Comment=
@@ -238,7 +266,7 @@ in
           wm = filter (s: s.manage == "window") list;
           dm = filter (s: s.manage == "desktop") list;
           names = flip concatMap dm
-            (d: map (w: d.name + optionalString (w.name != "none") (" + " + w.name))
+            (d: map (w: d.name + optionalString (w.name != "none") ("+" + w.name))
               (filter (w: d.name != "none" || w.name != "none") wm));
           desktops = mkDesktops names;
           script = xsession wm dm;
diff --git a/nixos/modules/services/x11/display-managers/kdm.nix b/nixos/modules/services/x11/display-managers/kdm.nix
deleted file mode 100644
index 04701a1640cc..000000000000
--- a/nixos/modules/services/x11/display-managers/kdm.nix
+++ /dev/null
@@ -1,158 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  dmcfg = config.services.xserver.displayManager;
-  cfg = dmcfg.kdm;
-
-  inherit (pkgs.kde4) kdebase_workspace;
-
-  defaultConfig =
-    ''
-      [Shutdown]
-      HaltCmd=${config.systemd.package}/sbin/shutdown -h now
-      RebootCmd=${config.systemd.package}/sbin/shutdown -r now
-      ${optionalString (config.system.boot.loader.id == "grub") ''
-        BootManager=${if config.boot.loader.grub.version == 2 then "Grub2" else "Grub"}
-      ''}
-
-      [X-*-Core]
-      Xrdb=${pkgs.xorg.xrdb}/bin/xrdb
-      SessionsDirs=${dmcfg.session.desktops}
-      Session=${dmcfg.session.script}
-      FailsafeClient=${pkgs.xterm}/bin/xterm
-
-      [X-:*-Core]
-      ServerCmd=${dmcfg.xserverBin} ${toString dmcfg.xserverArgs}
-      # KDM calls `rm' somewhere to clean up some temporary directory.
-      SystemPath=${pkgs.coreutils}/bin
-      # The default timeout (15) is too short in a heavily loaded boot process.
-      ServerTimeout=60
-      # Needed to prevent the X server from dying on logout and not coming back:
-      TerminateServer=true
-      ${optionalString (cfg.setupScript != "")
-      ''
-        Setup=${cfg.setupScript}
-      ''} 
-
-      [X-*-Greeter]
-      HiddenUsers=root,${concatStringsSep "," dmcfg.hiddenUsers}
-      PluginsLogin=${kdebase_workspace}/lib/kde4/kgreet_classic.so
-      ${optionalString (cfg.themeDirectory != null)
-      ''
-        UseTheme=true
-        Theme=${cfg.themeDirectory}
-      ''
-      }
-
-      ${optionalString (cfg.enableXDMCP)
-      ''
-        [Xdmcp]
-        Enable=true
-      ''}
-    '';
-
-  kdmrc = pkgs.runCommand "kdmrc"
-    { config = defaultConfig + cfg.extraConfig;
-      preferLocalBuild = true;
-    }
-    ''
-      echo "$config" > $out
-
-      # The default kdmrc would add "-nolisten tcp", and we already
-      # have that managed by nixos. Hence the grep.
-      cat ${kdebase_workspace}/share/config/kdm/kdmrc | grep -v nolisten >> $out
-    '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.xserver.displayManager.kdm = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to enable the KDE display manager.
-        '';
-      };
-
-      enableXDMCP = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to enable XDMCP, which allows remote logins.
-        '';
-      };
-
-      themeDirectory = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          The path to a KDM theme directory. This theme
-          will be used by the KDM greeter.
-        '';
-      };
-
-      setupScript = mkOption {
-        type = types.lines;
-        default = "";
-        description = ''
-          The path to a KDM setup script. This script is run as root just
-          before KDM starts. Can be used for setting up
-          monitors with xrandr, for example.
-        '';
-      };
-
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = ''
-          Options appended to <filename>kdmrc</filename>, the
-          configuration file of KDM.
-        '';
-      };
-
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    services.xserver.displayManager.slim.enable = false;
-
-    services.xserver.displayManager.job =
-      { execCmd =
-          ''
-            mkdir -m 0755 -p /var/lib/kdm
-            chown kdm /var/lib/kdm
-            ${(optionalString (config.system.boot.loader.id == "grub" && config.system.build.grub != null) "PATH=${config.system.build.grub}/sbin:$PATH ") +
-              "KDEDIRS=/run/current-system/sw exec ${kdebase_workspace}/bin/kdm -config ${kdmrc} -nodaemon -logfile /dev/stderr"}
-          '';
-        logsXsession = true;
-      };
-
-    security.pam.services.kde = { allowNullPassword = true; startSession = true; };
-
-    users.extraUsers = singleton
-      { name = "kdm";
-        uid = config.ids.uids.kdm;
-        description = "KDM user";
-      };
-
-    environment.systemPackages =
-      [ pkgs.kde4.kde_wallpapers ]; # contains kdm's default background
-
-  };
-
-}
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 dfda90978b1e..1d5dcb2c7cbe 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,7 @@ let
     theme-name = ${cfg.theme.name}
     icon-theme-name = ${cfg.iconTheme.name}
     background = ${ldmcfg.background}
+    ${cfg.extraConfig}
     '';
 
 in
@@ -103,6 +104,15 @@ in
 
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration that should be put in the lightdm-gtk-greeter.conf
+          configuration file.
+        '';
+      };
+
     };
 
   };
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 4afef32aaa47..256bfb9ce3f4 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -46,15 +46,13 @@ let
       [Seat:*]
       xserver-command = ${xserverWrapper}
       session-wrapper = ${dmcfg.session.script}
-      ${optionalString (elem defaultSessionName dmcfg.session.names) ''
-        user-session = ${defaultSessionName}
-      ''}
       ${optionalString cfg.greeter.enable ''
         greeter-session = ${cfg.greeter.name}
       ''}
       ${optionalString cfg.autoLogin.enable ''
         autologin-user = ${cfg.autoLogin.user}
         autologin-user-timeout = ${toString cfg.autoLogin.timeout}
+        autologin-session = ${defaultSessionName}
       ''}
       ${cfg.extraSeatDefaults}
     '';
@@ -63,7 +61,7 @@ let
     let
       dm = xcfg.desktopManager.default;
       wm = xcfg.windowManager.default;
-    in dm + optionalString (wm != "none") (" + " + wm);
+    in dm + optionalString (wm != "none") ("+" + wm);
 in
 {
   # Note: the order in which lightdm greeter modules are imported
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 6630b8257e4e..2eb7ddcb1ec0 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -9,7 +9,7 @@ let
   cfg = dmcfg.sddm;
   xEnv = config.systemd.services."display-manager".environment;
 
-  sddm = pkgs.sddm.override { inherit (cfg) themes; };
+  sddm = cfg.package;
 
   xserverWrapper = pkgs.writeScript "xserver-wrapper" ''
     #!/bin/sh
@@ -59,7 +59,7 @@ let
     [Autologin]
     User=${cfg.autoLogin.user}
     Session=${defaultSessionName}.desktop
-    Relogin=${if cfg.autoLogin.relogin then "true" else "false"}
+    Relogin=${boolToString cfg.autoLogin.relogin}
     ''}
 
     ${cfg.extraConfig}
@@ -69,7 +69,7 @@ let
     let
       dm = xcfg.desktopManager.default;
       wm = xcfg.windowManager.default;
-    in dm + optionalString (wm != "none") (" + " + wm);
+    in dm + optionalString (wm != "none") ("+" + wm);
 
 in
 {
@@ -105,11 +105,12 @@ in
         '';
       };
 
-      themes = mkOption {
-        type = types.listOf types.package;
-        default = [];
+      package = mkOption {
+        type = types.package;
+        default = pkgs.sddm;
         description = ''
-          Extra packages providing themes.
+          The SDDM package to install.
+          The default package can be overridden to provide extra themes.
         '';
       };
 
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 68acde85b5dc..0c4dd1973b53 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -17,9 +17,11 @@ let
       login_cmd exec ${pkgs.stdenv.shell} ${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
       ${optionalString (cfg.defaultUser != null) ("default_user " + cfg.defaultUser)}
       ${optionalString (cfg.defaultUser != null) ("focus_password yes")}
       ${optionalString cfg.autoLogin "auto_login yes"}
+      ${optionalString (cfg.consoleCmd != null) "console_cmd ${cfg.consoleCmd}"}
       ${cfg.extraConfig}
     '';
 
@@ -105,6 +107,18 @@ in
         '';
       };
 
+      consoleCmd = mkOption {
+        type = types.nullOr types.str;
+        default = ''
+          ${pkgs.xterm}/bin/xterm -C -fg white -bg black +sb -T "Console login" -e ${pkgs.shadow}/bin/login
+        '';
+        defaultText = ''
+          ''${pkgs.xterm}/bin/xterm -C -fg white -bg black +sb -T "Console login" -e ''${pkgs.shadow}/bin/login
+        '';
+        description = ''
+          The command to run when "console" is given as the username.
+        '';
+      };
     };
 
   };
@@ -115,11 +129,7 @@ in
   config = mkIf cfg.enable {
 
     services.xserver.displayManager.job =
-      { preStart =
-          ''
-            rm -f /var/log/slim.log
-          '';
-        environment =
+      { environment =
           { SLIM_CFGFILE = slimConfig;
             SLIM_THEMESDIR = slimThemesDir;
           };
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
new file mode 100644
index 000000000000..e60dd8765264
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -0,0 +1,249 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.displayManager.xpra;
+  dmcfg = config.services.xserver.displayManager;
+
+in
+
+{
+  ###### interface
+
+  options = {
+    services.xserver.displayManager.xpra = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable xpra as display manager.";
+      };
+
+      bindTcp = mkOption {
+        default = "127.0.0.1:10000";
+        example = "0.0.0.0:10000";
+        type = types.nullOr types.str;
+        description = "Bind xpra to TCP";
+      };
+
+      auth = mkOption {
+        type = types.str;
+        default = "pam";
+        example = "password:value=mysecret";
+        description = "Authentication to use when connecting to xpra";
+      };
+
+      pulseaudio = mkEnableOption "pulseaudio audio streaming.";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    services.xserver.videoDrivers = ["dummy"];
+
+    services.xserver.monitorSection = ''
+      HorizSync   1.0 - 2000.0
+      VertRefresh 1.0 - 200.0
+      #To add your own modes here, use a modeline calculator, like:
+      # cvt:
+      # http://www.x.org/archive/X11R7.5/doc/man/man1/cvt.1.html
+      # xtiming:
+      # http://xtiming.sourceforge.net/cgi-bin/xtiming.pl
+      # gtf:
+      # http://gtf.sourceforge.net/
+      #This can be used to get a specific DPI, but only for the default resolution:
+      #DisplaySize 508 317
+      #NOTE: the highest modes will not work without increasing the VideoRam
+      # for the dummy video card.
+      #Modeline "16000x15000" 300.00  16000 16408 18000 20000  15000 15003 15013 15016
+      #Modeline "15000x15000" 281.25  15000 15376 16872 18744  15000 15003 15013 15016
+      #Modeline "16384x8192" 167.75  16384 16800 18432 20480  8192 8195 8205 8208
+      #Modeline "15360x8640" 249.00 15360 15752 17280 19200 8640 8643 8648 8651
+      Modeline "8192x4096" 193.35 8192 8224 8952 8984 4096 4196 4200 4301
+      Modeline "7680x4320" 208.00 7680 7880 8640 9600 4320 4323 4328 4335
+      Modeline "6400x4096" 151.38 6400 6432 7000 7032 4096 4196 4200 4301
+      Modeline "6400x2560" 91.59 6400 6432 6776 6808 2560 2623 2626 2689
+      Modeline "6400x2160" 160.51 6400 6432 7040 7072 2160 2212 2216 2269
+      Modeline "5760x2160" 149.50 5760 5768 6320 6880 2160 2161 2164 2173
+      Modeline "5680x1440" 142.66 5680 5712 6248 6280 1440 1474 1478 1513
+      Modeline "5496x1200" 199.13 5496 5528 6280 6312 1200 1228 1233 1261
+      Modeline "5280x2560" 75.72 5280 5312 5592 5624 2560 2623 2626 2689
+      Modeline "5280x1920" 56.04 5280 5312 5520 5552 1920 1967 1969 2017
+      Modeline "5280x1200" 191.40 5280 5312 6032 6064 1200 1228 1233 1261
+      Modeline "5280x1080" 169.96 5280 5312 5952 5984 1080 1105 1110 1135
+      Modeline "5120x3200" 199.75 5120 5152 5904 5936 3200 3277 3283 3361
+      Modeline "5120x2560" 73.45 5120 5152 5424 5456 2560 2623 2626 2689
+      Modeline "5120x2880" 185.50 5120 5256 5760 6400 2880 2883 2888 2899
+      Modeline "4800x1200" 64.42 4800 4832 5072 5104 1200 1229 1231 1261
+      Modeline "4720x3840" 227.86 4720 4752 5616 5648 3840 3933 3940 4033
+      Modeline "4400x2560" 133.70 4400 4432 4936 4968 2560 2622 2627 2689
+      Modeline "4480x1440" 72.94 4480 4512 4784 4816 1440 1475 1478 1513
+      Modeline "4240x1440" 69.09 4240 4272 4528 4560 1440 1475 1478 1513
+      Modeline "4160x1440" 67.81 4160 4192 4448 4480 1440 1475 1478 1513
+      Modeline "4096x2304" 249.25 4096 4296 4720 5344 2304 2307 2312 2333
+      Modeline "4096x2160" 111.25 4096 4200 4608 5120 2160 2163 2173 2176
+      Modeline "4000x1660" 170.32 4000 4128 4536 5072 1660 1661 1664 1679
+      Modeline "4000x1440" 145.00 4000 4088 4488 4976 1440 1441 1444 1457
+      Modeline "3904x1440" 63.70 3904 3936 4176 4208 1440 1475 1478 1513
+      Modeline "3840x2880" 133.43 3840 3872 4376 4408 2880 2950 2955 3025
+      Modeline "3840x2560" 116.93 3840 3872 4312 4344 2560 2622 2627 2689
+      Modeline "3840x2160" 104.25 3840 3944 4320 4800 2160 2163 2168 2175
+      Modeline "3840x2048" 91.45 3840 3872 4216 4248 2048 2097 2101 2151
+      Modeline "3840x1200" 108.89 3840 3872 4280 4312 1200 1228 1232 1261
+      Modeline "3840x1080" 100.38 3840 3848 4216 4592 1080 1081 1084 1093
+      Modeline "3864x1050" 94.58 3864 3896 4248 4280 1050 1074 1078 1103
+      Modeline "3600x1200" 106.06 3600 3632 3984 4368 1200 1201 1204 1214
+      Modeline "3600x1080" 91.02 3600 3632 3976 4008 1080 1105 1109 1135
+      Modeline "3520x1196" 99.53 3520 3552 3928 3960 1196 1224 1228 1256
+      Modeline "3360x2560" 102.55 3360 3392 3776 3808 2560 2622 2627 2689
+      Modeline "3360x1050" 293.75 3360 3576 3928 4496 1050 1053 1063 1089
+      Modeline "3288x1080" 39.76 3288 3320 3464 3496 1080 1106 1108 1135
+      Modeline "3200x1800" 233.00 3200 3384 3720 4240  1800 1803 1808 1834
+      Modeline "3200x1080" 236.16 3200 3232 4128 4160 1080 1103 1112 1135
+      Modeline "3120x2560" 95.36 3120 3152 3512 3544 2560 2622 2627 2689
+      Modeline "3120x1050" 272.75 3120 3320 3648 4176 1050 1053 1063 1089
+      Modeline "3072x2560" 93.92 3072 3104 3456 3488 2560 2622 2627 2689
+      Modeline "3008x1692" 130.93 3008 3112 3416 3824 1692 1693 1696 1712
+      Modeline "3000x2560" 91.77 3000 3032 3376 3408 2560 2622 2627 2689
+      Modeline "2880x1620" 396.25 2880 3096 3408 3936 1620 1623 1628 1679
+      Modeline "2728x1680" 148.02 2728 2760 3320 3352 1680 1719 1726 1765
+      Modeline "2560x2240" 151.55 2560 2688 2952 3344 2240 2241 2244 2266
+      Modeline "2560x1600" 47.12 2560 2592 2768 2800 1600 1639 1642 1681
+      Modeline "2560x1440" 42.12 2560 2592 2752 2784 1440 1475 1478 1513
+      Modeline "2560x1400" 267.86 2560 2592 3608 3640 1400 1429 1441 1471
+      Modeline "2048x2048" 49.47 2048 2080 2264 2296 2048 2097 2101 2151
+      Modeline "2048x1536" 80.06 2048 2104 2312 2576 1536 1537 1540 1554
+      Modeline "2048x1152" 197.97 2048 2184 2408 2768 1152 1153 1156 1192
+      Modeline "2048x1152" 165.92 2048 2080 2704 2736 1152 1176 1186 1210
+      Modeline "1920x1440" 69.47 1920 1960 2152 2384 1440 1441 1444 1457
+      Modeline "1920x1200" 26.28 1920 1952 2048 2080 1200 1229 1231 1261
+      Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135
+      Modeline "1728x1520" 205.42 1728 1760 2536 2568 1520 1552 1564 1597
+      Modeline "1680x1050" 20.08 1680 1712 1784 1816 1050 1075 1077 1103
+      Modeline "1600x1200" 22.04 1600 1632 1712 1744 1200 1229 1231 1261
+      Modeline "1600x900" 33.92 1600 1632 1760 1792 900 921 924 946
+      Modeline "1440x900" 30.66 1440 1472 1584 1616 900 921 924 946
+      Modeline "1400x900" 103.50 1400 1480 1624 1848 900 903 913 934
+      ModeLine "1366x768" 72.00 1366 1414 1446 1494  768 771 777 803
+      Modeline "1360x768" 24.49 1360 1392 1480 1512 768 786 789 807
+      Modeline "1280x1024" 31.50 1280 1312 1424 1456 1024 1048 1052 1076
+      Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841
+      Modeline "1280x768" 23.11 1280 1312 1392 1424 768 786 789 807
+      Modeline "1280x720" 59.42 1280 1312 1536 1568 720 735 741 757
+      Modeline "1024x768" 18.71 1024 1056 1120 1152 768 786 789 807
+      Modeline "1024x640" 41.98 1024 1056 1208 1240 640 653 659 673
+      Modeline "1024x576" 46.50 1024 1064 1160 1296  576 579 584 599
+      Modeline "768x1024" 19.50 768 800 872 904 1024 1048 1052 1076
+      Modeline "960x540" 40.75 960 992 1088 1216 540 543 548 562
+      Modeline "864x486"  32.50 864 888 968 1072 486 489 494 506
+      Modeline "720x405" 22.50 720 744 808 896  405 408 413 422
+      Modeline "640x360" 14.75 640 664 720 800 360 363 368 374
+      #common resolutions for android devices (both orientations):
+      Modeline "800x1280" 25.89 800 832 928 960 1280 1310 1315 1345
+      Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841
+      Modeline "720x1280" 30.22 720 752 864 896 1280 1309 1315 1345
+      Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757
+      Modeline "768x1024" 24.93 768 800 888 920 1024 1047 1052 1076
+      Modeline "1024x768" 23.77 1024 1056 1144 1176 768 785 789 807
+      Modeline "600x1024" 19.90 600 632 704 736 1024 1047 1052 1076
+      Modeline "1024x600" 18.26 1024 1056 1120 1152 600 614 617 631
+      Modeline "536x960" 16.74 536 568 624 656 960 982 986 1009
+      Modeline "960x536" 15.23 960 992 1048 1080 536 548 551 563
+      Modeline "600x800" 15.17 600 632 688 720 800 818 822 841
+      Modeline "800x600" 14.50 800 832 880 912 600 614 617 631
+      Modeline "480x854" 13.34 480 512 560 592 854 873 877 897
+      Modeline "848x480" 12.09 848 880 920 952 480 491 493 505
+      Modeline "480x800" 12.43 480 512 552 584 800 818 822 841
+      Modeline "800x480" 11.46 800 832 872 904 480 491 493 505
+      #resolutions for android devices (both orientations)
+      #minus the status bar
+      #38px status bar (and width rounded up)
+      Modeline "800x1242" 25.03 800 832 920 952 1242 1271 1275 1305
+      Modeline "1280x762" 22.93 1280 1312 1392 1424 762 780 783 801
+      Modeline "720x1242" 29.20 720 752 856 888 1242 1271 1276 1305
+      Modeline "1280x682" 25.85 1280 1312 1408 1440 682 698 701 717
+      Modeline "768x986" 23.90 768 800 888 920 986 1009 1013 1036
+      Modeline "1024x730" 22.50 1024 1056 1136 1168 730 747 750 767
+      Modeline "600x986" 19.07 600 632 704 736 986 1009 1013 1036
+      Modeline "1024x562" 17.03 1024 1056 1120 1152 562 575 578 591
+      Modeline "536x922" 16.01 536 568 624 656 922 943 947 969
+      Modeline "960x498" 14.09 960 992 1040 1072 498 509 511 523
+      Modeline "600x762" 14.39 600 632 680 712 762 779 783 801
+      Modeline "800x562" 13.52 800 832 880 912 562 575 578 591
+      Modeline "480x810" 12.59 480 512 552 584 810 828 832 851
+      Modeline "848x442" 11.09 848 880 920 952 442 452 454 465
+      Modeline "480x762" 11.79 480 512 552 584 762 779 783 801
+    '';
+
+    services.xserver.resolutions = [
+      {x="8192"; y="4096";}
+      {x="5120"; y="3200";}
+      {x="3840"; y="2880";}
+      {x="3840"; y="2560";}
+      {x="3840"; y="2048";}
+      {x="3840"; y="2160";}
+      {x="2048"; y="2048";}
+      {x="2560"; y="1600";}
+      {x="1920"; y="1440";}
+      {x="1920"; y="1200";}
+      {x="1920"; y="1080";}
+      {x="1600"; y="1200";}
+      {x="1680"; y="1050";}
+      {x="1600"; y="900";}
+      {x="1400"; y="1050";}
+      {x="1440"; y="900";}
+      {x="1280"; y="1024";}
+      {x="1366"; y="768";}
+      {x="1280"; y="800";}
+      {x="1024"; y="768";}
+      {x="1024"; y="600";}
+      {x="800"; y="600";}
+      {x="320"; y="200";}
+    ];
+
+    services.xserver.serverFlagsSection = ''
+      Option "DontVTSwitch" "true"
+      Option "PciForceNone" "true"
+      Option "AutoEnableDevices" "false"
+      Option "AutoAddDevices" "false"
+    '';
+
+    services.xserver.deviceSection = ''
+      VideoRam 192000
+    '';
+
+    services.xserver.displayManager.job = {
+      logsXsession = true;
+
+      execCmd = ''
+        ${optionalString (cfg.pulseaudio)
+          "export PULSE_COOKIE=/var/run/pulse/.config/pulse/cookie"}
+        exec ${pkgs.xpra}/bin/xpra start \
+          --daemon=off \
+          --log-dir=/var/log \
+          --log-file=xpra.log \
+          --opengl=on \
+          --clipboard=on \
+          --notifications=on \
+          --speaker=yes \
+          --mdns=no \
+          --pulseaudio=no \
+          ${optionalString (cfg.pulseaudio) "--sound-source=pulse"} \
+          --socket-dirs=/var/run/xpra \
+          --xvfb="xpra_Xdummy ${concatStringsSep " " dmcfg.xserverArgs}" \
+          ${optionalString (cfg.bindTcp != null) "--bind-tcp=${cfg.bindTcp}"} \
+          --auth=${cfg.auth}
+      '';
+    };
+
+    services.xserver.terminateOnReset = false;
+
+    environment.systemPackages = [pkgs.xpra];
+
+    virtualisation.virtualbox.guest.x11 = false;
+    hardware.pulseaudio.enable = mkDefault cfg.pulseaudio;
+    hardware.pulseaudio.systemWide = mkDefault cfg.pulseaudio;
+  };
+
+}
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index b358550ba41d..d75c785270b3 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -73,7 +73,6 @@ in {
       clickMethod = mkOption {
         type = types.nullOr (types.enum [ "none" "buttonareas" "clickfinger" ]);
         default = null;
-        example = "none";
         description =
           ''
             Enables a click method. Permitted values are none, buttonareas, clickfinger.
@@ -85,14 +84,12 @@ in {
       leftHanded = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Enables left-handed button orientation, i.e. swapping left and right buttons.";
       };
 
       middleEmulation = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description =
           ''
             Enables middle button emulation. When enabled, pressing the left and right buttons
@@ -103,7 +100,6 @@ in {
       naturalScrolling = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Enables or disables natural scrolling behavior.";
       };
 
@@ -131,7 +127,6 @@ in {
       horizontalScrolling = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description =
           ''
             Disables horizontal scrolling. When disabled, this driver will discard any horizontal scroll
@@ -153,7 +148,6 @@ in {
       tapping = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description =
           ''
             Enables or disables tap-to-click behavior.
@@ -163,7 +157,6 @@ in {
       tappingDragLock = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description =
           ''
             Enables or disables drag lock during tapping behavior. When enabled, a finger up during tap-
@@ -175,7 +168,6 @@ in {
       disableWhileTyping = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description =
           ''
             Disable input method while typing.
diff --git a/nixos/modules/services/x11/hardware/multitouch.nix b/nixos/modules/services/x11/hardware/multitouch.nix
index f8386b5e333b..c03bb3b494fb 100644
--- a/nixos/modules/services/x11/hardware/multitouch.nix
+++ b/nixos/modules/services/x11/hardware/multitouch.nix
@@ -18,20 +18,17 @@ in {
 
       enable = mkOption {
         default = false;
-        example = true;
         description = "Whether to enable multitouch touchpad support.";
       };
 
       invertScroll = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Whether to invert scrolling direction à la OSX Lion";
       };
 
       ignorePalm = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = "Whether to ignore touches detected as being the palm (i.e when typing)";
       };
@@ -39,7 +36,6 @@ in {
       tapButtons = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = "Whether to enable tap buttons.";
       };
 
@@ -78,7 +74,7 @@ in {
           MatchIsTouchpad "on"
           Identifier "Touchpads"
           Driver "mtrack"
-          Option "IgnorePalm" "${if cfg.ignorePalm then "true" else "false"}"
+          Option "IgnorePalm" "${boolToString cfg.ignorePalm}"
           Option "ClickFinger1" "${builtins.elemAt cfg.buttonsMap 0}"
           Option "ClickFinger2" "${builtins.elemAt cfg.buttonsMap 1}"
           Option "ClickFinger3" "${builtins.elemAt cfg.buttonsMap 2}"
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index 2a7f4e5cbcd1..54454c736c1d 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -29,7 +29,6 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Whether to enable touchpad support.";
       };
 
@@ -102,7 +101,6 @@ in {
       tapButtons = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = "Whether to enable tap buttons.";
       };
 
@@ -125,7 +123,6 @@ in {
       palmDetect = mkOption {
         type = types.bool;
         default = false;
-        example = true;
         description = "Whether to enable palm detection (hardware support required)";
       };
 
@@ -146,7 +143,6 @@ in {
       horizontalScroll = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = "Whether to enable horizontal scrolling (on touchpad)";
       };
 
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index 78a97df98775..eb5dfdf95849 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -12,7 +12,6 @@ in {
     enable = mkOption {
       type = types.bool;
       default = false;
-      example = true;
       description = ''
         Enable Redshift to change your screen's colour temperature depending on
         the time of day.
diff --git a/nixos/modules/services/x11/terminal-server.nix b/nixos/modules/services/x11/terminal-server.nix
index 785394d9648c..09a7f386876f 100644
--- a/nixos/modules/services/x11/terminal-server.nix
+++ b/nixos/modules/services/x11/terminal-server.nix
@@ -16,18 +16,8 @@ with lib;
     services.xserver.enable = true;
     services.xserver.videoDrivers = [];
 
-    # Enable KDM.  Any display manager will do as long as it supports XDMCP.
-    services.xserver.displayManager.kdm.enable = true;
-    services.xserver.displayManager.kdm.enableXDMCP = true;
-    services.xserver.displayManager.kdm.extraConfig =
-      ''
-        [General]
-        # We're headless, so don't bother starting an X server.
-        StaticServers=
-
-        [Xdmcp]
-        Xaccess=${pkgs.writeText "Xaccess" "localhost"}
-      '';
+    # Enable GDM.  Any display manager will do as long as it supports XDMCP.
+    services.xserver.displayManager.gdm.enable = true;
 
     systemd.sockets.terminal-server =
       { description = "Terminal Server Socket";
diff --git a/nixos/modules/services/x11/unclutter-xfixes.nix b/nixos/modules/services/x11/unclutter-xfixes.nix
index bd02c5ed9895..b94dfb1a26a6 100644
--- a/nixos/modules/services/x11/unclutter-xfixes.nix
+++ b/nixos/modules/services/x11/unclutter-xfixes.nix
@@ -11,7 +11,6 @@ in {
       description = "Enable unclutter-xfixes to hide your mouse cursor when inactive.";
       type = types.bool;
       default = false;
-      example = true;
     };
 
     package = mkOption {
diff --git a/nixos/modules/services/x11/unclutter.nix b/nixos/modules/services/x11/unclutter.nix
index ebc195d108d0..a22e5ac2c95a 100644
--- a/nixos/modules/services/x11/unclutter.nix
+++ b/nixos/modules/services/x11/unclutter.nix
@@ -11,7 +11,6 @@ in {
       description = "Enable unclutter to hide your mouse cursor when inactive";
       type = types.bool;
       default = false;
-      example = true;
     };
 
     package = mkOption {
@@ -61,7 +60,10 @@ in {
       serviceConfig.ExecStart = ''
         ${cfg.package}/bin/unclutter \
           -idle ${toString cfg.timeout} \
-          -display :${toString config.services.xserver.display} \
+          -display :${toString (
+             let display = config.services.xserver.display;
+             in if display != null then display else 0
+          )} \
           -jitter ${toString (cfg.threeshold - 1)} \
           ${optionalString cfg.keystroke "-keystroke"} \
           ${concatMapStrings (x: " -"+x) cfg.extraOptions} \
diff --git a/nixos/modules/services/x11/urxvtd.nix b/nixos/modules/services/x11/urxvtd.nix
index be36efaa5897..57ad93f20174 100644
--- a/nixos/modules/services/x11/urxvtd.nix
+++ b/nixos/modules/services/x11/urxvtd.nix
@@ -11,7 +11,6 @@ in {
   options.services.urxvtd.enable = mkOption {
     type = types.bool;
     default = false;
-    example = true;
     description = ''
       Enable urxvtd, the urxvt terminal daemon. To use urxvtd, run
       "urxvtc".
diff --git a/nixos/modules/services/x11/window-managers/2bwm.nix b/nixos/modules/services/x11/window-managers/2bwm.nix
new file mode 100644
index 000000000000..e3f5ec7dbe67
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/2bwm.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.xserver.windowManager."2bwm";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.xserver.windowManager."2bwm".enable = mkEnableOption "2bwm";
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "2bwm";
+        start =
+          ''
+            ${pkgs."2bwm"}/bin/2bwm &
+            waitPID=$!
+          '';
+      };
+
+    environment.systemPackages = [ pkgs."2bwm" ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index f005decfa33c..32ef34bdad2b 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -8,12 +8,14 @@ in
 
 {
   imports = [
+    ./2bwm.nix
     ./afterstep.nix
     ./bspwm.nix
     ./compiz.nix
     ./dwm.nix
     ./exwm.nix
     ./fluxbox.nix
+    ./fvwm.nix
     ./herbstluftwm.nix
     ./i3.nix
     ./jwm.nix
diff --git a/nixos/modules/services/x11/window-managers/exwm.nix b/nixos/modules/services/x11/window-managers/exwm.nix
index dbbd8a125d66..dc1d957c1709 100644
--- a/nixos/modules/services/x11/window-managers/exwm.nix
+++ b/nixos/modules/services/x11/window-managers/exwm.nix
@@ -21,7 +21,6 @@ in
       enable = mkEnableOption "exwm";
       enableDefaultConfig = mkOption {
         default = true;
-        example = false;
         type = lib.types.bool;
         description = "Enable an uncustomised exwm configuration.";
       };
diff --git a/nixos/modules/services/x11/window-managers/fvwm.nix b/nixos/modules/services/x11/window-managers/fvwm.nix
new file mode 100644
index 000000000000..9a51b9cd6602
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/fvwm.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.fvwm;
+  fvwm = pkgs.fvwm.override { gestures = cfg.gestures; };
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.xserver.windowManager.fvwm = {
+      enable = mkEnableOption "Fvwm window manager";
+
+      gestures = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether or not to enable libstroke for gesture support";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton
+      { name = "fvwm";
+        start =
+          ''
+            ${fvwm}/bin/fvwm &
+            waitPID=$!
+          '';
+      };
+
+    environment.systemPackages = [ fvwm ];
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/herbstluftwm.nix b/nixos/modules/services/x11/window-managers/herbstluftwm.nix
index 829935fa432b..e3ea61cb9a6b 100644
--- a/nixos/modules/services/x11/window-managers/herbstluftwm.nix
+++ b/nixos/modules/services/x11/window-managers/herbstluftwm.nix
@@ -8,15 +8,30 @@ in
 
 {
   options = {
-    services.xserver.windowManager.herbstluftwm.enable = mkEnableOption "herbstluftwm";
+    services.xserver.windowManager.herbstluftwm = {
+      enable = mkEnableOption "herbstluftwm";
+
+      configFile = mkOption {
+        default     = null;
+        type        = with types; nullOr path;
+        description = ''
+          Path to the herbstluftwm configuration file.  If left at the
+          default value, $XDG_CONFIG_HOME/herbstluftwm/autostart will
+          be used.
+        '';
+      };
+    };
   };
 
   config = mkIf cfg.enable {
     services.xserver.windowManager.session = singleton {
       name = "herbstluftwm";
-      start = "
-        ${pkgs.herbstluftwm}/bin/herbstluftwm
-      ";
+      start =
+        let configFileClause = optionalString
+            (cfg.configFile != null)
+            ''-c "${cfg.configFile}"''
+            ;
+        in "${pkgs.herbstluftwm}/bin/herbstluftwm ${configFileClause}";
     };
     environment.systemPackages = [ pkgs.herbstluftwm ];
   };
diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
index 6af88d4f645b..e25a8ae22823 100644
--- a/nixos/modules/services/x11/window-managers/xmonad.nix
+++ b/nixos/modules/services/x11/window-managers/xmonad.nix
@@ -44,7 +44,6 @@ in
 
       enableContribAndExtras = mkOption {
         default = false;
-        example = true;
         type = lib.types.bool;
         description = "Enable xmonad-{contrib,extras} in Xmonad.";
       };
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index f5ed52338182..262115b39e79 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -258,7 +258,7 @@ in
         type = types.str;
         default = "us";
         description = ''
-          Keyboard layout.
+          Keyboard layout, or multiple keyboard layouts separated by commas.
         '';
       };
 
@@ -435,6 +435,14 @@ in
           by default.
         '';
       };
+
+      terminateOnReset = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to terminate X upon server reset.
+        '';
+      };
     };
 
   };
@@ -459,6 +467,8 @@ in
           knownVideoDrivers;
       in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
 
+    nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
+
     assertions =
       [ { assertion = config.security.polkit.enable;
           message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’).";
@@ -548,8 +558,7 @@ in
       };
 
     services.xserver.displayManager.xserverArgs =
-      [ "-terminate"
-        "-config ${configFile}"
+      [ "-config ${configFile}"
         "-xkbdir" "${cfg.xkbDir}"
         # Log at the default verbosity level to stderr rather than /var/log/X.*.log.
         "-verbose" "3" "-logfile" "/dev/null"
@@ -558,7 +567,8 @@ in
         ++ optional (cfg.dpi     != null) "-dpi ${toString cfg.dpi}"
         ++ optional (!cfg.enableTCP) "-nolisten tcp"
         ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}"
-        ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}";
+        ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}"
+        ++ optional cfg.terminateOnReset "-terminate";
 
     services.xserver.modules =
       concatLists (catAttrs "modules" cfg.drivers) ++
@@ -568,6 +578,35 @@ in
 
     services.xserver.xkbDir = mkDefault "${pkgs.xkeyboard_config}/etc/X11/xkb";
 
+    system.extraDependencies = [
+      (pkgs.runCommand "xkb-layouts-exist" {
+            layouts=cfg.layout;
+        } ''
+        missing=()
+        while read -d , layout
+        do
+          [[ -f "${cfg.xkbDir}/symbols/$layout" ]] || missing+=($layout)
+        done <<< "$layouts,"
+        if [[ ''${#missing[@]} -eq 0 ]]
+        then
+          touch $out
+          exit 0
+        fi
+
+        cat >&2 <<EOF
+
+        Some of the selected keyboard layouts do not exist:
+
+          ''${missing[@]}
+
+        Set services.xserver.layout to the name of an existing keyboard
+        layout (check ${cfg.xkbDir}/symbols for options).
+
+        EOF
+        exit -1
+      '')
+    ];
+
     services.xserver.config =
       ''
         Section "ServerFlags"
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index dcf105eb7844..c2ac731d433d 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -19,6 +19,7 @@ let
       glibc # needed for getent
       shadow
       nettools # needed for hostname
+      utillinux # needed for mount and mountpoint
     ];
 
 in
@@ -168,12 +169,12 @@ in
           local options="$3"
           local fsType="$4"
 
-          if ${pkgs.utillinux}/bin/mountpoint -q "$mountPoint"; then
+          if mountpoint -q "$mountPoint"; then
             local options="remount,$options"
           else
             mkdir -m 0755 -p "$mountPoint"
           fi
-          ${pkgs.utillinux}/bin/mount -t "$fsType" -o "$options" "$device" "$mountPoint"
+          mount -t "$fsType" -o "$options" "$device" "$mountPoint"
         }
         source ${config.system.build.earlyMountScript}
       '';
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 8747c1e3d4ac..88e7847cf8c8 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -41,7 +41,7 @@ if ($action eq "switch" || $action eq "boot") {
 }
 
 # Just in case the new configuration hangs the system, do a sync now.
-system("@coreutils@/bin/sync") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1";
+system("@coreutils@/bin/sync", "-f", "/nix/store") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1";
 
 exit 0 if $action eq "boot";
 
@@ -383,6 +383,10 @@ system("@systemd@/bin/systemctl", "reset-failed");
 # Make systemd reload its units.
 system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3;
 
+# Set the new tmpfiles
+print STDERR "setting up tmpfiles\n";
+system("@systemd@/bin/systemd-tmpfiles", "--create", "--remove", "--exclude-prefix=/dev") == 0 or $res = 3;
+
 # Reload units that need it. This includes remounting changed mount
 # units.
 if (scalar(keys %unitsToReload) > 0) {
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 0c08375da646..e9897cc01b6a 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -45,11 +45,16 @@ let
 
         ln -s ${kernelPath} $out/kernel
         ln -s ${config.system.modulesTree} $out/kernel-modules
+        ${optionalString (pkgs.stdenv.platform.kernelDTB or false) ''
+          ln -s ${config.boot.kernelPackages.kernel}/dtbs $out/dtbs
+        ''}
 
         echo -n "$kernelParams" > $out/kernel-params
 
         ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
 
+        ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
+
         ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
       ''}
 
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index 59ecaf8d5a6d..d78775c27582 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -44,9 +44,10 @@ in
       description = ''
         RSA SSH private key file in the Dropbear format.
 
-        WARNING: This key is contained insecurely in the global Nix store. Do NOT
-        use your regular SSH host private keys for this purpose or you'll expose
-        them to regular users!
+        WARNING: Unless your bootloader supports initrd secrets, this key is
+        contained insecurely in the global Nix store. Do NOT use your regular
+        SSH host private keys for this purpose or you'll expose them to
+        regular users!
       '';
     };
 
@@ -56,9 +57,10 @@ in
       description = ''
         DSS SSH private key file in the Dropbear format.
 
-        WARNING: This key is contained insecurely in the global Nix store. Do NOT
-        use your regular SSH host private keys for this purpose or you'll expose
-        them to regular users!
+        WARNING: Unless your bootloader supports initrd secrets, this key is
+        contained insecurely in the global Nix store. Do NOT use your regular
+        SSH host private keys for this purpose or you'll expose them to
+        regular users!
       '';
     };
 
@@ -68,9 +70,10 @@ in
       description = ''
         ECDSA SSH private key file in the Dropbear format.
 
-        WARNING: This key is contained insecurely in the global Nix store. Do NOT
-        use your regular SSH host private keys for this purpose or you'll expose
-        them to regular users!
+        WARNING: Unless your bootloader supports initrd secrets, this key is
+        contained insecurely in the global Nix store. Do NOT use your regular
+        SSH host private keys for this purpose or you'll expose them to
+        regular users!
       '';
     };
 
@@ -97,10 +100,6 @@ in
     boot.initrd.extraUtilsCommands = ''
       copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear
       cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib
-
-      ${optionalString (cfg.hostRSAKey != null) "install -D ${cfg.hostRSAKey} $out/etc/dropbear/dropbear_rsa_host_key"}
-      ${optionalString (cfg.hostDSSKey != null) "install -D ${cfg.hostDSSKey} $out/etc/dropbear/dropbear_dss_host_key"}
-      ${optionalString (cfg.hostECDSAKey != null) "install -D ${cfg.hostECDSAKey} $out/etc/dropbear/dropbear_ecdsa_host_key"}
     '';
 
     boot.initrd.extraUtilsCommandsTest = ''
@@ -116,9 +115,6 @@ in
       touch /var/log/lastlog
 
       mkdir -p /etc/dropbear
-      ${optionalString (cfg.hostRSAKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_rsa_host_key /etc/dropbear/dropbear_rsa_host_key"}
-      ${optionalString (cfg.hostDSSKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_dss_host_key /etc/dropbear/dropbear_dss_host_key"}
-      ${optionalString (cfg.hostECDSAKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_ecdsa_host_key /etc/dropbear/dropbear_ecdsa_host_key"}
 
       mkdir -p /root/.ssh
       ${concatStrings (map (key: ''
@@ -128,6 +124,11 @@ in
       dropbear -s -j -k -E -m -p ${toString cfg.port}
     '';
 
+    boot.initrd.secrets =
+     (optionalAttrs (cfg.hostRSAKey != null) { "/etc/dropbear/dropbear_rsa_host_key" = cfg.hostRSAKey; }) //
+     (optionalAttrs (cfg.hostDSSKey != null) { "/etc/dropbear/dropbear_dss_host_key" = cfg.hostDSSKey; }) //
+     (optionalAttrs (cfg.hostECDSAKey != null) { "/etc/dropbear/dropbear_ecdsa_host_key" = cfg.hostECDSAKey; });
+
   };
 
 }
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index e751ff141f70..cf70a891c0ca 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -176,7 +176,7 @@ in
 
     boot.initrd.availableKernelModules =
       [ # Note: most of these (especially the SATA/PATA modules)
-        # shouldn't be included by default since nixos-hardware-scan
+        # shouldn't be included by default since nixos-generate-config
         # detects them, but I'm keeping them for now for backwards
         # compatibility.
 
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 294fc1988e9f..cf47aed9fa99 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -53,12 +53,14 @@ let
       inherit (args) devices;
       inherit (efi) canTouchEfiVariables;
       inherit (cfg)
-        version extraConfig extraPerEntryConfig extraEntries forceInstall 
-        extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels
+        version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber
+        extraEntriesBeforeNixOS extraPrepareConfig extraInitrd configurationLimit copyKernels
         default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios;
       path = (makeBinPath ([
         pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfs-progs
-        pkgs.utillinux ] ++ (if cfg.efiSupport && (cfg.version == 2) then [pkgs.efibootmgr ] else [])
+        pkgs.utillinux ]
+        ++ (optional (cfg.efiSupport && (cfg.version == 2)) pkgs.efibootmgr)
+        ++ (optionals cfg.useOSProber [pkgs.busybox pkgs.os-prober])
       )) + ":" + (makeSearchPathOutput "bin" "sbin" [
         pkgs.mdadm pkgs.utillinux
       ]);
@@ -237,6 +239,12 @@ in
           menuentry "Windows 7" {
             chainloader (hd0,4)+1
           }
+
+          # GRUB 2 with UEFI example, chainloading another distro
+          menuentry "Fedora" {
+            set root=(hd1,1)
+            chainloader /efi/fedora/grubx64.efi
+          }
         '';
         description = ''
           Any additional entries you want added to the GRUB boot menu.
@@ -265,6 +273,27 @@ in
         '';
       };
 
+      extraInitrd = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/boot/extra_initrafms.gz";
+        description = ''
+          The path to a second initramfs to be supplied to the kernel.
+          This ramfs will not be copied to the store, so that it can
+          contain secrets such as LUKS keyfiles or ssh keys.
+          This implies that rolling back to a previous configuration
+          won't rollback the state of this file.
+        '';
+      };
+
+      useOSProber = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If set to true, append entries for other OSs detected by os-prober.
+        '';
+      };
+
       splashImage = mkOption {
         type = types.nullOr types.path;
         example = literalExample "./my-background.png";
@@ -358,7 +387,6 @@ in
 
       efiInstallAsRemovable = mkOption {
         default = false;
-        example = true;
         type = types.bool;
         description = ''
           Whether to invoke <literal>grub-install</literal> with
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index 24442ca12a30..5fcac5c8c6a4 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -49,6 +49,7 @@ my $extraPrepareConfig = get("extraPrepareConfig");
 my $extraPerEntryConfig = get("extraPerEntryConfig");
 my $extraEntries = get("extraEntries");
 my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
+my $extraInitrd = get("extraInitrd");
 my $splashImage = get("splashImage");
 my $configurationLimit = int(get("configurationLimit"));
 my $copyKernels = get("copyKernels") eq "true";
@@ -226,6 +227,13 @@ my $grubStore;
 if ($copyKernels == 0) {
     $grubStore = GrubFs($storePath);
 }
+my $extraInitrdPath;
+if ($extraInitrd) {
+    if (! -f $extraInitrd) {
+        print STDERR "Warning: the specified extraInitrd " . $extraInitrd . " doesn't exist. Your system won't boot without it.\n";
+    }
+    $extraInitrdPath = GrubFs($extraInitrd);
+}
 
 # Generate the header.
 my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
@@ -256,8 +264,6 @@ else {
         # ‘grub-reboot’ sets a one-time saved entry, which we process here and
         # then delete.
         if [ \"\${next_entry}\" ]; then
-          # FIXME: KDM expects the next line to be present.
-          set default=\"\${saved_entry}\"
           set default=\"\${next_entry}\"
           set next_entry=
           save_env next_entry
@@ -338,6 +344,9 @@ sub addEntry {
 
     my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
     my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
+    if ($extraInitrd) {
+        $initrd .= " " .$extraInitrdPath->path;
+    }
     my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
 
     # FIXME: $confName
@@ -360,6 +369,9 @@ sub addEntry {
         if ($copyKernels == 0) {
             $conf .= $grubStore->search . "\n";
         }
+        if ($extraInitrd) {
+            $conf .= $extraInitrdPath->search . "\n";
+        }
         $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
         $conf .= "  multiboot $xen $xenParams\n" if $xen;
         $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
@@ -426,10 +438,48 @@ if ($extraPrepareConfig ne "") {
   system((get("shell"), "-c", $extraPrepareConfig));
 }
 
-# Atomically update the GRUB config.
+# write the GRUB config.
 my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg";
 my $tmpFile = $confFile . ".tmp";
 writeFile($tmpFile, $conf);
+
+
+# check whether to install GRUB EFI or not
+sub getEfiTarget {
+    if ($grubVersion == 1) {
+        return "no"
+    } elsif (($grub ne "") && ($grubEfi ne "")) {
+        # EFI can only be installed when target is set;
+        # A target is also required then for non-EFI grub
+        if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
+        else { return "both" }
+    } elsif (($grub ne "") && ($grubEfi eq "")) {
+        # TODO: It would be safer to disallow non-EFI grub installation if no taget is given.
+        #       If no target is given, then grub auto-detects the target which can lead to errors.
+        #       E.g. it seems as if grub would auto-detect a EFI target based on the availability
+        #       of a EFI partition.
+        #       However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386
+        #       architectures in NixOS. That would have to be fixed in the nixos modules first.
+        return "no"
+    } elsif (($grub eq "") && ($grubEfi ne "")) {
+        # EFI can only be installed when target is set;
+        if ($grubTargetEfi eq "") { die }
+        else {return "only" }
+    } else {
+        # prevent an installation if neither grub nor grubEfi is given
+        return "neither"
+    }
+}
+
+my $efiTarget = getEfiTarget();
+
+# Append entries detected by os-prober
+if (get("useOSProber") eq "true") {
+    my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi;
+    system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile");
+}
+
+# Atomically switch to the new config
 rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n";
 
 
@@ -479,36 +529,7 @@ sub getDeviceTargets {
     }
     return @devices;
 }
-
-# check whether to install GRUB EFI or not
-sub getEfiTarget {
-    if ($grubVersion == 1) {
-        return "no"
-    } elsif (($grub ne "") && ($grubEfi ne "")) {
-        # EFI can only be installed when target is set;
-        # A target is also required then for non-EFI grub
-        if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
-        else { return "both" }
-    } elsif (($grub ne "") && ($grubEfi eq "")) {
-        # TODO: It would be safer to disallow non-EFI grub installation if no taget is given.
-        #       If no target is given, then grub auto-detects the target which can lead to errors.
-        #       E.g. it seems as if grub would auto-detect a EFI target based on the availability
-        #       of a EFI partition.
-        #       However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386
-        #       architectures in NixOS. That would have to be fixed in the nixos modules first.
-        return "no"
-    } elsif (($grub eq "") && ($grubEfi ne "")) {
-        # EFI can only be installed when target is set;
-        if ($grubTargetEfi eq "") { die }
-        else {return "only" }
-    } else {
-        # prevent an installation if neither grub nor grubEfi is given
-        return "neither"
-    }
-}
-
 my @deviceTargets = getDeviceTargets();
-my $efiTarget = getEfiTarget();
 my $prevGrubState = readGrubState();
 my @prevDeviceTargets = split/,/, $prevGrubState->devices;
 
diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder.sh b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
index ccb88ca1c529..f627d093eafb 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/builder.sh
+++ b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
@@ -61,12 +61,13 @@ addEntry() {
 
     local kernel=$(readlink -f $path/kernel)
     local initrd=$(readlink -f $path/initrd)
+    local dtb_path=$(readlink -f $path/kernel-modules/dtbs)
 
     if test -n "@copyKernels@"; then
         copyToKernelsDir $kernel; kernel=$result
         copyToKernelsDir $initrd; initrd=$result
     fi
-    
+
     echo $(readlink -f $path) > $outdir/$generation-system
     echo $(readlink -f $path/init) > $outdir/$generation-init
     cp $path/kernel-params $outdir/$generation-cmdline.txt
@@ -80,6 +81,11 @@ addEntry() {
         copyForced $kernel /boot/kernel7.img
       fi
       copyForced $initrd /boot/initrd
+      for dtb in $dtb_path/bcm*.dtb; do
+        dst="/boot/$(basename $dtb)"
+        copyForced $dtb "$dst"
+        filesCopied[$dst]=1
+      done
       cp "$(readlink -f "$path/init")" /boot/nixos-init
       echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt
 
@@ -108,8 +114,8 @@ copyForced $fwdir/start_cd.elf  /boot/start_cd.elf
 copyForced $fwdir/start_db.elf  /boot/start_db.elf
 copyForced $fwdir/start_x.elf   /boot/start_x.elf
 
-# Remove obsolete files from /boot/old.
-for fn in /boot/old/*linux* /boot/old/*initrd*; do
+# Remove obsolete files from /boot and /boot/old.
+for fn in /boot/old/*linux* /boot/old/*initrd-initrd* /boot/bcm*.dtb; do
     if ! test "${filesCopied[$fn]}" = 1; then
         rm -vf -- "$fn"
     fi
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index eb8ea6130972..f246d04284ca 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -33,7 +33,7 @@ in
 
     boot.loader.raspberryPi.version = mkOption {
       default = 2;
-      type = types.enum [ 1 2 ];
+      type = types.enum [ 1 2 3 ];
       description = ''
       '';
     };
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 515136c904c5..704c574b822e 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -1,4 +1,4 @@
-#! @python3@/bin/python3
+#! @python3@/bin/python3 -B
 import argparse
 import shutil
 import os
@@ -28,10 +28,15 @@ def write_loader_conf(generation):
         if "@timeout@" != "":
             f.write("timeout @timeout@\n")
         f.write("default nixos-generation-%d\n" % generation)
+        if not @editor@:
+            f.write("editor 0");
     os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
 
+def profile_path(generation, name):
+    return os.readlink("%s/%s" % (system_dir(generation), name))
+
 def copy_from_profile(generation, name, dry_run=False):
-    store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
+    store_file_path = profile_path(generation, name)
     suffix = os.path.basename(store_file_path)
     store_dir = os.path.basename(os.path.dirname(store_file_path))
     efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
@@ -42,6 +47,11 @@ def copy_from_profile(generation, name, dry_run=False):
 def write_entry(generation, machine_id):
     kernel = copy_from_profile(generation, "kernel")
     initrd = copy_from_profile(generation, "initrd")
+    try:
+        append_initrd_secrets = profile_path(generation, "append-initrd-secrets")
+        subprocess.check_call([append_initrd_secrets, "@efiSysMountPoint@%s" % (initrd)])
+    except FileNotFoundError:
+        pass
     entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
     generation_dir = os.readlink(system_dir(generation))
     tmp_path = "%s.tmp" % (entry_file)
@@ -99,11 +109,27 @@ def main():
     parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
     args = parser.parse_args()
 
+    try:
+        with open("/etc/machine-id") as machine_file:
+            machine_id = machine_file.readlines()[0]
+    except IOError as e:
+        if e.errno != errno.ENOENT:
+            raise
+        # Since systemd version 232 a machine ID is required and it might not
+        # be there on newly installed systems, so let's generate one so that
+        # bootctl can find it and we can also pass it to write_entry() later.
+        cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"]
+        machine_id = subprocess.check_output(cmd).rstrip()
+
     if os.getenv("NIXOS_INSTALL_GRUB") == "1":
         warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
         os.environ["NIXOS_INSTALL_BOOTLOADER"] = "1"
 
     if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
+        # bootctl uses fopen() with modes "wxe" and fails if the file exists.
+        if os.path.exists("@efiSysMountPoint@/loader/loader.conf"):
+            os.unlink("@efiSysMountPoint@/loader/loader.conf")
+
         if "@canTouchEfiVariables@" == "1":
             subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
         else:
@@ -111,13 +137,6 @@ def main():
 
     mkdir_p("@efiSysMountPoint@/efi/nixos")
     mkdir_p("@efiSysMountPoint@/loader/entries")
-    try:
-        with open("/etc/machine-id") as machine_file:
-            machine_id = machine_file.readlines()[0]
-    except IOError as e:
-        if e.errno != errno.ENOENT:
-            raise
-        machine_id = None
 
     gens = get_generations("system")
     remove_old_entries(gens)
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index cc43fb8bab4c..a5a88a99be8f 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -20,6 +20,8 @@ let
 
     timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else "";
 
+    editor = if cfg.editor then "True" else "False";
+
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
   };
 in {
@@ -36,6 +38,20 @@ in {
 
       description = "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager";
     };
+
+    editor = mkOption {
+      default = true;
+
+      type = types.bool;
+
+      description = ''
+        Whether to allow editing the kernel command-line before
+        boot. It is recommended to set this to false, as it allows
+        gaining root access by passing init=/bin/sh as a kernel
+        parameter. However, it is enabled by default for backwards
+        compatibility.
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
@@ -49,6 +65,8 @@ in {
 
     boot.loader.grub.enable = mkDefault false;
 
+    boot.loader.supportsInitrdSecrets = true;
+
     system = {
       build.installBootLoader = gummibootBuilder;
 
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 1f412fe2d8f2..6e867b674398 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -6,29 +6,38 @@ let
   luks = config.boot.initrd.luks;
 
   openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, ... }: assert name' == name; ''
-    # Wait for luksRoot to appear, e.g. if on a usb drive.
-    # XXX: copied and adapted from stage-1-init.sh - should be
-    # available as a function.
-    if ! test -e ${device}; then
-        echo -n "waiting 10 seconds for device ${device} to appear..."
-        for try in $(seq 10); do
-            sleep 1
-            if test -e ${device}; then break; fi
-            echo -n .
-        done
-        echo "ok"
-    fi
+
+    # Wait for a target (e.g. device, keyFile, header, ...) to appear.
+    wait_target() {
+        local name="$1"
+        local target="$2"
+
+        if [ ! -e $target ]; then
+            echo -n "Waiting 10 seconds for $name $target to appear"
+            local success=false;
+            for try in $(seq 10); do
+                echo -n "."
+                sleep 1
+                if [ -e $target ]; then success=true break; fi
+            done
+            if [ $success = true ]; then
+                echo " - success";
+            else
+                echo " - failure";
+            fi
+        fi
+    }
+
+    # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
+    # if on a USB drive.
+    wait_target "device" ${device}
 
     ${optionalString (keyFile != null) ''
-    if ! test -e ${keyFile}; then
-        echo -n "waiting 10 seconds for key file ${keyFile} to appear..."
-        for try in $(seq 10); do
-            sleep 1
-            if test -e ${keyFile}; then break; fi
-            echo -n .
-        done
-        echo "ok"
-    fi
+      wait_target "key file" ${keyFile}
+    ''}
+
+    ${optionalString (header != null) ''
+      wait_target "header" ${header}
     ''}
 
     open_normally() {
@@ -434,8 +443,8 @@ in
       chmod +x $out/bin/cryptsetup-askpass
 
       ${optionalString luks.yubikeySupport ''
-        copy_bin_and_libs ${pkgs.ykpers}/bin/ykchalresp
-        copy_bin_and_libs ${pkgs.ykpers}/bin/ykinfo
+        copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp
+        copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo
         copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl
 
         cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index b828ad53dc58..f96dde153610 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -79,7 +79,7 @@ let
   checkBond = checkUnitConfig "Bond" [
     (assertOnlyFields [
       "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec"
-      "UpDelaySec" "DownDelaySec"
+      "UpDelaySec" "DownDelaySec" "GratuitousARP"
     ])
     (assertValueOneOf "Mode" [
       "balance-rr" "active-backup" "balance-xor"
@@ -667,8 +667,10 @@ in
 
   config = mkIf config.systemd.network.enable {
 
-    systemd.additionalUpstreamSystemUnits =
-      [ "systemd-networkd.service" "systemd-networkd-wait-online.service" ];
+    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
       // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
diff --git a/nixos/modules/system/boot/readonly-mountpoint.c b/nixos/modules/system/boot/readonly-mountpoint.c
deleted file mode 100644
index 27b666873821..000000000000
--- a/nixos/modules/system/boot/readonly-mountpoint.c
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <sys/statvfs.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char ** argv) {
-	struct statvfs stat;
-	if (argc != 2) {
-		fprintf(stderr, "Usage: %s PATH", argv[0]);
-		exit(2);
-	}
-	if (statvfs(argv[1], &stat) != 0) {
-		perror("statvfs");
-		exit(3);
-	}
-	if (stat.f_flag & ST_RDONLY)
-		exit(0);
-	else
-		exit(1);
-}
-
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 4b7c545dcc0d..a3fb733c289d 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -71,7 +71,9 @@ in
 
   config = mkIf cfg.enable {
 
-    systemd.additionalUpstreamSystemUnits = [ "systemd-resolved.service" ];
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-resolved.service" "org.freedesktop.resolve1.busname"
+    ];
 
     systemd.services.systemd-resolved = {
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index f0699ad9832b..9a125dcb0aeb 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -8,6 +8,14 @@ export LD_LIBRARY_PATH=@extraUtils@/lib
 export PATH=@extraUtils@/bin
 ln -s @extraUtils@/bin /bin
 
+# Copy the secrets to their needed location
+if [ -d "@extraUtils@/secrets" ]; then
+    for secret in $(cd "@extraUtils@/secrets"; find . -type f); do
+        mkdir -p $(dirname "/$secret")
+        ln -s "@extraUtils@/secrets/$secret" "$secret"
+    done
+fi
+
 # Stop LVM complaining about fd3
 export LVM_SUPPRESS_FD_WARNINGS=true
 
@@ -146,6 +154,9 @@ for o in $(cat /proc/cmdline); do
             fi
             ln -s "$root" /dev/root
             ;;
+        copytoram)
+            copytoram=1
+            ;;
     esac
 done
 
@@ -466,6 +477,22 @@ while read -u 3 mountPoint; do
     # doing something with $device right now.
     udevadm settle
 
+    # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
+    if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
+      fsType=$(blkid -o value -s TYPE "$device")
+      fsSize=$(blockdev --getsize64 "$device")
+
+      mkdir -p /tmp-iso
+      mount -t "$fsType" /dev/root /tmp-iso
+      mountFS tmpfs /iso size="$fsSize" tmpfs
+
+      cp -r /tmp-iso/* /mnt-root/iso/
+
+      umount /tmp-iso
+      rmdir /tmp-iso
+      continue
+    fi
+
     mountFS "$device" "$mountPoint" "$options" "$fsType"
 done
 
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 61def24efd88..e3a3b6f88cf2 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -82,6 +82,17 @@ let
         copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
       ''}
 
+      # Copy secrets if needed.
+      ${optionalString (!config.boot.loader.supportsInitrdSecrets)
+          (concatStringsSep "\n" (mapAttrsToList (dest: source:
+             let source' = if source == null then dest else source; in
+               ''
+                  mkdir -p $(dirname "$out/secrets/${dest}")
+                  cp -a ${source'} "$out/secrets/${dest}"
+                ''
+          ) config.boot.initrd.secrets))
+       }
+
       ${config.boot.initrd.extraUtilsCommands}
 
       # Copy ld manually since it isn't detected correctly
@@ -242,6 +253,52 @@ let
       ];
   };
 
+  # Script to add secret files to the initrd at bootloader update time
+  initialRamdiskSecretAppender =
+    pkgs.writeScriptBin "append-initrd-secrets"
+      ''
+        #!${pkgs.bash}/bin/bash -e
+        function usage {
+          echo "USAGE: $0 INITRD_FILE" >&2
+          echo "Appends this configuration's secrets to INITRD_FILE" >&2
+        }
+
+        if [ $# -ne 1 ]; then
+          usage
+          exit 1
+        fi
+
+        if [ "$1"x = "--helpx" ]; then
+          usage
+          exit 0
+        fi
+
+        ${lib.optionalString (config.boot.initrd.secrets == {})
+            "exit 0"}
+
+        export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
+
+        function cleanup {
+          if [ -n "$tmp" -a -d "$tmp" ]; then
+            rm -fR "$tmp"
+          fi
+        }
+        trap cleanup EXIT
+
+        tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX)
+
+        ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
+            let source' = if source == null then dest else toString source; in
+              ''
+                mkdir -p $(dirname "$tmp/${dest}")
+                cp -a ${source'} "$tmp/${dest}"
+              ''
+          ) config.boot.initrd.secrets)
+         }
+
+        (cd "$tmp" && find . | cpio -H newc -o) | gzip >>"$1"
+      '';
+
 in
 
 {
@@ -370,6 +427,25 @@ in
       example = "xz";
     };
 
+    boot.initrd.secrets = mkOption
+      { internal = true;
+        default = {};
+        type = types.attrsOf (types.nullOr types.path);
+        description =
+          ''
+            Secrets to append to the initrd. The attribute name is the
+            path the secret should have inside the initrd, the value
+            is the path it should be copied from (or null for the same
+            path inside and out).
+          '';
+        example = literalExample
+          ''
+            { "/etc/dropbear/dropbear_rsa_host_key" =
+                ./secret-dropbear-key;
+            }
+          '';
+      };
+
     boot.initrd.supportedFilesystems = mkOption {
       default = [ ];
       example = [ "btrfs" ];
@@ -377,6 +453,18 @@ in
       description = "Names of supported filesystem types in the initial ramdisk.";
     };
 
+    boot.loader.supportsInitrdSecrets = mkOption
+      { internal = true;
+        default = false;
+        type = types.bool;
+        description =
+          ''
+            Whether the bootloader setup runs append-initrd-secrets.
+            If not, any needed secrets must be copied into the initrd
+            and thus added to the store.
+          '';
+      };
+
     fileSystems = mkOption {
       options.neededForBoot = mkOption {
         default = false;
@@ -404,9 +492,8 @@ in
       }
     ];
 
-    system.build.bootStage1 = bootStage1;
-    system.build.initialRamdisk = initialRamdisk;
-    system.build.extraUtils = extraUtils;
+    system.build =
+      { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
 
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isYes "TMPFS")
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index f827e530f877..46aed44bf10f 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -2,7 +2,22 @@
 
 systemConfig=@systemConfig@
 
-export HOME=/root
+export HOME=/root PATH="@path@"
+
+
+# Process the kernel command line.
+for o in $(</proc/cmdline); do
+    case $o in
+        boot.debugtrace)
+            # Show each command.
+            set -x
+            ;;
+        resume=*)
+            set -- $(IFS==; echo $o)
+            resumeDevice=$2
+            ;;
+    esac
+done
 
 
 # Print a greeting.
@@ -11,21 +26,6 @@ echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m"
 echo
 
 
-# Set the PATH.
-setPath() {
-    local dirs="$1"
-    export PATH=/empty
-    for i in $dirs; do
-        PATH=$PATH:$i/bin
-        if test -e $i/sbin; then
-            PATH=$PATH:$i/sbin
-        fi
-    done
-}
-
-setPath "@path@"
-
-
 # Normally, stage 1 mounts the root filesystem read/writable.
 # However, in some environments, stage 2 is executed directly, and the
 # root is read-only.  So make it writable here.
@@ -61,7 +61,9 @@ echo "booting system configuration $systemConfig" > /dev/kmsg
 chown -f 0:30000 /nix/store
 chmod -f 1775 /nix/store
 if [ -n "@readOnlyStore@" ]; then
-    if ! readonly-mountpoint /nix/store; then
+    if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then
+        # FIXME when linux < 4.5 is EOL, switch to atomic bind mounts
+        #mount /nix/store /nix/store -o bind,remount,ro
         mount --bind /nix/store /nix/store
         mount -o remount,ro,bind /nix/store
     fi
@@ -75,31 +77,12 @@ rm -f /etc/mtab* # not that we care about stale locks
 ln -s /proc/mounts /etc/mtab
 
 
-# Process the kernel command line.
-for o in $(cat /proc/cmdline); do
-    case $o in
-        boot.debugtrace)
-            # Show each command.
-            set -x
-            ;;
-        resume=*)
-            set -- $(IFS==; echo $o)
-            resumeDevice=$2
-            ;;
-    esac
-done
-
-
 # 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 /var/log /var/lib /var/db
-mkdir -m 0755 -p /nix/var
-mkdir -m 0700 -p /root
-chmod 0700 /root
-mkdir -m 0755 -p /bin # for the /bin/sh symlink
-mkdir -m 0755 -p /home
-mkdir -m 0755 -p /etc/nixos
+mkdir -m 0755 -p /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.
@@ -111,9 +94,6 @@ rm -f /etc/{group,passwd,shadow}.lock
 rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
 
 
-mkdir -m 0755 -p /run/lock
-
-
 # For backwards compatibility, symlink /var/run to /run, and /var/lock
 # to /run/lock.
 ln -s /run /var/run
@@ -127,8 +107,8 @@ fi
 
 
 # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
-if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then
-    cat /etc/resolv.conf | resolvconf -m 1000 -a host
+if [ -n "@useHostResolvConf@" ] && [ -e /etc/resolv.conf ]; then
+    resolvconf -m 1000 -a host </etc/resolv.conf
 fi
 
 # Log the script output to /dev/kmsg or /run/log/stage-2-init.log.
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index 7e4ec2a4a670..8db6d2d2f734 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -7,15 +7,6 @@ let
   kernel = config.boot.kernelPackages.kernel;
   activateConfiguration = config.system.activationScripts.script;
 
-  readonlyMountpoint = pkgs.stdenv.mkDerivation {
-    name = "readonly-mountpoint";
-    unpackPhase = "true";
-    installPhase = ''
-      mkdir -p $out/bin
-      cc -O3 ${./readonly-mountpoint.c} -o $out/bin/readonly-mountpoint
-    '';
-  };
-
   bootStage2 = pkgs.substituteAll {
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
@@ -23,11 +14,11 @@ let
     inherit (config.nix) readOnlyStore;
     inherit (config.networking) useHostResolvConf;
     inherit (config.system.build) earlyMountScript;
-    path =
-      [ pkgs.coreutils
-        pkgs.utillinux
-        pkgs.openresolv
-      ] ++ optional config.nix.readOnlyStore readonlyMountpoint;
+    path = lib.makeBinPath [
+      pkgs.coreutils
+      pkgs.utillinux
+      pkgs.openresolv
+    ];
     postBootCommands = pkgs.writeText "local-cmds"
       ''
         ${config.boot.postBootCommands}
diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix
index 997770b8beca..7c01f8ea9b7f 100644
--- a/nixos/modules/system/boot/systemd-lib.nix
+++ b/nixos/modules/system/boot/systemd-lib.nix
@@ -10,7 +10,7 @@ rec {
 
   makeUnit = name: unit:
     let
-      pathSafeName = lib.replaceChars ["@" ":" "\\"] ["-" "-" "-"] name;
+      pathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""] name;
     in
     if unit.enable then
       pkgs.runCommand "unit-${pathSafeName}"
@@ -159,7 +159,13 @@ rec {
         fi
       done
 
-      # Created .wants and .requires symlinks from the wantedBy and
+      # Create service aliases from aliases option.
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            ln -sfn '${name}' $out/'${name2}'
+          '') unit.aliases) units)}
+
+      # Create .wants and .requires symlinks from the wantedBy and
       # requiredBy options.
       ${concatStrings (mapAttrsToList (name: unit:
           concatMapStrings (name2: ''
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 69af23981485..9be10a8283ed 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -52,6 +52,12 @@ in rec {
       description = "Units that want (i.e. depend on) this unit.";
     };
 
+    aliases = mkOption {
+      default = [];
+      type = types.listOf types.str;
+      description = "Aliases of that unit.";
+    };
+
   };
 
   concreteUnitOptions = sharedOptions // {
@@ -322,7 +328,7 @@ in rec {
         Automatically start this unit at the given date/time, which
         must be in the format described in
         <citerefentry><refentrytitle>systemd.time</refentrytitle>
-        <manvolnum>5</manvolnum></citerefentry>.  This is equivalent
+        <manvolnum>7</manvolnum></citerefentry>.  This is equivalent
         to adding a corresponding timer unit with
         <option>OnCalendar</option> set to the value given here.
       '';
@@ -369,9 +375,9 @@ in rec {
         Each attribute in this set specifies an option in the
         <literal>[Timer]</literal> section of the unit.  See
         <citerefentry><refentrytitle>systemd.timer</refentrytitle>
-        <manvolnum>5</manvolnum></citerefentry> and
+        <manvolnum>7</manvolnum></citerefentry> and
         <citerefentry><refentrytitle>systemd.time</refentrytitle>
-        <manvolnum>5</manvolnum></citerefentry> for details.
+        <manvolnum>7</manvolnum></citerefentry> for details.
       '';
     };
 
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index a2ee51669715..f798862513cb 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -17,6 +17,7 @@ let
       "busnames.target"
       "sysinit.target"
       "sockets.target"
+      "exit.target"
       "graphical.target"
       "multi-user.target"
       "network.target"
@@ -41,11 +42,14 @@ let
       "systemd-udevd.service"
       "systemd-udev-settle.service"
       "systemd-udev-trigger.service"
+      # hwdb.bin is managed by NixOS
+      # "systemd-hwdb-update.service"
 
       # Consoles.
       "getty.target"
       "getty@.service"
       "serial-getty@.service"
+      "console-getty.service"
       "container-getty@.service"
       "systemd-vconsole-setup.service"
 
@@ -58,7 +62,6 @@ let
       # Login stuff.
       "systemd-logind.service"
       "autovt@.service"
-      #"systemd-vconsole-setup.service"
       "systemd-user-sessions.service"
       "dbus-org.freedesktop.login1.service"
       "dbus-org.freedesktop.machine1.service"
@@ -72,6 +75,7 @@ let
       "systemd-journal-flush.service"
       "systemd-journal-gatewayd.socket"
       "systemd-journal-gatewayd.service"
+      "systemd-journal-catalog-update.service"
       "systemd-journald-audit.socket"
       "systemd-journald-dev-log.socket"
       "syslog.socket"
@@ -104,6 +108,7 @@ let
       "systemd-random-seed.service"
       "systemd-backlight@.service"
       "systemd-rfkill.service"
+      "systemd-rfkill.socket"
 
       # Hibernate / suspend.
       "hibernate.target"
@@ -111,8 +116,8 @@ let
       "sleep.target"
       "hybrid-sleep.target"
       "systemd-hibernate.service"
-      "systemd-suspend.service"
       "systemd-hybrid-sleep.service"
+      "systemd-suspend.service"
 
       # Reboot stuff.
       "reboot.target"
@@ -136,10 +141,10 @@ let
 
       # Slices / containers.
       "slices.target"
-      "-.slice"
       "system.slice"
       "user.slice"
       "machine.slice"
+      "machines.target"
       "systemd-machined.service"
       "systemd-nspawn@.service"
 
@@ -162,12 +167,12 @@ let
       "systemd-localed.service"
       "systemd-hostnamed.service"
       "systemd-binfmt.service"
+      "systemd-exit.service"
     ]
     ++ cfg.additionalUpstreamSystemUnits;
 
   upstreamSystemWants =
-    [ #"basic.target.wants"
-      "sysinit.target.wants"
+    [ "sysinit.target.wants"
       "sockets.target.wants"
       "local-fs.target.wants"
       "multi-user.target.wants"
@@ -176,11 +181,18 @@ let
 
   upstreamUserUnits =
     [ "basic.target"
+      "bluetooth.target"
+      "busnames.target"
       "default.target"
       "exit.target"
+      "graphical-session-pre.target"
+      "graphical-session.target"
       "paths.target"
+      "printer.target"
       "shutdown.target"
+      "smartcard.target"
       "sockets.target"
+      "sound.target"
       "systemd-exit.service"
       "timers.target"
     ];
@@ -301,7 +313,7 @@ let
     '';
 
   targetToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text =
         ''
           [Unit]
@@ -310,14 +322,14 @@ let
     };
 
   serviceToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Service]
           ${let env = cfg.globalEnvironment // def.environment;
             in concatMapStrings (n:
               let s = optionalString (env."${n}" != null)
-                "Environment=\"${n}=${env.${n}}\"\n";
+                "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
               in if stringLength s >= 2048 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
           ${if def.reloadIfChanged then ''
             X-ReloadIfChanged=true
@@ -330,7 +342,7 @@ let
     };
 
   socketToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Socket]
@@ -340,7 +352,7 @@ let
     };
 
   timerToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Timer]
@@ -349,7 +361,7 @@ let
     };
 
   pathToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Path]
@@ -358,7 +370,7 @@ let
     };
 
   mountToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Mount]
@@ -367,7 +379,7 @@ let
     };
 
   automountToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Automount]
@@ -376,7 +388,7 @@ let
     };
 
   sliceToUnit = name: def:
-    { inherit (def) wantedBy requiredBy enable;
+    { inherit (def) aliases wantedBy requiredBy enable;
       text = commonUnitText def +
         ''
           [Slice]
@@ -741,7 +753,8 @@ in
 
         # Keep a persistent journal. Note that systemd-tmpfiles will
         # set proper ownership/permissions.
-        mkdir -m 0700 -p /var/log/journal
+        # FIXME: revert to 0700 with systemd v233.
+        mkdir -m 0750 -p /var/log/journal
       '';
 
     users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
@@ -816,7 +829,8 @@ in
 
     # Some overrides to upstream units.
     systemd.services."systemd-backlight@".restartIfChanged = false;
-    systemd.services."systemd-rfkill@".restartIfChanged = false;
+    systemd.services."systemd-fsck@".restartIfChanged = false;
+    systemd.services."systemd-fsck@".path = [ config.system.path ];
     systemd.services."user@".restartIfChanged = false;
     systemd.services.systemd-journal-flush.restartIfChanged = false;
     systemd.services.systemd-random-seed.restartIfChanged = false;
@@ -829,6 +843,7 @@ in
     systemd.services.systemd-journald.stopIfChanged = false;
     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" ];
 
     # Don't bother with certain units in containers.
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 49ba66ad50af..3951d617f6f1 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -5,6 +5,11 @@ with utils;
 
 let
 
+  addCheckDesc = desc: elemType: check: types.addCheck elemType check
+    // { description = "${elemType.description} (with check: ${desc})"; };
+  nonEmptyStr = addCheckDesc "non-empty" types.str
+    (x: x != "" && ! (all (c: c == " " || c == "\t") (stringToCharacters x)));
+
   fileSystems' = toposort fsBefore (attrValues config.fileSystems);
 
   fileSystems = if fileSystems' ? "result"
@@ -26,21 +31,21 @@ let
 
       mountPoint = mkOption {
         example = "/mnt/usb";
-        type = types.str;
+        type = nonEmptyStr;
         description = "Location of the mounted the file system.";
       };
 
       device = mkOption {
         default = null;
         example = "/dev/sda";
-        type = types.nullOr types.str;
+        type = types.nullOr nonEmptyStr;
         description = "Location of the device.";
       };
 
       fsType = mkOption {
         default = "auto";
         example = "ext3";
-        type = types.str;
+        type = nonEmptyStr;
         description = "Type of the file system.";
       };
 
@@ -48,7 +53,7 @@ let
         default = [ "defaults" ];
         example = [ "data=journal" ];
         description = "Options used to mount the file system.";
-        type = types.listOf types.str;
+        type = types.listOf nonEmptyStr;
       };
 
     };
@@ -67,7 +72,7 @@ let
       label = mkOption {
         default = null;
         example = "root-partition";
-        type = types.nullOr types.str;
+        type = types.nullOr nonEmptyStr;
         description = "Label of the device (if any).";
       };
 
@@ -216,7 +221,7 @@ in
 
     environment.etc.fstab.text =
       let
-        fsToSkipCheck = [ "none" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" ];
+        fsToSkipCheck = [ "none" "bindfs" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" "glusterfs" ];
         skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck;
       in ''
         # This is a generated file.  Do not edit!
@@ -286,7 +291,7 @@ in
     # Sync mount options with systemd's src/core/mount-setup.c: mount_table.
     boot.specialFileSystems = {
       "/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; };
-      "/run" = { fsType = "tmpfs"; options = [ "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
+      "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
       "/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; };
       "/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; };
       "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "gid=${toString config.ids.gids.tty}" ]; };
diff --git a/nixos/modules/tasks/filesystems/f2fs.nix b/nixos/modules/tasks/filesystems/f2fs.nix
index 430ac630a885..d103ff1a57b5 100644
--- a/nixos/modules/tasks/filesystems/f2fs.nix
+++ b/nixos/modules/tasks/filesystems/f2fs.nix
@@ -10,7 +10,7 @@ in
 
     system.fsPackages = [ pkgs.f2fs-tools ];
 
-    boot.initrd.availableKernelModules = mkIf inInitrd [ "f2fs" ];
+    boot.initrd.availableKernelModules = mkIf inInitrd [ "f2fs" "crc32" ];
 
     boot.initrd.extraUtilsCommands = mkIf inInitrd ''
       copy_bin_and_libs ${pkgs.f2fs-tools}/sbin/fsck.f2fs
diff --git a/nixos/modules/tasks/filesystems/glusterfs.nix b/nixos/modules/tasks/filesystems/glusterfs.nix
new file mode 100644
index 000000000000..e8c7fa8efbae
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/glusterfs.nix
@@ -0,0 +1,11 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  config = mkIf (any (fs: fs == "glusterfs") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.glusterfs ];
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index e9a7ccc721a9..73cf18384bd4 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -24,6 +24,8 @@ let
     Method = nsswitch
   '';
 
+  nfsConfFile = pkgs.writeText "nfs.conf" cfg.extraConfig;
+
   cfg = config.services.nfs;
 
 in
@@ -32,23 +34,12 @@ in
   ###### interface
 
   options = {
-
     services.nfs = {
-      statdPort = mkOption {
-        default = null;
-        example = 4000;
-        description = ''
-          Use a fixed port for <command>rpc.statd</command>. This is
-          useful if the NFS server is behind a firewall.
-        '';
-      };
-      lockdPort = mkOption {
-        default = null;
-        example = 4001;
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
         description = ''
-          Use a fixed port for the NFS lock manager kernel module
-          (<literal>lockd/nlockmgr</literal>).  This is useful if the
-          NFS server is behind a firewall.
+          Extra nfs-utils configuration.
         '';
       };
     };
@@ -62,69 +53,49 @@ in
 
     system.fsPackages = [ pkgs.nfs-utils ];
 
-    boot.extraModprobeConfig = mkIf (cfg.lockdPort != null) ''
-      options lockd nlm_udpport=${toString cfg.lockdPort} nlm_tcpport=${toString cfg.lockdPort}
-    '';
-
-    boot.kernelModules = [ "sunrpc" ];
-
     boot.initrd.kernelModules = mkIf inInitrd [ "nfs" ];
 
-    # FIXME: should use upstream units from nfs-utils.
-
-    systemd.services.statd =
-      { description = "NFSv3 Network Status Monitor";
+    systemd.packages = [ pkgs.nfs-utils ];
+    systemd.generator-packages = [ pkgs.nfs-utils ];
 
-        path = [ pkgs.nfs-utils pkgs.sysvtools pkgs.utillinux ];
+    environment.etc = {
+      "idmapd.conf".source = idmapdConfFile;
+      "nfs.conf".source = nfsConfFile;
+    };
 
-        wants = [ "remote-fs-pre.target" ];
-        before = [ "remote-fs-pre.target" ];
-        wantedBy = [ "remote-fs.target" ];
-        requires = [ "basic.target" "rpcbind.service" ];
-        after = [ "basic.target" "rpcbind.service" ];
+    systemd.services.nfs-blkmap =
+      { restartTriggers = [ nfsConfFile ];
+      };
 
-        unitConfig.DefaultDependencies = false; # don't stop during shutdown
+    systemd.targets.nfs-client =
+      { wantedBy = [ "multi-user.target" "remote-fs.target" ];
+      };
 
-        preStart =
-          ''
-            mkdir -p ${nfsStateDir}/sm
-            mkdir -p ${nfsStateDir}/sm.bak
-            sm-notify -d
-          '';
+    systemd.services.nfs-idmapd =
+      { restartTriggers = [ idmapdConfFile ];
+      };
 
-        serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = ''
-          @${pkgs.nfs-utils}/sbin/rpc.statd rpc.statd --no-notify \
-              ${if cfg.statdPort != null then "-p ${toString cfg.statdPort}" else ""}
-        '';
-        serviceConfig.Restart = "always";
+    systemd.services.nfs-mountd =
+      { restartTriggers = [ nfsConfFile ];
+        enable = mkDefault false;
       };
 
-    systemd.services.idmapd =
-      { description = "NFSv4 ID Mapping Daemon";
+    systemd.services.nfs-server =
+      { restartTriggers = [ nfsConfFile ];
+        enable = mkDefault false;
+      };
 
-        path = [ pkgs.sysvtools pkgs.utillinux ];
+    systemd.services.rpc-gssd =
+      { restartTriggers = [ nfsConfFile ];
+      };
 
-        wants = [ "remote-fs-pre.target" ];
-        before = [ "remote-fs-pre.target" ];
-        wantedBy = [ "remote-fs.target" ];
-        requires = [ "rpcbind.service" ];
-        after = [ "rpcbind.service" ];
+    systemd.services.rpc-statd =
+      { restartTriggers = [ nfsConfFile ];
 
         preStart =
           ''
-            mkdir -p ${rpcMountpoint}
-            mount -t rpc_pipefs rpc_pipefs ${rpcMountpoint}
+            mkdir -p /var/lib/nfs/{sm,sm.bak}
           '';
-
-        postStop =
-          ''
-            umount ${rpcMountpoint}
-          '';
-
-        serviceConfig.Type = "forking";
-        serviceConfig.ExecStart = "@${pkgs.nfs-utils}/sbin/rpc.idmapd rpc.idmapd -c ${idmapdConfFile}";
-        serviceConfig.Restart = "always";
       };
 
   };
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 045cbeb7cff8..518fe0a97d27 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -13,12 +13,14 @@ let
   cfgZfs = config.boot.zfs;
   cfgSnapshots = config.services.zfs.autoSnapshot;
   cfgSnapFlags = cfgSnapshots.flags;
+  cfgScrub = config.services.zfs.autoScrub;
 
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
 
   enableAutoSnapshots = cfgSnapshots.enable;
-  enableZfs = inInitrd || inSystem || enableAutoSnapshots;
+  enableAutoScrub = cfgScrub.enable;
+  enableZfs = inInitrd || inSystem || enableAutoSnapshots || enableAutoScrub;
 
   kernel = config.boot.kernelPackages;
 
@@ -107,7 +109,6 @@ in
       forceImportRoot = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = ''
           Forcibly import the ZFS root pool(s) during early boot.
 
@@ -126,7 +127,6 @@ in
       forceImportAll = mkOption {
         type = types.bool;
         default = true;
-        example = false;
         description = ''
           Forcibly import all ZFS pool(s).
 
@@ -217,6 +217,37 @@ in
         '';
       };
     };
+
+    services.zfs.autoScrub = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enables periodic scrubbing of ZFS pools.
+        '';
+      };
+
+      interval = mkOption {
+        default = "Sun, 02:00";
+        type = types.str;
+        example = "daily";
+        description = ''
+          Systemd calendar expression when to scrub ZFS pools. See
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>.
+        '';
+      };
+
+      pools = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = [ "tank" ];
+        description = ''
+          List of ZFS pools to periodically scrub. If empty, all pools
+          will be scrubbed.
+        '';
+      };
+    };
   };
 
   ###### implementation
@@ -282,7 +313,7 @@ in
         zfsSupport = true;
       };
 
-      environment.etc."zfs/zed.d".source = "${packages.zfsUser}/etc/zfs/zed.d/*";
+      environment.etc."zfs/zed.d".source = "${packages.zfsUser}/etc/zfs/zed.d/";
 
       system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck
       environment.systemPackages = [ packages.zfsUser ]
@@ -391,5 +422,31 @@ in
                               };
                             }) snapshotNames);
     })
+
+    (mkIf enableAutoScrub {
+      systemd.services.zfs-scrub = {
+        description = "ZFS pools scrubbing";
+        after = [ "zfs-import.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+        };
+        script = ''
+          ${packages.zfsUser}/bin/zpool scrub ${
+            if cfgScrub.pools != [] then
+              (concatStringsSep " " cfgScrub.pools)
+            else
+              "$(${packages.zfsUser}/bin/zpool list -H -o name)"
+            }
+        '';
+      };
+
+      systemd.timers.zfs-scrub = {
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          OnCalendar = cfgScrub.interval;
+          Persistent = "yes";
+        };
+      };
+    })
   ];
 }
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index e001832ec2eb..3975dd5b0ffd 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -71,7 +71,7 @@ in
   ###### implementation
 
   config = mkMerge [
-    (mkIf (!setVconsole || (setVconsole && config.boot.earlyVconsoleSetup)) {
+    (mkIf (!setVconsole) {
       systemd.services."systemd-vconsole-setup".enable = false;
     })
 
@@ -97,20 +97,25 @@ in
             printf "${makeColorCS n color}" >> /dev/console
           '') config.i18n.consoleColors}
         '';
-      }
 
-      (mkIf (!config.boot.earlyVconsoleSetup) {
-        # This is identical to the systemd-vconsole-setup.service unit
-        # shipped with systemd, except that it uses /dev/tty1 instead of
-        # /dev/tty0 to prevent putting the X server in non-raw mode, and
-        # it has a restart trigger.
+        /* XXX: systemd-vconsole-setup needs a "main" terminal. By default
+         * /dev/tty0 is used which wouldn't work when the service is restarted
+         * from X11. We set this to /dev/tty1; not ideal because it may also be
+         * owned by X11 or something else.
+         *
+         * See #22470.
+         */
         systemd.services."systemd-vconsole-setup" =
           { wantedBy = [ "sysinit.target" ];
             before = [ "display-manager.service" ];
             after = [ "systemd-udev-settle.service" ];
             restartTriggers = [ vconsoleConf kbdEnv ];
+            serviceConfig.ExecStart = [
+              ""
+              "${pkgs.systemd}/lib/systemd/systemd-vconsole-setup /dev/tty1"
+            ];
           };
-      })
+      }
 
       (mkIf config.boot.earlyVconsoleSetup {
         boot.initrd.extraUtilsCommands = ''
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index c50ea5c79643..f6f104ce7a68 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -37,31 +37,41 @@ let
     ip link del "${i}" 2>/dev/null || true
   '';
 
-in
+  # warn that these attributes are deprecated (2017-2-2)
+  # Should be removed in the release after next
+  bondDeprecation = rec {
+    deprecated = [ "lacp_rate" "miimon" "mode" "xmit_hash_policy" ];
+    filterDeprecated = bond: (filterAttrs (attrName: attr:
+                         elem attrName deprecated && attr != null) bond);
+  };
 
-{
+  bondWarnings =
+    let oneBondWarnings = bondName: bond:
+          mapAttrsToList (bondText bondName) (bondDeprecation.filterDeprecated bond);
+        bondText = bondName: optName: _:
+          "${bondName}.${optName} is deprecated, use ${bondName}.driverOptions";
+    in {
+      warnings = flatten (mapAttrsToList oneBondWarnings cfg.bonds);
+    };
 
-  config = mkIf (!cfg.useNetworkd) {
+  normalConfig = {
 
     systemd.services =
       let
 
         deviceDependency = dev:
-          if (config.boot.isContainer == false)
-          then
-            # Trust udev when not in the container
-            optional (dev != null) (subsystemDevice dev)
-          else
-            # When in the container, check whether the interface is built from other definitions
-            if (hasAttr dev cfg.bridges) ||
-               (hasAttr dev cfg.bonds) ||
-               (hasAttr dev cfg.macvlans) ||
-               (hasAttr dev cfg.sits) ||
-               (hasAttr dev cfg.vlans) ||
-               (hasAttr dev cfg.vswitches) ||
-               (hasAttr dev cfg.wlanInterfaces)
-            then [ "${dev}-netdev.service" ]
-            else [];
+          # Use systemd service if we manage device creation, else
+          # trust udev when not in a container
+          if (hasAttr dev (filterAttrs (k: v: v.virtual) cfg.interfaces)) ||
+             (hasAttr dev cfg.bridges) ||
+             (hasAttr dev cfg.bonds) ||
+             (hasAttr dev cfg.macvlans) ||
+             (hasAttr dev cfg.sits) ||
+             (hasAttr dev cfg.vlans) ||
+             (hasAttr dev cfg.vswitches) ||
+             (hasAttr dev cfg.wlanInterfaces)
+          then [ "${dev}-netdev.service" ]
+          else optional (dev != null && !config.boot.isContainer) (subsystemDevice dev);
 
         networkLocalCommands = {
           after = [ "network-setup.service" ];
@@ -102,17 +112,25 @@ in
                 EOF
 
                 # Set the default gateway.
-                ${optionalString (cfg.defaultGateway != null && cfg.defaultGateway != "") ''
+                ${optionalString (cfg.defaultGateway != null && cfg.defaultGateway.address != "") ''
                   # FIXME: get rid of "|| true" (necessary to make it idempotent).
-                  ip route add default via "${cfg.defaultGateway}" ${
+                  ip route add default ${optionalString (cfg.defaultGateway.metric != null)
+                      "metric ${toString cfg.defaultGateway.metric}"
+                    } via "${cfg.defaultGateway.address}" ${
                     optionalString (cfg.defaultGatewayWindowSize != null)
-                      "window ${toString cfg.defaultGatewayWindowSize}"} || true
+                      "window ${toString cfg.defaultGatewayWindowSize}"} ${
+                    optionalString (cfg.defaultGateway.interface != null)
+                      "dev ${cfg.defaultGateway.interface}"} proto static || true
                 ''}
-                ${optionalString (cfg.defaultGateway6 != null && cfg.defaultGateway6 != "") ''
+                ${optionalString (cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "") ''
                   # FIXME: get rid of "|| true" (necessary to make it idempotent).
-                  ip -6 route add ::/0 via "${cfg.defaultGateway6}" ${
+                  ip -6 route add ::/0 ${optionalString (cfg.defaultGateway6.metric != null)
+                      "metric ${toString cfg.defaultGateway6.metric}"
+                    } via "${cfg.defaultGateway6.address}" ${
                     optionalString (cfg.defaultGatewayWindowSize != null)
-                      "window ${toString cfg.defaultGatewayWindowSize}"} || true
+                      "window ${toString cfg.defaultGatewayWindowSize}"} ${
+                    optionalString (cfg.defaultGateway6.interface != null)
+                      "dev ${cfg.defaultGateway6.interface}"} proto static || true
                 ''}
               '';
           };
@@ -141,35 +159,42 @@ in
             after = [ "network-pre.target" ] ++ (deviceDependency i.name);
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
+            # Restart rather than stop+start this unit to prevent the
+            # network from dying during switch-to-configuration.
+            stopIfChanged = false;
             path = [ pkgs.iproute ];
             script =
               ''
+                # FIXME: shouldn't this be done in network-link?
                 echo "bringing up interface..."
                 ip link set "${i.name}" up
 
-                restart_network_interfaces=false
+                state="/run/nixos/network/addresses/${i.name}"
+
+                mkdir -p $(dirname "$state")
+
               '' + flip concatMapStrings (ips) (ip:
                 let
                   address = "${ip.address}/${toString ip.prefixLength}";
                 in
                 ''
-                  echo "checking ip ${address}..."
+                  echo "${address}" >> $state
                   if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
-                    echo "added ip ${address}..."
+                    echo "added ip ${address}"
                   elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
                     echo "failed to add ${address}"
                     exit 1
                   fi
                 '');
-            preStop = flip concatMapStrings (ips) (ip:
-                let
-                  address = "${ip.address}/${toString ip.prefixLength}";
-                in
-                ''
-                  echo -n "deleting ${address}..."
-                  ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
-                  echo ""
-                '');
+            preStop = ''
+              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 ""
+              done < "$state"
+              rm -f "$state"
+            '';
           };
 
         createTunDevice = i: nameValuePair "${i.name}-netdev"
@@ -190,7 +215,7 @@ in
               user "${i.virtualOwner}"
             '';
             postStop = ''
-              ip link del ${i.name}
+              ip link del ${i.name} || true
             '';
           };
 
@@ -202,7 +227,7 @@ in
             wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps ++ optional v.rstp "mstpd.service";
             partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service";
-            after = [ "network-pre.target" "mstpd.service" ] ++ deps
+            after = [ "network-pre.target" ] ++ deps ++ optional v.rstp "mstpd.service"
               ++ concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) v.interfaces;
             before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
@@ -221,6 +246,10 @@ in
                 ip link set "${i}" master "${n}"
                 ip link set "${i}" up
               '')}
+              # Save list of enslaved interfaces
+              echo "${flip concatMapStrings v.interfaces (i: ''
+                ${i}
+              '')}" > /run/${n}.interfaces
 
               # Enable stp on the interface
               ${optionalString v.rstp ''
@@ -232,7 +261,28 @@ in
             postStop = ''
               ip link set "${n}" down || true
               ip link del "${n}" || true
+              rm -f /run/${n}.interfaces
             '';
+            reload = ''
+              # Un-enslave child interfaces (old list of interfaces)
+              for interface in `cat /run/${n}.interfaces`; do
+                ip link set "$interface" nomaster up
+              done
+
+              # Enslave child interfaces (new list of interfaces)
+              ${flip concatMapStrings v.interfaces (i: ''
+                ip link set "${i}" master "${n}"
+                ip link set "${i}" up
+              '')}
+              # Save list of enslaved interfaces
+              echo "${flip concatMapStrings v.interfaces (i: ''
+                ${i}
+              '')}" > /run/${n}.interfaces
+
+              # (Un-)set stp on the bridge
+              echo ${if v.rstp then "2" else "0"} > /sys/class/net/${n}/bridge/stp_state
+            '';
+            reloadIfChanged = true;
           });
 
         createVswitchDevice = n: v: nameValuePair "${n}-netdev"
@@ -288,10 +338,11 @@ in
 
               echo "Creating new bond ${n}..."
               ip link add name "${n}" type bond \
-                ${optionalString (v.mode != null) "mode ${toString v.mode}"} \
-                ${optionalString (v.miimon != null) "miimon ${toString v.miimon}"} \
-                ${optionalString (v.xmit_hash_policy != null) "xmit_hash_policy ${toString v.xmit_hash_policy}"} \
-                ${optionalString (v.lacp_rate != null) "lacp_rate ${toString v.lacp_rate}"}
+              ${let opts = (mapAttrs (const toString)
+                             (bondDeprecation.filterDeprecated v))
+                           // v.driverOptions;
+                 in concatStringsSep "\n"
+                      (mapAttrsToList (set: val: "  ${set} ${val} \\") opts)}
 
               # !!! There must be a better way to wait for the interface
               while [ ! -d "/sys/class/net/${n}" ]; do sleep 0.1; done;
@@ -327,7 +378,7 @@ in
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
@@ -355,7 +406,7 @@ in
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
@@ -379,7 +430,7 @@ in
               ip link set "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}"
+              ip link delete "${n}" || true
             '';
           });
 
@@ -402,6 +453,14 @@ in
         KERNEL=="tun", TAG+="systemd"
       '';
 
+
   };
 
+in
+
+{
+  config = mkMerge [
+    bondWarnings
+    (mkIf (!cfg.useNetworkd) normalConfig)
+  ];
 }
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 974041d7e1a5..a365a01bfb1e 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -38,6 +38,12 @@ in
     } {
       assertion = cfg.vswitches == {};
       message = "networking.vswichtes are not supported by networkd.";
+    } {
+      assertion = cfg.defaultGateway == null || cfg.defaultGateway.interface == null;
+      message = "networking.defaultGateway.interface is not supported by networkd.";
+    } {
+      assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null;
+      message = "networking.defaultGateway6.interface is not supported by networkd.";
     } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
       assertion = !rstp;
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
@@ -53,15 +59,16 @@ in
     systemd.network =
       let
         domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain);
-        genericNetwork = override: {
-          DHCP = override (dhcpStr cfg.useDHCP);
-        } // optionalAttrs (cfg.defaultGateway != null) {
-          gateway = override [ cfg.defaultGateway ];
-        } // optionalAttrs (cfg.defaultGateway6 != null) {
-          gateway = override [ cfg.defaultGateway6 ];
-        } // optionalAttrs (domains != [ ]) {
-          domains = override domains;
-        };
+        genericNetwork = override:
+          let gateway = optional (cfg.defaultGateway != null) cfg.defaultGateway.address
+            ++ optional (cfg.defaultGateway6 != null) cfg.defaultGateway6.address;
+          in {
+            DHCP = override (dhcpStr cfg.useDHCP);
+          } // optionalAttrs (gateway != [ ]) {
+            gateway = override gateway;
+          } // optionalAttrs (domains != [ ]) {
+            domains = override domains;
+          };
       in mkMerge [ {
         enable = true;
         networks."99-main" = genericNetwork mkDefault;
@@ -109,17 +116,65 @@ in
             Name = name;
             Kind = "bond";
           };
-          bondConfig =
-            (optionalAttrs (bond.lacp_rate != null) {
-              LACPTransmitRate = bond.lacp_rate;
-            }) // (optionalAttrs (bond.miimon != null) {
-              MIIMonitorSec = bond.miimon;
-            }) // (optionalAttrs (bond.mode != null) {
-              Mode = bond.mode;
-            }) // (optionalAttrs (bond.xmit_hash_policy != null) {
-              TransmitHashPolicy = bond.xmit_hash_policy;
-            });
+          bondConfig = let
+            # manual mapping as of 2017-02-03
+            # man 5 systemd.netdev [BOND]
+            # to https://www.kernel.org/doc/Documentation/networking/bonding.txt
+            # driver options.
+            driverOptionMapping = let
+              trans = f: optName: { valTransform = f; optNames = [optName]; };
+              simp  = trans id;
+              ms    = trans (v: v + "ms");
+              in {
+                Mode                       = simp "mode";
+                TransmitHashPolicy         = simp "xmit_hash_policy";
+                LACPTransmitRate           = simp "lacp_rate";
+                MIIMonitorSec              = ms "miimon";
+                UpDelaySec                 = ms "updelay";
+                DownDelaySec               = ms "downdelay";
+                LearnPacketIntervalSec     = simp "lp_interval";
+                AdSelect                   = simp "ad_select";
+                FailOverMACPolicy          = simp "fail_over_mac";
+                ARPValidate                = simp "arp_validate";
+                # apparently in ms for this value?! Upstream bug?
+                ARPIntervalSec             = simp "arp_interval";
+                ARPIPTargets               = simp "arp_ip_target";
+                ARPAllTargets              = simp "arp_all_targets";
+                PrimaryReselectPolicy      = simp "primary_reselect";
+                ResendIGMP                 = simp "resend_igmp";
+                PacketsPerSlave            = simp "packets_per_slave";
+                GratuitousARP = { valTransform = id;
+                                  optNames = [ "num_grat_arp" "num_unsol_na" ]; };
+                AllSlavesActive            = simp "all_slaves_active";
+                MinLinks                   = simp "min_links";
+              };
+
+            do = bond.driverOptions;
+            assertNoUnknownOption = let
+              knownOptions = flatten (mapAttrsToList (_: kOpts: kOpts.optNames)
+                                                     driverOptionMapping);
+              # options that apparently don’t exist in the networkd config
+              unknownOptions = [ "primary" ];
+              assertTrace = bool: msg: if bool then true else builtins.trace msg false;
+              in assert all (driverOpt: assertTrace
+                               (elem driverOpt (knownOptions ++ unknownOptions))
+                               "The bond.driverOption `${driverOpt}` cannot be mapped to the list of known networkd bond options. Please add it to the mapping above the assert or to `unknownOptions` should it not exist in networkd.")
+                            (mapAttrsToList (k: _: k) do); "";
+            # get those driverOptions that have been set
+            filterSystemdOptions = filterAttrs (sysDOpt: kOpts:
+                                     any (kOpt: do ? "${kOpt}") kOpts.optNames);
+            # build final set of systemd options to bond values
+            buildOptionSet = mapAttrs (_: kOpts: with kOpts;
+                               # we simply take the first set kernel bond option
+                               # (one option has multiple names, which is silly)
+                               head (map (optN: valTransform (do."${optN}"))
+                                 # only map those that exist
+                                 (filter (o: do ? "${o}") optNames)));
+            in seq assertNoUnknownOption
+                   (buildOptionSet (filterSystemdOptions driverOptionMapping));
+
         };
+
         networks = listToAttrs (flip map bond.interfaces (bi:
           nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
             DHCP = mkOverride 0 (dhcpStr false);
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 83d9854d3517..e9a3dca6418a 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, utils, ... }:
+{ config, lib, pkgs, utils, stdenv, ... }:
 
 with lib;
 with utils;
@@ -116,6 +116,35 @@ let
       };
     };
 
+  gatewayCoerce = address: { inherit address; };
+
+  gatewayOpts = { ... }: {
+
+    options = {
+
+      address = mkOption {
+        type = types.str;
+        description = "The default gateway address.";
+      };
+
+      interface = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "enp0s3";
+        description = "The default gateway interface.";
+      };
+
+      metric = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 42;
+        description = "The default gateway metric/preference.";
+      };
+
+    };
+
+  };
+
   interfaceOpts = { name, ... }: {
 
     options = {
@@ -327,19 +356,27 @@ in
 
     networking.defaultGateway = mkOption {
       default = null;
-      example = "131.211.84.1";
-      type = types.nullOr types.str;
+      example = {
+        address = "131.211.84.1";
+        interface = "enp3s0";
+      };
+      type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
       description = ''
-        The default gateway.  It can be left empty if it is auto-detected through DHCP.
+        The default gateway. It can be left empty if it is auto-detected through DHCP.
+        It can be specified as a string or an option set along with a network interface.
       '';
     };
 
     networking.defaultGateway6 = mkOption {
       default = null;
-      example = "2001:4d0:1e04:895::1";
-      type = types.nullOr types.str;
+      example = {
+        address = "2001:4d0:1e04:895::1";
+        interface = "enp3s0";
+      };
+      type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
       description = ''
-        The default ipv6 gateway.  It can be left empty if it is auto-detected through DHCP.
+        The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP.
+        It can be specified as a string or an option set along with a network interface.
       '';
     };
 
@@ -511,7 +548,6 @@ in
           };
 
           rstp = mkOption {
-            example = true;
             default = false;
             type = types.bool;
             description = "Whether the bridge interface should enable rstp.";
@@ -523,81 +559,102 @@ in
 
     };
 
-    networking.bonds = mkOption {
-      default = { };
-      example = literalExample {
-        bond0 = {
-          interfaces = [ "eth0" "wlan0" ];
-          miimon = 100;
+    networking.bonds =
+      let
+        driverOptionsExample = {
+          miimon = "100";
           mode = "active-backup";
         };
-        fatpipe.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ];
-      };
-      description = ''
-        This option allows you to define bond devices that aggregate multiple,
-        underlying networking interfaces together. The value of this option is
-        an attribute set. Each attribute specifies a bond, with the attribute
-        name specifying the name of the bond's network interface
-      '';
-
-      type = with types; attrsOf (submodule {
-
-        options = {
-
-          interfaces = mkOption {
-            example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
-            type = types.listOf types.str;
-            description = "The interfaces to bond together";
-          };
-
-          lacp_rate = mkOption {
-            default = null;
-            example = "fast";
-            type = types.nullOr types.str;
-            description = ''
-              Option specifying the rate in which we'll ask our link partner
-              to transmit LACPDU packets in 802.3ad mode.
-            '';
-          };
-
-          miimon = mkOption {
-            default = null;
-            example = 100;
-            type = types.nullOr types.int;
-            description = ''
-              Miimon is the number of millisecond in between each round of polling
-              by the device driver for failed links. By default polling is not
-              enabled and the driver is trusted to properly detect and handle
-              failure scenarios.
-            '';
+      in mkOption {
+        default = { };
+        example = literalExample {
+          bond0 = {
+            interfaces = [ "eth0" "wlan0" ];
+            driverOptions = driverOptionsExample;
           };
+          anotherBond.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ];
+        };
+        description = ''
+          This option allows you to define bond devices that aggregate multiple,
+          underlying networking interfaces together. The value of this option is
+          an attribute set. Each attribute specifies a bond, with the attribute
+          name specifying the name of the bond's network interface
+        '';
 
-          mode = mkOption {
-            default = null;
-            example = "active-backup";
-            type = types.nullOr types.str;
-            description = ''
-              The mode which the bond will be running. The default mode for
-              the bonding driver is balance-rr, optimizing for throughput.
-              More information about valid modes can be found at
-              https://www.kernel.org/doc/Documentation/networking/bonding.txt
-            '';
-          };
+        type = with types; attrsOf (submodule {
+
+          options = {
+
+            interfaces = mkOption {
+              example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
+              type = types.listOf types.str;
+              description = "The interfaces to bond together";
+            };
+
+            driverOptions = mkOption {
+              type = types.attrsOf types.str;
+              default = {};
+              example = literalExample driverOptionsExample;
+              description = ''
+                Options for the bonding driver.
+                Documentation can be found in
+                <link xlink:href="https://www.kernel.org/doc/Documentation/networking/bonding.txt" />
+              '';
+
+            };
+
+            lacp_rate = mkOption {
+              default = null;
+              example = "fast";
+              type = types.nullOr types.str;
+              description = ''
+                DEPRECATED, use `driverOptions`.
+                Option specifying the rate in which we'll ask our link partner
+                to transmit LACPDU packets in 802.3ad mode.
+              '';
+            };
+
+            miimon = mkOption {
+              default = null;
+              example = 100;
+              type = types.nullOr types.int;
+              description = ''
+                DEPRECATED, use `driverOptions`.
+                Miimon is the number of millisecond in between each round of polling
+                by the device driver for failed links. By default polling is not
+                enabled and the driver is trusted to properly detect and handle
+                failure scenarios.
+              '';
+            };
+
+            mode = mkOption {
+              default = null;
+              example = "active-backup";
+              type = types.nullOr types.str;
+              description = ''
+                DEPRECATED, use `driverOptions`.
+                The mode which the bond will be running. The default mode for
+                the bonding driver is balance-rr, optimizing for throughput.
+                More information about valid modes can be found at
+                https://www.kernel.org/doc/Documentation/networking/bonding.txt
+              '';
+            };
+
+            xmit_hash_policy = mkOption {
+              default = null;
+              example = "layer2+3";
+              type = types.nullOr types.str;
+              description = ''
+                DEPRECATED, use `driverOptions`.
+                Selects the transmit hash policy to use for slave selection in
+                balance-xor, 802.3ad, and tlb modes.
+              '';
+            };
 
-          xmit_hash_policy = mkOption {
-            default = null;
-            example = "layer2+3";
-            type = types.nullOr types.str;
-            description = ''
-              Selects the transmit hash policy to use for slave selection in
-              balance-xor, 802.3ad, and tlb modes.
-            '';
           };
 
-        };
-
-      });
-    };
+        });
+      };
 
     networking.macvlans = mkOption {
       default = { };
@@ -896,7 +953,16 @@ in
         (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true))
       ));
 
-    security.setuidPrograms = [ "ping" "ping6" ];
+    # Capabilities won't work unless we have at-least a 4.3 Linux
+    # kernel because we need the ambient capability
+    security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then {
+      ping = {
+        source  = "${pkgs.iputils.out}/bin/ping";
+        capabilities = "cap_net_raw+p";
+      };
+    } else {
+      ping.source = "${pkgs.iputils.out}/bin/ping";
+    };
 
     # Set the host and domain names in the activation script.  Don't
     # clear it if it's not configured in the NixOS configuration,
diff --git a/nixos/modules/tasks/powertop.nix b/nixos/modules/tasks/powertop.nix
new file mode 100644
index 000000000000..6f57f5f5c25e
--- /dev/null
+++ b/nixos/modules/tasks/powertop.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.powerManagment.powertop;
+in {
+  ###### interface
+
+  options.powerManagment.powertop.enable = mkEnableOption "powertop auto tuning on startup";
+
+  ###### implementation
+
+  config = mkIf (cfg.enable) {
+    systemd.services = {
+      powertop = {
+        wantedBy = [ "multi-user.target" ];
+        description = "Powertop tunings";
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = "yes";
+          ExecStart = "${pkgs.powertop}/bin/powertop --auto-tune";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/tasks/trackpoint.nix b/nixos/modules/tasks/trackpoint.nix
index 32e69dd2bf58..1f8f2891e98c 100644
--- a/nixos/modules/tasks/trackpoint.nix
+++ b/nixos/modules/tasks/trackpoint.nix
@@ -81,7 +81,7 @@ with lib;
       services.xserver.inputClassSections =
         [''
         Identifier "Trackpoint Wheel Emulation"
-          MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"}"
+          MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "ETPS/2 Elantech TrackPoint|Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"}"
           MatchDevicePath "/dev/input/event*"
           Option "EmulateWheel" "true"
           Option "EmulateWheelButton" "2"
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 099ead3d846c..7f5b55d5cca0 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -123,15 +123,6 @@ let kernel = config.boot.kernelPackages.kernel; in
     users.extraUsers.root.initialHashedPassword = mkOverride 150 "";
 
     services.xserver.displayManager.logToJournal = true;
-
-    # Bump kdm's X server start timeout to account for heavily loaded
-    # VM host systems.
-    services.xserver.displayManager.kdm.extraConfig =
-      ''
-        [X-:*-Core]
-        ServerTimeout=240
-      '';
-
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index 80e481d79b9e..3e47710361a8 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -15,6 +15,12 @@ let cfg = config.ec2; in
 
   config = {
 
+    assertions = [
+      { assertion = cfg.hvm;
+        message = "Paravirtualized EC2 instances are no longer supported.";
+      }
+    ];
+
     virtualisation.growPartition = cfg.hvm;
 
     fileSystems."/" = {
diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix
index c9356c9b4eaa..a7362423eb46 100644
--- a/nixos/modules/virtualisation/amazon-init.nix
+++ b/nixos/modules/virtualisation/amazon-init.nix
@@ -1,20 +1,18 @@
-{ config, pkgs, modulesPath, ... }:
-
-# This attempts to pull a nix expression from this EC2 instance's user-data.
+{ config, pkgs, ... }:
 
 let
-  bootScript = pkgs.writeScript "bootscript.sh" ''
+  script = ''
     #!${pkgs.stdenv.shell} -eu
 
     echo "attempting to fetch configuration from EC2 user data..."
 
+    export HOME=/root
     export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.gnused config.system.build.nixos-rebuild]}:$PATH
     export NIX_PATH=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
 
     userData=/etc/ec2-metadata/user-data
 
     if [ -s "$userData" ]; then
-
       # If the user-data looks like it could be a nix expression,
       # copy it over. Also, look for a magic three-hash comment and set
       # that as the channel.
@@ -43,7 +41,21 @@ let
     nixos-rebuild switch
   '';
 in {
-  boot.postBootCommands = ''
-    ${bootScript} &
-  '';
+  systemd.services.amazon-init = {
+    inherit script;
+    description = "Reconfigure the system from EC2 userdata on startup";
+
+    wantedBy = [ "multi-user.target" ];
+    after = [ "multi-user.target" ];
+    requires = [ "network-online.target" ];
+ 
+    restartIfChanged = false;
+    unitConfig.X-StopOnRemoval = false;
+
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+    };
+  };
 }
+
diff --git a/nixos/modules/virtualisation/amazon-options.nix b/nixos/modules/virtualisation/amazon-options.nix
index 34a50dcab16f..349fd3adfc96 100644
--- a/nixos/modules/virtualisation/amazon-options.nix
+++ b/nixos/modules/virtualisation/amazon-options.nix
@@ -3,7 +3,7 @@
   options = {
     ec2 = {
       hvm = lib.mkOption {
-        default = false;
+        default = lib.versionAtLeast config.system.stateVersion "17.03";
         internal = true;
         description = ''
           Whether the EC2 instance is a HVM instance.
@@ -11,6 +11,4 @@
       };
     };
   };
-
-  config = {};
 }
diff --git a/nixos/modules/virtualisation/azure-image.nix b/nixos/modules/virtualisation/azure-image.nix
index 9fac543b03d5..cb756842f369 100644
--- a/nixos/modules/virtualisation/azure-image.nix
+++ b/nixos/modules/virtualisation/azure-image.nix
@@ -2,93 +2,19 @@
 
 with lib;
 let
-  diskSize = "30720";
+  diskSize = 30720;
 in
 {
-  system.build.azureImage =
-    pkgs.vmTools.runInLinuxVM (
-      pkgs.runCommand "azure-image"
-        { preVM =
-            ''
-              mkdir $out
-              diskImage=$out/$diskImageBase
-
-              cyl=$(((${diskSize}*1024*1024)/(512*63*255)))
-              size=$(($cyl*255*63*512))              
-              roundedsize=$((($size/(1024*1024)+1)*(1024*1024)))
-              ${pkgs.vmTools.qemu-220}/bin/qemu-img create -f raw $diskImage $roundedsize
-              mv closure xchg/
-            '';
-
-          postVM =
-            ''
-              mkdir -p $out
-              ${pkgs.vmTools.qemu-220}/bin/qemu-img convert -f raw -O vpc $diskImage $out/disk.vhd
-              rm $diskImage
-            '';
-          diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw";
-          buildInputs = [ pkgs.utillinux pkgs.perl ];
-          exportReferencesGraph =
-            [ "closure" config.system.build.toplevel ];
-        }
-        ''
-          # Create partition table
-          ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
-          ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 ${diskSize}M
-          ${pkgs.parted}/sbin/parted /dev/vda print
-          . /sys/class/block/vda1/uevent
-          mknod /dev/vda1 b $MAJOR $MINOR
-
-          # Create an empty filesystem and mount it.
-          ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1
-          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
-
-          mkdir /mnt
-          mount /dev/vda1 /mnt
-
-          # The initrd expects these directories to exist.
-          mkdir /mnt/dev /mnt/proc /mnt/sys
-
-          mount --bind /proc /mnt/proc
-          mount --bind /dev /mnt/dev
-          mount --bind /sys /mnt/sys
-
-          # Copy all paths in the closure to the filesystem.
-          storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
-
-          mkdir -p /mnt/nix/store
-          echo "copying everything (will take a while)..."
-          cp -prd $storePaths /mnt/nix/store/
-
-          echo Register the paths in the Nix database.
-          printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
-              chroot /mnt ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group ""
-
-          echo Create the system profile to allow nixos-rebuild to work.
-          chroot /mnt ${config.nix.package.out}/bin/nix-env \
-              -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} --option build-users-group ""
-
-          echo nixos-rebuild requires an /etc/NIXOS.
-          mkdir -p /mnt/etc
-          touch /mnt/etc/NIXOS
-
-          echo switch-to-configuration requires a /bin/sh
-          mkdir -p /mnt/bin
-          ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
-
-          echo Install a configuration.nix.
-          mkdir -p /mnt/etc/nixos /mnt/boot/grub
-          cp ${./azure-config-user.nix} /mnt/etc/nixos/configuration.nix
-
-          echo Generate the GRUB menu.
-          ln -s vda /dev/sda
-          chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
-
-          echo Almost done
-          umount /mnt/proc /mnt/dev /mnt/sys
-          umount /mnt
-        ''
-    );
+  system.build.azureImage = import ../../lib/make-disk-image.nix {
+    name = "azure-image";
+    postVM = ''
+      ${pkgs.vmTools.qemu-220}/bin/qemu-img convert -f raw -o subformat=fixed -O vpc $diskImage $out/disk.vhd
+    '';
+    configFile = ./azure-config-user.nix;
+    format = "raw";
+    inherit diskSize;
+    inherit config lib pkgs;
+  };
 
   imports = [ ./azure-common.nix ];
 
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 7d445fa0951e..6adb2c1681a2 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -89,6 +89,15 @@ let
         if [ -n "$HOST_BRIDGE" ]; then
           extraFlags+=" --network-bridge=$HOST_BRIDGE"
         fi
+        if [ -n "$HOST_PORT" ]; then
+          OIFS=$IFS
+          IFS=","
+          for i in $HOST_PORT
+          do
+              extraFlags+=" --port=$i"
+          done
+          IFS=$OIFS
+        fi
       fi
 
       extraFlags+=" ${concatStringsSep " " (mapAttrsToList nspawnExtraVethArgs cfg.extraVeths)}"
@@ -128,6 +137,7 @@ let
         --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
         --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
         --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
+        --setenv HOST_PORT="$HOST_PORT" \
         --setenv PATH="$PATH" \
         ${if cfg.additionalCapabilities != null && cfg.additionalCapabilities != [] then
           ''--capability="${concatStringsSep " " cfg.additionalCapabilities}"'' else ""
@@ -264,7 +274,6 @@ let
       };
       isReadOnly = mkOption {
         default = true;
-        example = true;
         type = types.bool;
         description = "Determine whether the mounted path will be accessed in read-only mode.";
       };
@@ -315,6 +324,36 @@ let
       '';
     };
 
+    forwardPorts = mkOption {
+      type = types.listOf (types.submodule {
+        options = {
+          protocol = mkOption {
+            type = types.str;
+            default = "tcp";
+            description = "The protocol specifier for port forwarding between host and container";
+          };
+          hostPort = mkOption {
+            type = types.int;
+            description = "Source port of the external interface on host";
+          };
+          containerPort = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            description = "Target port of container";
+          };
+        };
+      });
+      default = [];
+      example = [ { protocol = "tcp"; hostPort = 8080; containerPort = 80; } ];
+      description = ''
+        List of forwarded ports from host to container. Each forwarded port
+        is specified by protocol, hostPort and containerPort. By default,
+        protocol is tcp and hostPort and containerPort are assumed to be
+        the same if containerPort is not explicitly given. 
+      '';
+    };
+
+
     hostAddress = mkOption {
       type = types.nullOr types.str;
       default = null;
@@ -642,7 +681,9 @@ in
     # Generate a configuration file in /etc/containers for each
     # container so that container@.target can get the container
     # configuration.
-    environment.etc = mapAttrs' (name: cfg: nameValuePair "containers/${name}.conf"
+    environment.etc =
+      let mkPortStr = p: p.protocol + ":" + (toString p.hostPort) + ":" + (if p.containerPort == null then toString p.hostPort else toString p.containerPort); 
+      in mapAttrs' (name: cfg: nameValuePair "containers/${name}.conf"
       { text =
           ''
             SYSTEM_PATH=${cfg.path}
@@ -651,6 +692,9 @@ in
               ${optionalString (cfg.hostBridge != null) ''
                 HOST_BRIDGE=${cfg.hostBridge}
               ''}
+              ${optionalString (length cfg.forwardPorts > 0) ''
+                HOST_PORT=${concatStringsSep "," (map mkPortStr cfg.forwardPorts)}
+              ''}
               ${optionalString (cfg.hostAddress != null) ''
                 HOST_ADDRESS=${cfg.hostAddress}
               ''}
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index 4b30a38f832f..c26cae06cd1d 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -7,8 +7,7 @@ with lib;
 let
 
   cfg = config.virtualisation.docker;
-  pro = config.networking.proxy.default;
-  proxy_env = optionalAttrs (pro != null) { Environment = "\"http_proxy=${pro}\""; };
+  proxy_env = config.networking.proxy.envVars;
 
 in
 
@@ -106,6 +105,7 @@ in
 
       systemd.services.docker = {
         wantedBy = optional cfg.enableOnBoot "multi-user.target";
+        environment = proxy_env;
         serviceConfig = {
           ExecStart = [
             ""
@@ -122,11 +122,21 @@ in
             ""
             "${pkgs.procps}/bin/kill -s HUP $MAINPID"
           ];
-        } // proxy_env;
+        };
 
         path = [ pkgs.kmod ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
       };
-      systemd.sockets.docker.socketConfig.ListenStream = cfg.listenOptions;
+
+      systemd.sockets.docker = {
+        description = "Docker Socket for the API";
+        wantedBy = [ "sockets.target" ];
+        socketConfig = {
+          ListenStream = cfg.listenOptions;
+          SocketMode = "0660";
+          SocketUser = "root";
+          SocketGroup = "docker";
+        };
+      };
     }
   ]);
 
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index ffd1cbec3ce3..745518f85972 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -135,51 +135,93 @@ let self = {
   "16.03".us-west-2.pv-ebs = "ami-5e61a23e";
   "16.03".us-west-2.pv-s3 = "ami-734c8f13";
 
-  # 16.09.666.3738950
-  "16.09".ap-northeast-1.hvm-ebs = "ami-35578954";
-  "16.09".ap-northeast-1.hvm-s3 = "ami-d6528cb7";
-  "16.09".ap-northeast-1.pv-ebs = "ami-07548a66";
-  "16.09".ap-northeast-1.pv-s3 = "ami-f1548a90";
-  "16.09".ap-northeast-2.hvm-ebs = "ami-d48753ba";
-  "16.09".ap-northeast-2.hvm-s3 = "ami-4c865222";
-  "16.09".ap-northeast-2.pv-ebs = "ami-ca8551a4";
-  "16.09".ap-northeast-2.pv-s3 = "ami-9c8551f2";
-  "16.09".ap-south-1.hvm-ebs = "ami-922450fd";
-  "16.09".ap-south-1.hvm-s3 = "ami-6d3a4e02";
-  "16.09".ap-south-1.pv-ebs = "ami-4d394d22";
-  "16.09".ap-south-1.pv-s3 = "ami-17384c78";
-  "16.09".ap-southeast-1.hvm-ebs = "ami-f824809b";
-  "16.09".ap-southeast-1.hvm-s3 = "ami-f924809a";
-  "16.09".ap-southeast-1.pv-ebs = "ami-af2480cc";
-  "16.09".ap-southeast-1.pv-s3 = "ami-5826823b";
-  "16.09".ap-southeast-2.hvm-ebs = "ami-40fecd23";
-  "16.09".ap-southeast-2.hvm-s3 = "ami-48fecd2b";
-  "16.09".ap-southeast-2.pv-ebs = "ami-dffecdbc";
-  "16.09".ap-southeast-2.pv-s3 = "ami-e0fccf83";
-  "16.09".eu-central-1.hvm-ebs = "ami-1d8b7472";
-  "16.09".eu-central-1.hvm-s3 = "ami-1c8b7473";
-  "16.09".eu-central-1.pv-ebs = "ami-8c8d72e3";
-  "16.09".eu-central-1.pv-s3 = "ami-3488775b";
-  "16.09".eu-west-1.hvm-ebs = "ami-15662766";
-  "16.09".eu-west-1.hvm-s3 = "ami-476b2a34";
-  "16.09".eu-west-1.pv-ebs = "ami-876928f4";
-  "16.09".eu-west-1.pv-s3 = "ami-70682903";
-  "16.09".sa-east-1.hvm-ebs = "ami-27bc2e4b";
-  "16.09".sa-east-1.hvm-s3 = "ami-e4b92b88";
-  "16.09".sa-east-1.pv-ebs = "ami-4dbe2c21";
-  "16.09".sa-east-1.pv-s3 = "ami-77fc6e1b";
-  "16.09".us-east-1.hvm-ebs = "ami-93347684";
-  "16.09".us-east-1.hvm-s3 = "ami-5e347649";
-  "16.09".us-east-1.pv-ebs = "ami-b0387aa7";
-  "16.09".us-east-1.pv-s3 = "ami-51357746";
-  "16.09".us-west-1.hvm-ebs = "ami-06337a66";
-  "16.09".us-west-1.hvm-s3 = "ami-76307916";
-  "16.09".us-west-1.pv-ebs = "ami-fd327b9d";
-  "16.09".us-west-1.pv-s3 = "ami-cc347dac";
-  "16.09".us-west-2.hvm-ebs = "ami-49fe2729";
-  "16.09".us-west-2.hvm-s3 = "ami-93fc25f3";
-  "16.09".us-west-2.pv-ebs = "ami-14fe2774";
-  "16.09".us-west-2.pv-s3 = "ami-74f12814";
+  # 16.09.1508.3909827
+  "16.09".ap-northeast-1.hvm-ebs = "ami-68453b0f";
+  "16.09".ap-northeast-1.hvm-s3 = "ami-f9bec09e";
+  "16.09".ap-northeast-1.pv-ebs = "ami-254a3442";
+  "16.09".ap-northeast-1.pv-s3 = "ami-ef473988";
+  "16.09".ap-northeast-2.hvm-ebs = "ami-18ae7f76";
+  "16.09".ap-northeast-2.hvm-s3 = "ami-9eac7df0";
+  "16.09".ap-northeast-2.pv-ebs = "ami-57aa7b39";
+  "16.09".ap-northeast-2.pv-s3 = "ami-5cae7f32";
+  "16.09".ap-south-1.hvm-ebs = "ami-b3f98fdc";
+  "16.09".ap-south-1.hvm-s3 = "ami-98e690f7";
+  "16.09".ap-south-1.pv-ebs = "ami-aef98fc1";
+  "16.09".ap-south-1.pv-s3 = "ami-caf88ea5";
+  "16.09".ap-southeast-1.hvm-ebs = "ami-80fb51e3";
+  "16.09".ap-southeast-1.hvm-s3 = "ami-2df3594e";
+  "16.09".ap-southeast-1.pv-ebs = "ami-37f05a54";
+  "16.09".ap-southeast-1.pv-s3 = "ami-27f35944";
+  "16.09".ap-southeast-2.hvm-ebs = "ami-57ece834";
+  "16.09".ap-southeast-2.hvm-s3 = "ami-87f4f0e4";
+  "16.09".ap-southeast-2.pv-ebs = "ami-d8ede9bb";
+  "16.09".ap-southeast-2.pv-s3 = "ami-a6ebefc5";
+  "16.09".ca-central-1.hvm-ebs = "ami-9f863bfb";
+  "16.09".ca-central-1.hvm-s3 = "ami-ea85388e";
+  "16.09".ca-central-1.pv-ebs = "ami-ce8a37aa";
+  "16.09".ca-central-1.pv-s3 = "ami-448a3720";
+  "16.09".eu-central-1.hvm-ebs = "ami-1b884774";
+  "16.09".eu-central-1.hvm-s3 = "ami-b08c43df";
+  "16.09".eu-central-1.pv-ebs = "ami-888946e7";
+  "16.09".eu-central-1.pv-s3 = "ami-06874869";
+  "16.09".eu-west-1.hvm-ebs = "ami-1ed3e76d";
+  "16.09".eu-west-1.hvm-s3 = "ami-73d1e500";
+  "16.09".eu-west-1.pv-ebs = "ami-44c0f437";
+  "16.09".eu-west-1.pv-s3 = "ami-f3d8ec80";
+  "16.09".eu-west-2.hvm-ebs = "ami-2c9c9648";
+  "16.09".eu-west-2.hvm-s3 = "ami-6b9e940f";
+  "16.09".eu-west-2.pv-ebs = "ami-f1999395";
+  "16.09".eu-west-2.pv-s3 = "ami-bb9f95df";
+  "16.09".sa-east-1.hvm-ebs = "ami-a11882cd";
+  "16.09".sa-east-1.hvm-s3 = "ami-7726bc1b";
+  "16.09".sa-east-1.pv-ebs = "ami-9725bffb";
+  "16.09".sa-east-1.pv-s3 = "ami-b027bddc";
+  "16.09".us-east-1.hvm-ebs = "ami-854ca593";
+  "16.09".us-east-1.hvm-s3 = "ami-2241a834";
+  "16.09".us-east-1.pv-ebs = "ami-a441a8b2";
+  "16.09".us-east-1.pv-s3 = "ami-e841a8fe";
+  "16.09".us-east-2.hvm-ebs = "ami-3f41645a";
+  "16.09".us-east-2.hvm-s3 = "ami-804065e5";
+  "16.09".us-east-2.pv-ebs = "ami-f1466394";
+  "16.09".us-east-2.pv-s3 = "ami-05426760";
+  "16.09".us-west-1.hvm-ebs = "ami-c2efbca2";
+  "16.09".us-west-1.hvm-s3 = "ami-d71042b7";
+  "16.09".us-west-1.pv-ebs = "ami-04e8bb64";
+  "16.09".us-west-1.pv-s3 = "ami-31e9ba51";
+  "16.09".us-west-2.hvm-ebs = "ami-6449f504";
+  "16.09".us-west-2.hvm-s3 = "ami-344af654";
+  "16.09".us-west-2.pv-ebs = "ami-6d4af60d";
+  "16.09".us-west-2.pv-s3 = "ami-de48f4be";
 
-  latest = self."16.09";
+  # 17.03.885.6024dd4067
+  "17.03".ap-northeast-1.hvm-ebs = "ami-dbd0f7bc";
+  "17.03".ap-northeast-1.hvm-s3 = "ami-7cdff81b";
+  "17.03".ap-northeast-2.hvm-ebs = "ami-c59a48ab";
+  "17.03".ap-northeast-2.hvm-s3 = "ami-0b944665";
+  "17.03".ap-south-1.hvm-ebs = "ami-4f413220";
+  "17.03".ap-south-1.hvm-s3 = "ami-864033e9";
+  "17.03".ap-southeast-1.hvm-ebs = "ami-e08c3383";
+  "17.03".ap-southeast-1.hvm-s3 = "ami-c28f30a1";
+  "17.03".ap-southeast-2.hvm-ebs = "ami-fca9a69f";
+  "17.03".ap-southeast-2.hvm-s3 = "ami-3daaa55e";
+  "17.03".ca-central-1.hvm-ebs = "ami-9b00bdff";
+  "17.03".ca-central-1.hvm-s3 = "ami-e800bd8c";
+  "17.03".eu-central-1.hvm-ebs = "ami-5450803b";
+  "17.03".eu-central-1.hvm-s3 = "ami-6e2efe01";
+  "17.03".eu-west-1.hvm-ebs = "ami-10754c76";
+  "17.03".eu-west-1.hvm-s3 = "ami-11734a77";
+  "17.03".eu-west-2.hvm-ebs = "ami-ff1d099b";
+  "17.03".eu-west-2.hvm-s3 = "ami-fe1d099a";
+  "17.03".sa-east-1.hvm-ebs = "ami-d95d3eb5";
+  "17.03".sa-east-1.hvm-s3 = "ami-fca2c190";
+  "17.03".us-east-1.hvm-ebs = "ami-0940c61f";
+  "17.03".us-east-1.hvm-s3 = "ami-674fc971";
+  "17.03".us-east-2.hvm-ebs = "ami-afc2e6ca";
+  "17.03".us-east-2.hvm-s3 = "ami-a1cde9c4";
+  "17.03".us-west-1.hvm-ebs = "ami-587b2138";
+  "17.03".us-west-1.hvm-s3 = "ami-70411b10";
+  "17.03".us-west-2.hvm-ebs = "ami-a93daac9";
+  "17.03".us-west-2.hvm-s3 = "ami-5139ae31";
+
+  latest = self."17.03";
 }; in self
diff --git a/nixos/modules/virtualisation/ecs-agent.nix b/nixos/modules/virtualisation/ecs-agent.nix
new file mode 100644
index 000000000000..fc51b159579e
--- /dev/null
+++ b/nixos/modules/virtualisation/ecs-agent.nix
@@ -0,0 +1,46 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ecs-agent;
+in {
+  options.services.ecs-agent = {
+    enable = mkEnableOption "Amazon ECS agent";
+
+    package = mkOption {
+      type = types.path;
+      description = "The ECS agent package to use";
+      default = pkgs.ecs-agent;
+      defaultText = "pkgs.ecs-agent";
+    };
+
+    extra-environment = mkOption {
+      type = types.attrsOf types.str;
+      description = "The environment the ECS agent should run with. See the ECS agent documentation for keys that work here.";
+      default = {};
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    # This service doesn't run if docker isn't running, and unlike potentially remote services like e.g., postgresql, docker has
+    # to be running locally so `docker.enable` will always be set if the ECS agent is enabled.
+    virtualisation.docker.enable = true;
+
+    systemd.services.ecs-agent = {
+      inherit (cfg.package.meta) description;
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = cfg.extra-environment;
+
+      script = ''
+        if [ ! -z "$ECS_DATADIR" ]; then
+          mkdir -p "$ECS_DATADIR"
+        fi
+        ${cfg.package.bin}/bin/agent
+      '';
+    };
+  };
+}
+
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 0e6825c3f5da..ff39f1bf8dae 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -2,99 +2,34 @@
 
 with lib;
 let
-  diskSize = "100G";
+  diskSize = 1024; # MB
 in
 {
-  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ];
+  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ./grow-partition.nix ];
 
   # https://cloud.google.com/compute/docs/tutorials/building-images
   networking.firewall.enable = mkDefault false;
 
-  system.build.googleComputeImage =
-    pkgs.vmTools.runInLinuxVM (
-      pkgs.runCommand "google-compute-image"
-        { preVM =
-            ''
-              mkdir $out
-              diskImage=$out/$diskImageBase
-              truncate $diskImage --size ${diskSize}
-              mv closure xchg/
-            '';
-
-          postVM =
-            ''
-              PATH=$PATH:${stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
-              pushd $out
-              mv $diskImageBase disk.raw
-              tar -Szcf $diskImageBase.tar.gz disk.raw
-              rm $out/disk.raw
-              popd
-            '';
-          diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw";
-          buildInputs = [ pkgs.utillinux pkgs.perl ];
-          exportReferencesGraph =
-            [ "closure" config.system.build.toplevel ];
-        }
-        ''
-          # Create partition table
-          ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
-          ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 ${diskSize}
-          ${pkgs.parted}/sbin/parted /dev/vda print
-          . /sys/class/block/vda1/uevent
-          mknod /dev/vda1 b $MAJOR $MINOR
-
-          # Create an empty filesystem and mount it.
-          ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1
-          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
-
-          mkdir /mnt
-          mount /dev/vda1 /mnt
-
-          # The initrd expects these directories to exist.
-          mkdir /mnt/dev /mnt/proc /mnt/sys
-
-          mount --bind /proc /mnt/proc
-          mount --bind /dev /mnt/dev
-          mount --bind /sys /mnt/sys
-
-          # Copy all paths in the closure to the filesystem.
-          storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
-
-          mkdir -p /mnt/nix/store
-          echo "copying everything (will take a while)..."
-          cp -prd $storePaths /mnt/nix/store/
-
-          # Register the paths in the Nix database.
-          printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
-              chroot /mnt ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group ""
-
-          # Create the system profile to allow nixos-rebuild to work.
-          chroot /mnt ${config.nix.package.out}/bin/nix-env \
-              -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} \
-              --option build-users-group ""
-
-          # `nixos-rebuild' requires an /etc/NIXOS.
-          mkdir -p /mnt/etc
-          touch /mnt/etc/NIXOS
-
-          # `switch-to-configuration' requires a /bin/sh
-          mkdir -p /mnt/bin
-          ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
-
-          # Install a configuration.nix.
-          mkdir -p /mnt/etc/nixos /mnt/boot/grub
-          cp ${./google-compute-config.nix} /mnt/etc/nixos/configuration.nix
-
-          # Generate the GRUB menu.
-          ln -s vda /dev/sda
-          chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
-
-          umount /mnt/proc /mnt/dev /mnt/sys
-          umount /mnt
-        ''
-    );
+  system.build.googleComputeImage = import ../../lib/make-disk-image.nix {
+    name = "google-compute-image";
+    postVM = ''
+      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
+      rm $out/disk.raw
+      popd
+    '';
+    configFile = ./google-compute-config.nix;
+    format = "raw";
+    inherit diskSize;
+    inherit config lib pkgs;
+  };
 
-  fileSystems."/".label = "nixos";
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/nixos";
+    autoResize = true;
+  };
 
   boot.kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
   boot.initrd.kernelModules = [ "virtio_scsi" ];
@@ -261,7 +196,7 @@ in
     "kernel.kptr_restrict" = mkDefault "1";
 
     # set ptrace protections
-    "kernel.yama.ptrace_scope" = mkDefault "1";
+    "kernel.yama.ptrace_scope" = mkOverride 500 "1";
 
     # set perf only available to root
     "kernel.perf_event_paranoid" = mkDefault "2";
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 5f669dee7545..c0cebaddec8d 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -13,6 +13,9 @@ let
     auth_unix_rw = "none"
     ${cfg.extraConfig}
   '';
+  qemuConfigFile = pkgs.writeText "qemu.conf" ''
+    ${cfg.qemuVerbatimConfig}
+  '';
 
 in {
 
@@ -48,6 +51,18 @@ in {
       '';
     };
 
+    virtualisation.libvirtd.qemuVerbatimConfig = mkOption {
+      type = types.lines;
+      default = ''
+        namespaces = []
+      '';
+      description = ''
+        Contents written to the qemu configuration file, qemu.conf.
+        Make sure to include a proper namespace configuration when
+        supplying custom configuration.
+      '';
+    };
+
     virtualisation.libvirtd.extraOptions = mkOption {
       type = types.listOf types.str;
       default = [ ];
@@ -75,14 +90,16 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages =
-      [ pkgs.libvirt pkgs.netcat-openbsd ]
-       ++ optional cfg.enableKVM pkgs.qemu_kvm;
+    environment.systemPackages = with pkgs;
+      [ libvirt netcat-openbsd ]
+       ++ optional cfg.enableKVM qemu_kvm;
 
     boot.kernelModules = [ "tun" ];
 
     users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
 
+    systemd.packages = [ pkgs.libvirt ];
+
     systemd.services.libvirtd = {
       description = "Libvirt Virtual Machine Management Daemon";
 
@@ -90,13 +107,17 @@ in {
       after = [ "systemd-udev-settle.service" ]
               ++ optional vswitch.enable "vswitchd.service";
 
-      path = [
-          pkgs.bridge-utils
-          pkgs.dmidecode
-          pkgs.dnsmasq
-          pkgs.ebtables
+      environment = {
+        LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
+      };
+
+      path = with pkgs; [
+          bridge-utils
+          dmidecode
+          dnsmasq
+          ebtables
         ]
-        ++ optional cfg.enableKVM pkgs.qemu_kvm
+        ++ optional cfg.enableKVM qemu_kvm
         ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
@@ -119,6 +140,9 @@ in {
             cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i
         done
 
+        # Copy generated qemu config to libvirt directory
+        cp -f ${qemuConfigFile} /var/lib/libvirt/qemu.conf
+
         # libvirtd puts the full path of the emulator binary in the machine
         # config file. But this path can unfortunately be garbage collected
         # while still being used by the virtual machine. So update the
@@ -135,13 +159,17 @@ in {
       ''; # */
 
       serviceConfig = {
-        ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
         Type = "notify";
         KillMode = "process"; # when stopping, leave the VMs alone
         Restart = "on-failure";
       };
     };
 
+    systemd.services.libvirt-guests = {
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ coreutils libvirt gawk ];
+    };
+
     systemd.sockets.virtlogd = {
       description = "Virtual machine log manager socket";
       wantedBy = [ "sockets.target" ];
diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
index 6759ff0f2fe9..2310fe984325 100644
--- a/nixos/modules/virtualisation/lxc.nix
+++ b/nixos/modules/virtualisation/lxc.nix
@@ -71,6 +71,7 @@ in
     environment.etc."lxc/lxc.conf".text = cfg.systemConfig;
     environment.etc."lxc/lxc-usernet".text = cfg.usernetConfig;
     environment.etc."lxc/default.conf".text = cfg.defaultConfig;
+    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" ];
diff --git a/nixos/modules/virtualisation/nova-config.nix b/nixos/modules/virtualisation/nova-config.nix
new file mode 100644
index 000000000000..aac11ec8a178
--- /dev/null
+++ b/nixos/modules/virtualisation/nova-config.nix
@@ -0,0 +1,57 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  imports = [
+    ../profiles/qemu-guest.nix
+    ../profiles/headless.nix
+    ./grow-partition.nix
+  ];
+
+  config = {
+    fileSystems."/" = {
+      device = "/dev/disk/by-label/nixos";
+      autoResize = true;
+    };
+
+    virtualisation.growPartition = true;
+
+    boot.kernelParams = [ "console=ttyS0" ];
+    boot.loader.grub.device = "/dev/vda";
+    boot.loader.timeout = 0;
+
+    # Allow root logins
+    services.openssh.enable = true;
+    services.openssh.permitRootLogin = "prohibit-password";
+
+    # Put /tmp and /var on /ephemeral0, which has a lot more space.
+    # Unfortunately we can't do this with the `fileSystems' option
+    # because it has no support for creating the source of a bind
+    # mount.  Also, "move" /nix to /ephemeral0 by layering a unionfs-fuse
+    # mount on top of it so we have a lot more space for Nix operations.
+
+    /*
+    boot.initrd.postMountCommands =
+      ''
+        mkdir -m 1777 -p $targetRoot/ephemeral0/tmp
+        mkdir -m 1777 -p $targetRoot/tmp
+        mount --bind $targetRoot/ephemeral0/tmp $targetRoot/tmp
+
+        mkdir -m 755 -p $targetRoot/ephemeral0/var
+        mkdir -m 755 -p $targetRoot/var
+        mount --bind $targetRoot/ephemeral0/var $targetRoot/var
+
+        mkdir -p /unionfs-chroot/ro-nix
+        mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
+
+        mkdir -p /unionfs-chroot/rw-nix
+        mkdir -m 755 -p $targetRoot/ephemeral0/nix
+        mount --rbind $targetRoot/ephemeral0/nix /unionfs-chroot/rw-nix
+        unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
+      '';
+
+      boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
+    */
+  };
+}
diff --git a/nixos/modules/virtualisation/nova-image.nix b/nixos/modules/virtualisation/nova-image.nix
deleted file mode 100644
index e253c77ebb4f..000000000000
--- a/nixos/modules/virtualisation/nova-image.nix
+++ /dev/null
@@ -1,65 +0,0 @@
-# Usage:
-# $ NIXOS_CONFIG=`pwd`/nixos/modules/virtualisation/nova-image.nix nix-build '<nixpkgs/nixos>' -A config.system.build.novaImage
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-  system.build.novaImage = import ../../lib/make-disk-image.nix {
-    inherit pkgs lib config;
-    partitioned = true;
-    diskSize = 1 * 1024;
-    configFile = pkgs.writeText "configuration.nix"
-      ''
-        {
-          imports = [ <nixpkgs/nixos/modules/virtualisation/nova-image.nix> ];
-        }
-      '';
-  };
-
-  imports = [
-    ../profiles/qemu-guest.nix
-    ../profiles/headless.nix
-  ];
-
-  fileSystems."/".device = "/dev/disk/by-label/nixos";
-
-  boot.kernelParams = [ "console=ttyS0" ];
-  boot.loader.grub.device = "/dev/vda";
-  boot.loader.timeout = 0;
-
-  # Allow root logins
-  services.openssh.enable = true;
-  services.openssh.permitRootLogin = "prohibit-password";
-
-  # Put /tmp and /var on /ephemeral0, which has a lot more space.
-  # Unfortunately we can't do this with the `fileSystems' option
-  # because it has no support for creating the source of a bind
-  # mount.  Also, "move" /nix to /ephemeral0 by layering a unionfs-fuse
-  # mount on top of it so we have a lot more space for Nix operations.
-
-  /*
-  boot.initrd.postMountCommands =
-    ''
-      mkdir -m 1777 -p $targetRoot/ephemeral0/tmp
-      mkdir -m 1777 -p $targetRoot/tmp
-      mount --bind $targetRoot/ephemeral0/tmp $targetRoot/tmp
-
-      mkdir -m 755 -p $targetRoot/ephemeral0/var
-      mkdir -m 755 -p $targetRoot/var
-      mount --bind $targetRoot/ephemeral0/var $targetRoot/var
-
-      mkdir -p /unionfs-chroot/ro-nix
-      mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
-
-      mkdir -p /unionfs-chroot/rw-nix
-      mkdir -m 755 -p $targetRoot/ephemeral0/nix
-      mount --rbind $targetRoot/ephemeral0/nix /unionfs-chroot/rw-nix
-      unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
-    '';
-
-    boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
-  */
-
-}
diff --git a/nixos/modules/virtualisation/openstack/glance.nix b/nixos/modules/virtualisation/openstack/glance.nix
index 4d85718e369c..7862409a65ec 100644
--- a/nixos/modules/virtualisation/openstack/glance.nix
+++ b/nixos/modules/virtualisation/openstack/glance.nix
@@ -43,7 +43,7 @@ in {
     package = mkOption {
       type = types.package;
       default = pkgs.glance;
-      example = literalExample "pkgs.glance";
+      defaultText = "pkgs.glance";
       description = ''
         Glance package to use.
       '';
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 586f5d9c0a38..c75edfcd8cf6 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -75,6 +75,7 @@ let
       exec ${qemu}/bin/qemu-kvm \
           -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 \
@@ -136,15 +137,17 @@ let
                       else "-nographic -serial pty";
         }
         ''
-          # Create a /boot EFI partition with 40M
-          ${pkgs.gptfdisk}/bin/sgdisk -G /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -a 1 -n 1:34:2047 -c 1:"BIOS Boot Partition" -t 1:ef02 /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -a 512 -N 2 -c 2:"EFI System" -t 2:ef00 /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -A 1:set:1 /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -A 2:set:2 /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -h 2 /dev/vda
-          ${pkgs.gptfdisk}/bin/sgdisk -C /dev/vda
-          ${pkgs.utillinux}/bin/sfdisk /dev/vda -A 2
+          # Create a /boot EFI partition with 40M and arbitrary but fixed GUIDs for reproducibility
+          ${pkgs.gptfdisk}/bin/sgdisk \
+            --set-alignment=1 --new=1:34:2047 --change-name=1:BIOSBootPartition --typecode=1:ef02 \
+            --set-alignment=512 --largest-new=2 --change-name=2:EFISystem --typecode=2:ef00 \
+            --attributes=1:set:1 \
+            --attributes=2:set:2 \
+            --disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C1 \
+            --partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
+            --partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
+            --hybrid 2 \
+            --recompute-chs /dev/vda
           . /sys/class/block/vda2/uevent
           mknod /dev/vda2 b $MAJOR $MINOR
           . /sys/class/block/vda/uevent
@@ -242,6 +245,18 @@ in
           '';
       };
 
+    virtualisation.cores =
+      mkOption {
+        default = 1;
+        type = types.int;
+        description =
+          ''
+            Specify the number of cores the guest is permitted to use.
+            The number can be higher than the available cores on the
+            host system.
+          '';
+      };
+
     virtualisation.pathsInNixDB =
       mkOption {
         default = [];
@@ -440,13 +455,20 @@ in
         ${if cfg.writableStore then "/nix/.ro-store" else "/nix/store"} =
           { device = "store";
             fsType = "9p";
-            options = [ "trans=virtio" "version=9p2000.L" "veryloose" ];
+            options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
+            neededForBoot = true;
+          };
+        "/tmp" = mkIf config.boot.tmpOnTmpfs
+          { device = "tmpfs";
+            fsType = "tmpfs";
             neededForBoot = true;
+            # Sync with systemd's tmp.mount;
+            options = [ "mode=1777" "strictatime" "nosuid" "nodev" ];
           };
         "/tmp/xchg" =
           { device = "xchg";
             fsType = "9p";
-            options = [ "trans=virtio" "version=9p2000.L" "veryloose" ];
+            options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
             neededForBoot = true;
           };
         "/tmp/shared" =
diff --git a/nixos/modules/virtualisation/rkt.nix b/nixos/modules/virtualisation/rkt.nix
index c4c5cb3380e8..98be4f680c3a 100644
--- a/nixos/modules/virtualisation/rkt.nix
+++ b/nixos/modules/virtualisation/rkt.nix
@@ -22,7 +22,7 @@ in
         description = ''
           Specification (in the format described by
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          <manvolnum>7</manvolnum></citerefentry>) of the time at
           which the garbage collector will run.
         '';
       };
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index d253e9eab62b..5da4b7e3bafd 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -15,18 +15,27 @@ in
 
   ###### interface
 
-  options.virtualisation.virtualbox.guest.enable = mkOption {
-    default = false;
-    description = "Whether to enable the VirtualBox service and other guest additions.";
+  options.virtualisation.virtualbox.guest = {
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = "Whether to enable the VirtualBox service and other guest additions.";
+    };
+
+    x11 = mkOption {
+      default = true;
+      type = types.bool;
+      description = "Whether to enable x11 graphics";
+    };
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable {
-    assertions = [ {
+  config = mkIf cfg.enable (mkMerge [{
+    assertions = [{
       assertion = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
       message = "Virtualbox not currently supported on ${pkgs.stdenv.system}";
-    } ];
+    }];
 
     environment.systemPackages = [ kernel.virtualboxGuestAdditions ];
 
@@ -49,6 +58,16 @@ in
         serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/bin/VBoxService VBoxService --foreground";
       };
 
+    services.udev.extraRules =
+      ''
+        # /dev/vboxuser is necessary for VBoxClient to work.  Maybe we
+        # should restrict this to logged-in users.
+        KERNEL=="vboxuser",  OWNER="root", GROUP="root", MODE="0666"
+
+        # Allow systemd dependencies on vboxguest.
+        SUBSYSTEM=="misc", KERNEL=="vboxguest", TAG+="systemd"
+      '';
+  } (mkIf cfg.x11 {
     services.xserver.videoDrivers = mkOverride 50 [ "virtualbox" "modesetting" ];
 
     services.xserver.config =
@@ -69,16 +88,6 @@ in
         PATH=${makeBinPath [ pkgs.gnugrep pkgs.which pkgs.xorg.xorgserver.out ]}:$PATH \
           ${kernel.virtualboxGuestAdditions}/bin/VBoxClient-all
       '';
-
-    services.udev.extraRules =
-      ''
-        # /dev/vboxuser is necessary for VBoxClient to work.  Maybe we
-        # should restrict this to logged-in users.
-        KERNEL=="vboxuser",  OWNER="root", GROUP="root", MODE="0666"
-
-        # Allow systemd dependencies on vboxguest.
-        SUBSYSTEM=="misc", KERNEL=="vboxguest", TAG+="systemd"
-      '';
-  };
+  })]);
 
 }
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index 7214543871d6..bb0c38bd4eb8 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -68,15 +68,15 @@ in
     boot.extraModulePackages = [ kernelModules ];
     environment.systemPackages = [ virtualbox ];
 
-    security.setuidOwners = let
+    security.wrappers = let
       mkSuid = program: {
-        inherit program;
         source = "${virtualbox}/libexec/virtualbox/${program}";
         owner = "root";
         group = "vboxusers";
         setuid = true;
       };
-    in mkIf cfg.enableHardening (map mkSuid [
+    in mkIf cfg.enableHardening
+      (builtins.listToAttrs (map (x: { name = x; value = mkSuid x; }) [
       "VBoxHeadless"
       "VBoxNetAdpCtl"
       "VBoxNetDHCP"
@@ -84,7 +84,7 @@ in
       "VBoxSDL"
       "VBoxVolInfo"
       "VirtualBox"
-    ]);
+    ]));
 
     users.extraGroups.vboxusers.gid = config.ids.gids.vboxusers;
 
@@ -99,7 +99,7 @@ in
         SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor"
       '';
 
-    # Since we lack the right setuid binaries, set up a host-only network by default.
+    # Since we lack the right setuid/setcap binaries, set up a host-only network by default.
   } (mkIf cfg.addNetworkInterface {
     systemd.services."vboxnet0" =
       { description = "VirtualBox vboxnet0 Interface";
diff --git a/nixos/modules/virtualisation/vmware-guest.nix b/nixos/modules/virtualisation/vmware-guest.nix
index ac5f87817fe9..ce1224a8f131 100644
--- a/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixos/modules/virtualisation/vmware-guest.nix
@@ -4,12 +4,19 @@ with lib;
 
 let
   cfg = config.services.vmwareGuest;
-  open-vm-tools = pkgs.open-vm-tools;
+  open-vm-tools = if cfg.headless then pkgs.open-vm-tools-headless else pkgs.open-vm-tools;
   xf86inputvmmouse = pkgs.xorg.xf86inputvmmouse;
 in
 {
   options = {
-    services.vmwareGuest.enable = mkEnableOption "VMWare Guest Support";
+    services.vmwareGuest = {
+      enable = mkEnableOption "VMWare Guest Support";
+      headless = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to disable X11-related features.";
+      };
+    };
   };
 
   config = mkIf cfg.enable {
@@ -28,7 +35,7 @@ in
 
     environment.etc."vmware-tools".source = "${pkgs.open-vm-tools}/etc/vmware-tools/*";
 
-    services.xserver = {
+    services.xserver = mkIf (!cfg.headless) {
       videoDrivers = mkOverride 50 [ "vmware" ];
       modules = [ xf86inputvmmouse ];
 
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 67eef0ec1e4c..1f5b6bd1d808 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -27,6 +27,33 @@ in
           '';
       };
 
+    virtualisation.xen.package = mkOption {
+      type = types.package;
+      defaultText = "pkgs.xen";
+      example = literalExample "pkgs.xen-light";
+      description = ''
+        The package used for Xen binary.
+      '';
+    };
+
+    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 {
+      type = types.package;
+      defaultText = "pkgs.xen";
+      example = literalExample "pkgs.qemu_xen-light";
+      description = ''
+        The package with qemu binaries for xendomains.
+      '';
+    };
+
     virtualisation.xen.bootParams =
       mkOption {
         default = "";
@@ -106,9 +133,12 @@ in
       message = "Xen currently does not support EFI boot";
     } ];
 
-    virtualisation.xen.stored = mkDefault "${pkgs.xen}/bin/oxenstored";
+    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.stored = mkDefault "${cfg.package}/bin/oxenstored";
 
-    environment.systemPackages = [ pkgs.xen ];
+    environment.systemPackages = [ cfg.package ];
 
     # Make sure Domain 0 gets the required configuration
     #boot.kernelPackages = pkgs.boot.kernelPackages.override { features={xen_dom0=true;}; };
@@ -144,7 +174,7 @@ in
 
     system.extraSystemBuilderCmds =
       ''
-        ln -s ${pkgs.xen}/boot/xen.gz $out/xen.gz
+        ln -s ${cfg.package}/boot/xen.gz $out/xen.gz
         echo "${toString cfg.bootParams}" > $out/xen-params
       '';
 
@@ -180,19 +210,19 @@ in
 
 
     environment.etc =
-      [ { source = "${pkgs.xen}/etc/xen/xl.conf";
+      [ { source = "${cfg.package}/etc/xen/xl.conf";
           target = "xen/xl.conf";
         }
-        { source = "${pkgs.xen}/etc/xen/scripts";
+        { source = "${cfg.package}/etc/xen/scripts";
           target = "xen/scripts";
         }
-        { source = "${pkgs.xen}/etc/default/xendomains";
+        { source = "${cfg.package}/etc/default/xendomains";
           target = "default/xendomains";
         }
       ];
 
     # Xen provides udev rules.
-    services.udev.packages = [ pkgs.xen ];
+    services.udev.packages = [ cfg.package ];
 
     services.udev.path = [ pkgs.bridge-utils pkgs.iproute ];
 
@@ -217,7 +247,7 @@ in
         time=0
         timeout=30
         # Wait for xenstored to actually come up, timing out after 30 seconds
-        while [ $time -lt $timeout ] && ! `${pkgs.xen}/bin/xenstore-read -s / >/dev/null 2>&1` ; do
+        while [ $time -lt $timeout ] && ! `${cfg.package}/bin/xenstore-read -s / >/dev/null 2>&1` ; do
             time=$(($time+1))
             sleep 1
         done
@@ -228,8 +258,8 @@ in
             exit 1
         fi
 
-        ${pkgs.xen}/bin/xenstore-write "/local/domain/0/name" "Domain-0"
-        ${pkgs.xen}/bin/xenstore-write "/local/domain/0/domid" 0
+        ${cfg.package}/bin/xenstore-write "/local/domain/0/name" "Domain-0"
+        ${cfg.package}/bin/xenstore-write "/local/domain/0/domid" 0
         '';
     };
 
@@ -256,7 +286,7 @@ in
         '';
       serviceConfig = {
         ExecStart = ''
-          ${pkgs.xen}/bin/xenconsoled${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
+          ${cfg.package}/bin/xenconsoled${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
           '';
       };
     };
@@ -267,8 +297,8 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "xen-console.service" ];
       serviceConfig.ExecStart = ''
-        ${pkgs.xen}/lib/xen/bin/qemu-system-i386 -xen-domid 0 -xen-attach -name dom0 -nographic -M xenpv \
-           -monitor /dev/null -serial /dev/null -parallel /dev/null
+        ${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \
+           -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
         '';
     };
 
@@ -277,7 +307,7 @@ in
       description = "Xen Watchdog Daemon";
       wantedBy = [ "multi-user.target" ];
       after = [ "xen-qemu.service" ];
-      serviceConfig.ExecStart = "${pkgs.xen}/bin/xenwatchdogd 30 15";
+      serviceConfig.ExecStart = "${cfg.package}/bin/xenwatchdogd 30 15";
       serviceConfig.Type = "forking";
       serviceConfig.RestartSec = "1";
       serviceConfig.Restart = "on-failure";
@@ -366,11 +396,11 @@ in
       before = [ "dhcpd.service" ];
       restartIfChanged = false;
       serviceConfig.RemainAfterExit = "yes";
-      path = [ pkgs.xen ];
-      environment.XENDOM_CONFIG = "${pkgs.xen}/etc/sysconfig/xendomains";
+      path = [ cfg.package cfg.qemu-package ];
+      environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
       preStart = "mkdir -p /var/lock/subsys -m 755";
-      serviceConfig.ExecStart = "${pkgs.xen}/etc/init.d/xendomains start";
-      serviceConfig.ExecStop = "${pkgs.xen}/etc/init.d/xendomains stop";
+      serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";
+      serviceConfig.ExecStop = "${cfg.package}/etc/init.d/xendomains stop";
     };
 
   };
diff --git a/nixos/modules/virtualisation/xen-domU.nix b/nixos/modules/virtualisation/xen-domU.nix
index 8dd0d1dbfd2c..b46002c10b54 100644
--- a/nixos/modules/virtualisation/xen-domU.nix
+++ b/nixos/modules/virtualisation/xen-domU.nix
@@ -3,11 +3,8 @@
 { config, pkgs, ... }:
 
 {
-  # We're being booted using pv-grub, which means that we need to
-  # generate a GRUB 1 menu without actually installing GRUB.
-  boot.loader.grub.version = 1;
+  boot.loader.grub.version = 2;
   boot.loader.grub.device = "nodev";
-  boot.loader.grub.extraPerEntryConfig = "root (hd0)";
 
   boot.initrd.kernelModules =
     [ "xen-blkfront" "xen-tpmfront" "xen-kbdfront" "xen-fbfront"
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index 70b29aa23a5b..4217f5940ec6 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -68,12 +68,11 @@ in rec {
         (all nixos.tests.boot.uefiCdrom)
         (all nixos.tests.boot.uefiUsb)
         (all nixos.tests.boot-stage1)
-        (all nixos.tests.hibernate)
+        nixos.tests.hibernate.x86_64-linux # i686 is flaky, see #23107
         (all nixos.tests.ecryptfs)
         (all nixos.tests.ipv6)
         (all nixos.tests.i3wm)
-        (all nixos.tests.kde4)
-        (all nixos.tests.kde5)
+        (all nixos.tests.plasma5)
         #(all nixos.tests.lightdm)
         (all nixos.tests.login)
         (all nixos.tests.misc)
@@ -93,8 +92,9 @@ in rec {
         (all nixos.tests.openssh)
         (all nixos.tests.printing)
         (all nixos.tests.proxy)
-        (all nixos.tests.sddm)
+        (all nixos.tests.sddm.default)
         (all nixos.tests.simple)
+        (all nixos.tests.slim)
         (all nixos.tests.udisks2)
         (all nixos.tests.xfce)
 
diff --git a/nixos/release-small.nix b/nixos/release-small.nix
index f6e7a65fbdea..28f1340caf8d 100644
--- a/nixos/release-small.nix
+++ b/nixos/release-small.nix
@@ -53,8 +53,7 @@ in rec {
 
   nixpkgs = {
     inherit (nixpkgs')
-      apacheHttpd_2_2
-      apacheHttpd_2_4
+      apacheHttpd
       cmake
       cryptsetup
       emacs
@@ -63,13 +62,12 @@ in rec {
       imagemagick
       jdk
       linux
-      mysql55
+      mysql
       nginx
       nodejs
       openssh
       php
-      postgresql92
-      postgresql93
+      postgresql
       python
       rsyslog
       stdenv
diff --git a/nixos/release.nix b/nixos/release.nix
index dfa9b67654fb..aaf23d7ffb79 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -228,6 +228,7 @@ in rec {
   tests.containers-imperative = callTest tests/containers-imperative.nix {};
   tests.containers-extra_veth = callTest tests/containers-extra_veth.nix {};
   tests.containers-physical_interfaces = callTest tests/containers-physical_interfaces.nix {};
+  tests.containers-restart_networking = callTest tests/containers-restart_networking.nix {};
   tests.containers-tmpfs = callTest tests/containers-tmpfs.nix {};
   tests.containers-hosts = callTest tests/containers-hosts.nix {};
   tests.containers-macvlans = callTest tests/containers-macvlans.nix {};
@@ -247,7 +248,7 @@ in rec {
   tests.gocd-server = callTest tests/gocd-server.nix {};
   tests.gnome3 = callTest tests/gnome3.nix {};
   tests.gnome3-gdm = callTest tests/gnome3-gdm.nix {};
-  tests.grsecurity = callTest tests/grsecurity.nix {};
+  tests.hardened = callTest tests/hardened.nix { };
   tests.hibernate = callTest tests/hibernate.nix {};
   tests.hound = callTest tests/hound.nix {};
   tests.i3wm = callTest tests/i3wm.nix {};
@@ -255,8 +256,7 @@ in rec {
   tests.influxdb = callTest tests/influxdb.nix {};
   tests.ipv6 = callTest tests/ipv6.nix {};
   tests.jenkins = callTest tests/jenkins.nix {};
-  tests.kde4 = callTest tests/kde4.nix {};
-  tests.kde5 = callTest tests/kde5.nix {};
+  tests.plasma5 = callTest tests/plasma5.nix {};
   tests.keymap = callSubTests tests/keymap.nix {};
   tests.initrdNetwork = callTest tests/initrd-network.nix {};
   tests.keystone = callTest tests/keystone.nix {};
@@ -273,6 +273,7 @@ in rec {
   tests.mysql = callTest tests/mysql.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.networking.networkd = callSubTests tests/networking.nix { networkd = true; };
   tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; };
@@ -280,26 +281,31 @@ in rec {
   tests.networkingProxy = callTest tests/networking-proxy.nix {};
   tests.nfs3 = callTest tests/nfs.nix { version = 3; };
   tests.nfs4 = callTest tests/nfs.nix { version = 4; };
+  tests.nginx = callTest tests/nginx.nix { };
   tests.leaps = callTest tests/leaps.nix { };
   tests.nsd = callTest tests/nsd.nix {};
   tests.openssh = callTest tests/openssh.nix {};
+  tests.pam-oath-login = callTest tests/pam-oath-login.nix {};
   #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
   tests.peerflix = callTest tests/peerflix.nix {};
-  tests.postgresql = callTest tests/postgresql.nix {};
+  tests.postgresql = callSubTests tests/postgresql.nix {};
+  tests.pgjwt = callTest tests/pgjwt.nix {};
   tests.printing = callTest tests/printing.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.runInMachine = callTest tests/run-in-machine.nix {};
   tests.samba = callTest tests/samba.nix {};
-  tests.sddm = callTest tests/sddm.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.taskserver = callTest tests/taskserver.nix {};
   tests.tomcat = callTest tests/tomcat.nix {};
   tests.udisks2 = callTest tests/udisks2.nix {};
   tests.virtualbox = callSubTests tests/virtualbox.nix { system = "x86_64-linux"; };
+  tests.wordpress = callTest tests/wordpress.nix {};
   tests.xfce = callTest tests/xfce.nix {};
 
 
@@ -324,8 +330,8 @@ in rec {
 
     kde = makeClosure ({ pkgs, ... }:
       { services.xserver.enable = true;
-        services.xserver.displayManager.kdm.enable = true;
-        services.xserver.desktopManager.kde4.enable = true;
+        services.xserver.displayManager.sddm.enable = true;
+        services.xserver.desktopManager.plasma5.enable = true;
       });
 
     xfce = makeClosure ({ pkgs, ... }:
diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
index 5aded554f4e8..3a718a798315 100644
--- a/nixos/tests/bittorrent.nix
+++ b/nixos/tests/bittorrent.nix
@@ -11,7 +11,7 @@ import ./make-test.nix ({ pkgs, ... }:
 let
 
   # Some random file to serve.
-  file = pkgs.nixUnstable.src;
+  file = pkgs.hello.src;
 
   miniupnpdConf = nodes: pkgs.writeText "miniupnpd.conf"
     ''
diff --git a/nixos/tests/buildbot.nix b/nixos/tests/buildbot.nix
new file mode 100644
index 000000000000..13a162e6c6e8
--- /dev/null
+++ b/nixos/tests/buildbot.nix
@@ -0,0 +1,46 @@
+# Test ensures buildbot master comes up correctly and workers can connect
+
+import ./make-test.nix ({ pkgs, ... } : {
+  name = "buildbot";
+
+  nodes = {
+    bbmaster = { config, pkgs, nodes, ... }: {
+      services.buildbot-master = {
+        enable = true;
+        factorySteps = [
+          "steps.Git(repourl='git://github.com/buildbot/pyflakes.git', mode='incremental')"
+          "steps.ShellCommand(command=['trial', 'pyflakes'])"
+        ];
+        changeSource = [
+          "changes.GitPoller('git://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)"
+        ];
+      };
+      networking.firewall.allowedTCPPorts = [ 8010 9989 ];
+    };
+
+    bbworker = { config, pkgs, ... }: {
+      services.buildbot-worker = {
+        enable = true;
+        masterUrl = "bbmaster:9989";
+      };
+    };
+  };
+
+  testScript = ''
+
+    $bbmaster->waitForUnit("network.target");
+    $bbworker->waitForUnit("network.target");
+
+    # Additional tests to be added
+    #$bbmaster->waitForUnit("buildbot-master.service");
+    #$bbmaster->waitUntilSucceeds("curl -s --head http://bbmaster:8010") =~ /200 OK/ or die;
+    #$bbworker->waitForUnit("buildbot-worker.service");
+    #$bbworker->waitUntilSucceeds("tail -10 /home/bbworker/worker/twistd.log") =~ /success/ or die;
+
+  '';
+
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ nand0p ];
+  };
+
+})
diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix
index 55b1fb5a7222..3a2c65164766 100644
--- a/nixos/tests/chromium.nix
+++ b/nixos/tests/chromium.nix
@@ -18,8 +18,9 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
   enableOCR = true;
 
-  machine.imports = [ ./common/x11.nix ];
+  machine.imports = [ ./common/user-account.nix ./common/x11.nix ];
   machine.virtualisation.memorySize = 2047;
+  machine.services.xserver.displayManager.auto.user = "alice";
   machine.environment.systemPackages = [ chromiumPkg ];
 
   startupHTML = pkgs.writeText "chromium-startup.html" ''
@@ -43,14 +44,20 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
       xdoScript = pkgs.writeText "${name}.xdo" text;
     in "${pkgs.xdotool}/bin/xdotool '${xdoScript}'";
   in ''
+    # Run as user alice
+    sub ru ($) {
+      my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
+      return "su - alice -c '$esc'";
+    }
+
     sub createNewWin {
       $machine->nest("creating a new Chromium window", sub {
-        $machine->execute("${xdo "new-window" ''
+        $machine->execute(ru "${xdo "new-window" ''
           search --onlyvisible --name "startup done"
           windowfocus --sync
           windowactivate --sync
         ''}");
-        $machine->execute("${xdo "new-window" ''
+        $machine->execute(ru "${xdo "new-window" ''
           key Ctrl+n
         ''}");
       });
@@ -58,16 +65,16 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
     sub closeWin {
       Machine::retry sub {
-        $machine->execute("${xdo "close-window" ''
+        $machine->execute(ru "${xdo "close-window" ''
           search --onlyvisible --name "new tab"
           windowfocus --sync
           windowactivate --sync
         ''}");
-        $machine->execute("${xdo "close-window" ''
+        $machine->execute(ru "${xdo "close-window" ''
           key Ctrl+w
         ''}");
         for (1..20) {
-          my ($status, $out) = $machine->execute("${xdo "wait-for-close" ''
+          my ($status, $out) = $machine->execute(ru "${xdo "wait-for-close" ''
             search --onlyvisible --name "new tab"
           ''}");
           return 1 if $status != 0;
@@ -80,7 +87,7 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
       my $ret = 0;
       $machine->nest("waiting for new Chromium window to appear", sub {
         for (1..20) {
-          my ($status, $out) = $machine->execute("${xdo "wait-for-window" ''
+          my ($status, $out) = $machine->execute(ru "${xdo "wait-for-window" ''
             search --onlyvisible --name "new tab"
             windowfocus --sync
             windowactivate --sync
@@ -113,13 +120,9 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
     $machine->waitForX;
 
     my $url = "file://${startupHTML}";
-    my $args = "--user-data-dir=/tmp/chromium-${channel}";
-    $machine->execute(
-      "ulimit -c unlimited; ".
-      "chromium $args \"$url\" & disown"
-    );
+    $machine->execute(ru "ulimit -c unlimited; chromium \"$url\" & disown");
     $machine->waitForText(qr/startup done/);
-    $machine->waitUntilSucceeds("${xdo "check-startup" ''
+    $machine->waitUntilSucceeds(ru "${xdo "check-startup" ''
       search --sync --onlyvisible --name "startup done"
       # close first start help popup
       key -delay 1000 Escape
@@ -134,13 +137,13 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
     $machine->screenshot("startup_done");
 
     testNewWin "check sandbox", sub {
-      $machine->succeed("${xdo "type-url" ''
+      $machine->succeed(ru "${xdo "type-url" ''
         search --sync --onlyvisible --name "new tab"
         windowfocus --sync
         type --delay 1000 "chrome://sandbox"
       ''}");
 
-      $machine->succeed("${xdo "submit-url" ''
+      $machine->succeed(ru "${xdo "submit-url" ''
         search --sync --onlyvisible --name "new tab"
         windowfocus --sync
         key --delay 1000 Return
@@ -148,15 +151,15 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
       $machine->screenshot("sandbox_info");
 
-      $machine->succeed("${xdo "submit-url" ''
+      $machine->succeed(ru "${xdo "submit-url" ''
         search --sync --onlyvisible --name "sandbox status"
         windowfocus --sync
       ''}");
-      $machine->succeed("${xdo "submit-url" ''
+      $machine->succeed(ru "${xdo "submit-url" ''
         key --delay 1000 Ctrl+a Ctrl+c
       ''}");
 
-      my $clipboard = $machine->succeed("${pkgs.xclip}/bin/xclip -o");
+      my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o");
       die "sandbox not working properly: $clipboard"
       unless $clipboard =~ /namespace sandbox.*yes/mi
           && $clipboard =~ /pid namespaces.*yes/mi
diff --git a/nixos/tests/cjdns.nix b/nixos/tests/cjdns.nix
index f32ec52dfc26..466663799241 100644
--- a/nixos/tests/cjdns.nix
+++ b/nixos/tests/cjdns.nix
@@ -109,14 +109,14 @@ import ./make-test.nix ({ pkgs, ...} : {
 
       # ping a few times each to let the routing table establish itself
 
-      $alice->succeed("ping6 -c 4 $carolIp6");
-      $bob->succeed("ping6 -c 4 $carolIp6");
+      $alice->succeed("ping -c 4 $carolIp6");
+      $bob->succeed("ping -c 4 $carolIp6");
 
-      $carol->succeed("ping6 -c 4 $aliceIp6");
-      $carol->succeed("ping6 -c 4 $bobIp6");
+      $carol->succeed("ping -c 4 $aliceIp6");
+      $carol->succeed("ping -c 4 $bobIp6");
 
-      $alice->succeed("ping6 -c 4 $bobIp6");
-      $bob->succeed("ping6 -c 4 $aliceIp6");
+      $alice->succeed("ping -c 4 $bobIp6");
+      $bob->succeed("ping -c 4 $aliceIp6");
 
       $alice->waitForUnit("httpd.service");
 
diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix
index bb32d852a6f5..598abd22e61b 100644
--- a/nixos/tests/containers-bridge.nix
+++ b/nixos/tests/containers-bridge.nix
@@ -66,7 +66,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       "${containerIp6}" =~ /([^\/]+)\/([0-9+])/;
       my $ip6 = $1;
       chomp $ip6;
-      $machine->succeed("ping6 -n -c 1 $ip6");
+      $machine->succeed("ping -n -c 1 $ip6");
       $machine->succeed("curl --fail http://[$ip6]/ > /dev/null");
 
       # Stop the container.
diff --git a/nixos/tests/containers-extra_veth.nix b/nixos/tests/containers-extra_veth.nix
index 2a54b1d961c8..6339c8c558b9 100644
--- a/nixos/tests/containers-extra_veth.nix
+++ b/nixos/tests/containers-extra_veth.nix
@@ -84,7 +84,7 @@ import ./make-test.nix ({ pkgs, ...} : {
 
       # Ping on main veth
       $machine->succeed("ping -n -c 1 192.168.0.100");
-      $machine->succeed("ping6 -n -c 1 fc00::2");
+      $machine->succeed("ping -n -c 1 fc00::2");
 
       # Ping on the first extra veth
       $machine->succeed("ping -n -c 1 192.168.1.100 >&2");
diff --git a/nixos/tests/containers-ipv6.nix b/nixos/tests/containers-ipv6.nix
index 320465ebb95b..f676ed122bb3 100644
--- a/nixos/tests/containers-ipv6.nix
+++ b/nixos/tests/containers-ipv6.nix
@@ -47,7 +47,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       # multi-user.target, we should now be able to access it.
       my $ip = "${localIp}";
       chomp $ip;
-      $machine->succeed("ping6 -n -c 1 $ip");
+      $machine->succeed("ping -n -c 1 $ip");
       $machine->succeed("curl --fail http://[$ip]/ > /dev/null");
 
       # Stop the container.
diff --git a/nixos/tests/containers-portforward.nix b/nixos/tests/containers-portforward.nix
new file mode 100644
index 000000000000..78cc445c2dd0
--- /dev/null
+++ b/nixos/tests/containers-portforward.nix
@@ -0,0 +1,63 @@
+# Test for NixOS' container support.
+
+let
+  hostIp = "192.168.0.1";
+  hostPort = 10080;
+  containerIp = "192.168.0.100";
+  containerPort = 80;
+in 
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-portforward";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ aristid aszlig eelco chaoflow kampfschlaefer ianwookim ];
+  };
+
+  machine =
+    { config, pkgs, ... }:
+    { imports = [ ../modules/installer/cd-dvd/channel.nix ];
+      virtualisation.writableStore = true;
+      virtualisation.memorySize = 768;
+
+      containers.webserver =
+        { privateNetwork = true;
+          hostAddress = hostIp;
+          localAddress = containerIp;
+          forwardPorts = [ { protocol = "tcp"; hostPort = hostPort; containerPort = containerPort; } ];
+          config =
+            { services.httpd.enable = true;
+              services.httpd.adminAddr = "foo@example.org";
+              networking.firewall.allowedTCPPorts = [ 80 ];
+              networking.firewall.allowPing = true;
+            };
+        };
+
+      virtualisation.pathsInNixDB = [ pkgs.stdenv ];
+    };
+
+  testScript =
+    ''
+      $machine->succeed("nixos-container list") =~ /webserver/ or die;
+
+      # Start the webserver container.
+      $machine->succeed("nixos-container start webserver");
+
+      # wait two seconds for the container to start and the network to be up
+      sleep 2;
+
+      # Since "start" returns after the container has reached
+      # multi-user.target, we should now be able to access it.
+      #my $ip = $machine->succeed("nixos-container show-ip webserver");
+      #chomp $ip;
+      $machine->succeed("ping -n -c1 ${hostIp}");
+      $machine->succeed("curl --fail http://${hostIp}:${toString hostPort}/ > /dev/null");
+
+      # Stop the container.
+      $machine->succeed("nixos-container stop webserver");
+      $machine->fail("curl --fail --connect-timeout 2 http://${hostIp}:${toString hostPort}/ > /dev/null");
+
+      # Destroying a declarative container should fail.
+      $machine->fail("nixos-container destroy webserver");
+    '';
+
+})
diff --git a/nixos/tests/containers-restart_networking.nix b/nixos/tests/containers-restart_networking.nix
new file mode 100644
index 000000000000..086d056c51cd
--- /dev/null
+++ b/nixos/tests/containers-restart_networking.nix
@@ -0,0 +1,114 @@
+# Test for NixOS' container support.
+
+let
+  client_base = rec {
+    networking.firewall.enable = false;
+
+    containers.webserver = {
+      autoStart = true;
+      privateNetwork = true;
+      hostBridge = "br0";
+      config = {
+        networking.firewall.enable = false;
+        networking.firewall.allowPing = true;
+        networking.interfaces.eth0.ip4 = [
+          { address = "192.168.1.122"; prefixLength = 24; }
+        ];
+      };
+    };
+  };
+in import ./make-test.nix ({ pkgs, lib, ...} :
+{
+  name = "containers-restart_networking";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ kampfschlaefer ];
+  };
+
+  nodes = {
+    client = { lib, pkgs, ... }: client_base // {
+      virtualisation.vlans = [ 1 ];
+
+      networking.bridges.br0 = {
+        interfaces = [];
+        rstp = false;
+      };
+      networking.interfaces = {
+        eth1.ip4 = lib.mkOverride 0 [ ];
+        br0.ip4 = [{ address = "192.168.1.1"; prefixLength = 24; }];
+      };
+
+    };
+    client_eth1 = { lib, pkgs, ... }: client_base // {
+      networking.bridges.br0 = {
+        interfaces = [ "eth1" ];
+        rstp = false;
+      };
+      networking.interfaces = {
+        eth1.ip4 = lib.mkOverride 0 [ ];
+        br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }];
+      };
+    };
+    client_eth1_rstp = { lib, pkgs, ... }: client_base // {
+      networking.bridges.br0 = {
+        interfaces = [ "eth1" ];
+        rstp = true;
+      };
+      networking.interfaces = {
+        eth1.ip4 = lib.mkOverride 0 [ ];
+        br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }];
+      };
+    };
+  };
+
+  testScript = {nodes, ...}: let
+    originalSystem = nodes.client.config.system.build.toplevel;
+    eth1_bridged = nodes.client_eth1.config.system.build.toplevel;
+    eth1_rstp = nodes.client_eth1_rstp.config.system.build.toplevel;
+  in ''
+    $client->start();
+
+    $client->waitForUnit("default.target");
+
+    subtest "initial state", sub {
+      $client->succeed("ping 192.168.1.122 -c 1 -n >&2");
+      $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2");
+
+      $client->fail("ip l show eth1 |grep \"master br0\" >&2");
+      $client->fail("grep eth1 /run/br0.interfaces >&2");
+    };
+
+    subtest "interfaces without stp", sub {
+      $client->succeed("${eth1_bridged}/bin/switch-to-configuration test >&2");
+
+      $client->succeed("ping 192.168.1.122 -c 1 -n >&2");
+      $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2");
+
+      $client->succeed("ip l show eth1 |grep \"master br0\" >&2");
+      $client->succeed("grep eth1 /run/br0.interfaces >&2");
+    };
+
+    # activating rstp needs another service, therefor the bridge will restart and the container will loose its connectivity
+    #subtest "interfaces with rstp", sub {
+    #  $client->succeed("${eth1_rstp}/bin/switch-to-configuration test >&2");
+    #  $client->execute("ip -4 a >&2");
+    #  $client->execute("ip l >&2");
+    #
+    #  $client->succeed("ping 192.168.1.122 -c 1 -n >&2");
+    #  $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2");
+    #
+    #  $client->succeed("ip l show eth1 |grep \"master br0\" >&2");
+    #  $client->succeed("grep eth1 /run/br0.interfaces >&2");
+    #};
+
+    subtest "back to no interfaces and no stp", sub {
+      $client->succeed("${originalSystem}/bin/switch-to-configuration test >&2");
+
+      $client->succeed("ping 192.168.1.122 -c 1 -n >&2");
+      $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2");
+
+      $client->fail("ip l show eth1 |grep \"master br0\" >&2");
+      $client->fail("grep eth1 /run/br0.interfaces >&2");
+    };
+  '';
+
+})
diff --git a/nixos/tests/dnscrypt-proxy.nix b/nixos/tests/dnscrypt-proxy.nix
index 26409949ec62..845623368250 100644
--- a/nixos/tests/dnscrypt-proxy.nix
+++ b/nixos/tests/dnscrypt-proxy.nix
@@ -15,6 +15,7 @@ import ./make-test.nix ({ pkgs, ... }: {
 
       services.dnscrypt-proxy.enable = true;
       services.dnscrypt-proxy.localPort = localProxyPort;
+      services.dnscrypt-proxy.extraArgs = [ "-X libdcplugin_example.so" ];
 
       services.dnsmasq.enable = true;
       services.dnsmasq.servers = [ "127.0.0.1#${toString localProxyPort}" ];
diff --git a/nixos/tests/docker.nix b/nixos/tests/docker.nix
index 1b57a94a05d4..9096a5868f6c 100644
--- a/nixos/tests/docker.nix
+++ b/nixos/tests/docker.nix
@@ -11,6 +11,21 @@ import ./make-test.nix ({ pkgs, ...} : {
       { config, pkgs, ... }:
         {
           virtualisation.docker.enable = true;
+
+          users.users = {
+            noprivs = {
+              isNormalUser = true;
+              description = "Can't access the docker daemon";
+              password = "foobar";
+            };
+
+            hasprivs = {
+              isNormalUser = true;
+              description = "Can access the docker daemon";
+              password = "foobar";
+              extraGroups = [ "docker" ];
+            };
+          };
         };
     };
 
@@ -21,6 +36,8 @@ import ./make-test.nix ({ pkgs, ...} : {
     $docker->succeed("tar cv --files-from /dev/null | docker import - scratchimg");
     $docker->succeed("docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10");
     $docker->succeed("docker ps | grep sleeping");
+    $docker->succeed("sudo -u hasprivs docker ps");
+    $docker->fail("sudo -u noprivs docker ps");
     $docker->succeed("docker stop sleeping");
   '';
 })
diff --git a/nixos/tests/ec2.nix b/nixos/tests/ec2.nix
index e1f7143e3a95..4ec7e56cc6cb 100644
--- a/nixos/tests/ec2.nix
+++ b/nixos/tests/ec2.nix
@@ -25,8 +25,13 @@ let
           # access. Mostly copied from
           # modules/profiles/installation-device.nix.
           system.extraDependencies =
-            [ pkgs.stdenv pkgs.busybox pkgs.perlPackages.ArchiveCpio
-              pkgs.unionfs-fuse pkgs.mkinitcpio-nfs-utils
+            with pkgs; [
+              stdenv busybox perlPackages.ArchiveCpio unionfs-fuse mkinitcpio-nfs-utils
+
+              # These are used in the configure-from-userdata tests for EC2. Httpd and valgrind are requested
+              # directly by the configuration we set, and libxslt.bin is used indirectly as a build dependency
+              # of the derivation for dbus configuration files.
+              apacheHttpd valgrind.doc libxslt.bin
             ];
         }
       ];
@@ -137,6 +142,8 @@ in {
 
     # ### http://nixos.org/channels/nixos-unstable nixos
     userData = ''
+      { pkgs, ... }:
+
       {
         imports = [
           <nixpkgs/nixos/modules/virtualisation/amazon-image.nix>
@@ -146,12 +153,22 @@ in {
         environment.etc.testFile = {
           text = "whoa";
         };
+
+        services.httpd = {
+          enable = true;
+          adminAddr = "test@example.org";
+          documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
+        };
+        networking.firewall.allowedTCPPorts = [ 80 ];
       }
     '';
     script = ''
       $machine->start;
       $machine->waitForFile("/etc/testFile");
       $machine->succeed("cat /etc/testFile | grep -q 'whoa'");
+
+      $machine->waitForUnit("httpd.service");
+      $machine->succeed("curl http://localhost | grep Valgrind");
     '';
   };
 }
diff --git a/nixos/tests/emacs-daemon.nix b/nixos/tests/emacs-daemon.nix
index a4d63bdb7e41..466e772a881f 100644
--- a/nixos/tests/emacs-daemon.nix
+++ b/nixos/tests/emacs-daemon.nix
@@ -1,7 +1,7 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "emacs-daemon";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ DamienCassou ];
+    maintainers = [ ];
   };
 
   enableOCR = true;
diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix
index 1bdabe93fec1..e1b628c91445 100644
--- a/nixos/tests/firefox.nix
+++ b/nixos/tests/firefox.nix
@@ -8,15 +8,21 @@ import ./make-test.nix ({ pkgs, ... }: {
     { config, pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      environment.systemPackages = [ pkgs.firefox ];
+      environment.systemPackages = [ pkgs.firefox pkgs.xdotool ];
     };
 
   testScript =
     ''
       $machine->waitForX;
-      $machine->execute("firefox file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html &");
+      $machine->execute("xterm -e 'firefox file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' &");
       $machine->waitForWindow(qr/Valgrind/);
       $machine->sleep(40); # wait until Firefox has finished loading the page
+      $machine->execute("xdotool key space"); # do I want to make Firefox the
+                             # default browser? I just want to close the dialog
+      $machine->sleep(2); # wait until Firefox hides the default browser window
+      $machine->execute("xdotool key F12");
+      $machine->sleep(10); # wait until Firefox draws the developer tool panel
+      $machine->succeed("xwininfo -root -tree | grep Valgrind");
       $machine->screenshot("screen");
     '';
 
diff --git a/nixos/tests/gnome3-gdm.nix b/nixos/tests/gnome3-gdm.nix
index 42425b57ba33..2c9c745021af 100644
--- a/nixos/tests/gnome3-gdm.nix
+++ b/nixos/tests/gnome3-gdm.nix
@@ -11,6 +11,7 @@ import ./make-test.nix ({ pkgs, ...} : {
 
       services.xserver.enable = true;
 
+      services.xserver.displayManager.slim.enable = false;
       services.xserver.displayManager.gdm = {
         enable = true;
         autoLogin = {
diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix
deleted file mode 100644
index ee9e0709e5e7..000000000000
--- a/nixos/tests/grsecurity.nix
+++ /dev/null
@@ -1,46 +0,0 @@
-# Basic test to make sure grsecurity works
-
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "grsecurity";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ copumpkin joachifm ];
-  };
-
-  machine = { config, pkgs, ... }:
-    { security.grsecurity.enable = true;
-      boot.kernel.sysctl."kernel.grsecurity.audit_mount" = 0;
-      boot.kernel.sysctl."kernel.grsecurity.deter_bruteforce" = 0;
-      networking.useDHCP = false;
-    };
-
-  testScript = ''
-    subtest "grsec-lock", sub {
-      $machine->succeed("systemctl is-active grsec-lock");
-      $machine->succeed("grep -Fq 1 /proc/sys/kernel/grsecurity/grsec_lock");
-      $machine->fail("echo -n 0 >/proc/sys/kernel/grsecurity/grsec_lock");
-    };
-
-    subtest "paxtest", sub {
-      # TODO: running paxtest blackhat hangs the vm
-      my @pax_mustkill = (
-        "anonmap", "execbss", "execdata", "execheap", "execstack",
-        "mprotanon", "mprotbss", "mprotdata", "mprotheap", "mprotstack",
-      );
-      foreach my $name (@pax_mustkill) {
-        my $paxtest = "${pkgs.paxtest}/lib/paxtest/" . $name;
-        $machine->succeed($paxtest) =~ /Killed/ or die
-      }
-    };
-
-    # tcc -run executes run-time generated code and so allows us to test whether
-    # paxmark actually works (otherwise, the process should be terminated)
-    subtest "tcc", sub {
-      $machine->execute("echo -e '#include <stdio.h>\nint main(void) { puts(\"hello\"); return 0; }' >main.c");
-      $machine->succeed("${pkgs.tinycc.bin}/bin/tcc -run main.c");
-    };
-
-    subtest "RBAC", sub {
-      $machine->succeed("[ -c /dev/grsec ]");
-    };
-  '';
-})
diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix
new file mode 100644
index 000000000000..1d9a9043e03a
--- /dev/null
+++ b/nixos/tests/hardened.nix
@@ -0,0 +1,36 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "hardened";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ joachifm ];
+  };
+
+  machine =
+    { config, lib, pkgs, ... }:
+    with lib;
+    { users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; };
+      users.users.sybil = { isNormalUser = true; group = "wheel"; };
+      imports = [ ../modules/profiles/hardened.nix ];
+    };
+
+  testScript =
+    ''
+      # Test hidepid
+      subtest "hidepid", sub {
+          $machine->succeed("grep -Fq hidepid=2 /proc/mounts");
+          $machine->succeed("[ `su - sybil -c 'pgrep -c -u root'` = 0 ]");
+          $machine->succeed("[ `su - alice -c 'pgrep -c -u root'` != 0 ]");
+      };
+
+      # Test kernel module hardening
+      subtest "lock-modules", sub {
+          $machine->waitForUnit("multi-user.target");
+          # note: this better a be module we normally wouldn't load ...
+          $machine->fail("modprobe dccp");
+      };
+
+      # Test userns
+      subtest "userns", sub {
+          $machine->fail("unshare --user");
+      };
+    '';
+})
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 7616a75b0214..db58a2f5c857 100644
--- a/nixos/tests/hibernate.nix
+++ b/nixos/tests/hibernate.nix
@@ -13,7 +13,7 @@ import ./make-test.nix (pkgs: {
 
       networking.firewall.allowedTCPPorts = [ 4444 ];
 
-      systemd.services.listener.serviceConfig.ExecStart = "${pkgs.netcat}/bin/nc -l 4444";
+      systemd.services.listener.serviceConfig.ExecStart = "${pkgs.netcat}/bin/nc -l 4444 -k";
     };
 
     probe = { config, lib, pkgs, ...}: {
@@ -32,6 +32,7 @@ import ./make-test.nix (pkgs: {
       $machine->succeed("mkswap /dev/vdb");
       $machine->succeed("swapon -a");
       $machine->startJob("listener");
+      $machine->waitForOpenPort(4444);
       $machine->succeed("systemctl hibernate &");
       $machine->waitForShutdown;
       $machine->start;
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 1df2c651f9bc..3ab3c1bac48a 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -34,6 +34,12 @@ let
           boot.loader.systemd-boot.enable = true;
         ''}
 
+        users.extraUsers.alice = {
+          isNormalUser = true;
+          home = "/home/alice";
+          description = "Alice Foobar";
+        };
+
         hardware.enableAllFirmware = lib.mkForce false;
 
         ${replaceChars ["\n"] ["\n  "] extraConfig}
@@ -96,7 +102,7 @@ let
       $machine->shutdown;
 
       # Now see if we can boot the installation.
-      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
+      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "boot-after-install" });
 
       # For example to enter LUKS passphrase.
       ${preBootCommands}
@@ -115,14 +121,20 @@ let
 
       # Did the swap device get activated?
       # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved
-      #$machine->waitForUnit("swap.target");
-      $machine->waitUntilSucceeds("cat /proc/swaps | grep -q /dev");
+      $machine->waitForUnit("swap.target");
+      $machine->succeed("cat /proc/swaps | grep -q /dev");
+
+      # Check that the store is in good shape
+      $machine->succeed("nix-store --verify --check-contents >&2");
 
       # Check whether the channel works.
       $machine->succeed("nix-env -iA nixos.procps >&2");
       $machine->succeed("type -tP ps | tee /dev/stderr") =~ /.nix-profile/
           or die "nix-env failed";
 
+      # 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.
       $machine->copyFileFromHost(
           "${ makeConfig { inherit bootLoader grubVersion grubDevice grubIdentifier extraConfig; forceGrubReinstallCount = 1; } }",
@@ -139,7 +151,7 @@ let
       $machine->shutdown;
 
       # Check whether a writable store build works
-      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
+      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", name => "rebuild-switch" });
       ${preBootCommands}
       $machine->waitForUnit("multi-user.target");
       $machine->copyFileFromHost(
@@ -150,7 +162,7 @@ let
 
       # And just to be sure, check that the machine still boots after
       # "nixos-rebuild switch".
-      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
+      $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}", "boot-after-rebuild-switch" });
       ${preBootCommands}
       $machine->waitForUnit("network.target");
       $machine->shutdown;
diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix
index 4e2e6379cad3..060f63216796 100644
--- a/nixos/tests/ipv6.nix
+++ b/nixos/tests/ipv6.nix
@@ -54,22 +54,22 @@ import ./make-test.nix ({ pkgs, ...} : {
       }
 
       subtest "loopback address", sub {
-          $client->succeed("ping6 -c 1 ::1 >&2");
-          $client->fail("ping6 -c 1 ::2 >&2");
+          $client->succeed("ping -c 1 ::1 >&2");
+          $client->fail("ping -c 1 ::2 >&2");
       };
 
       subtest "local link addressing", sub {
           my $clientIp = waitForAddress $client, "eth1", "link";
           my $serverIp = waitForAddress $server, "eth1", "link";
-          $client->succeed("ping6 -c 1 -I eth1 $clientIp >&2");
-          $client->succeed("ping6 -c 1 -I eth1 $serverIp >&2");
+          $client->succeed("ping -c 1 $clientIp%eth1 >&2");
+          $client->succeed("ping -c 1 $serverIp%eth1 >&2");
       };
 
       subtest "global addressing", sub {
           my $clientIp = waitForAddress $client, "eth1", "global";
           my $serverIp = waitForAddress $server, "eth1", "global";
-          $client->succeed("ping6 -c 1 $clientIp >&2");
-          $client->succeed("ping6 -c 1 $serverIp >&2");
+          $client->succeed("ping -c 1 $clientIp >&2");
+          $client->succeed("ping -c 1 $serverIp >&2");
           $client->succeed("curl --fail -g http://[$serverIp]");
           $client->fail("curl --fail -g http://[$clientIp]");
       };
diff --git a/nixos/tests/kde4.nix b/nixos/tests/kde4.nix
deleted file mode 100644
index 9ecfe6870564..000000000000
--- a/nixos/tests/kde4.nix
+++ /dev/null
@@ -1,70 +0,0 @@
-import ./make-test.nix ({ pkgs, ... }: {
-  name = "kde4";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ domenkozar eelco chaoflow ];
-  };
-
-  machine =
-    { config, pkgs, ... }:
-
-    { imports = [ ./common/user-account.nix ];
-
-      virtualisation.memorySize = 1024;
-
-      services.xserver.enable = true;
-
-      services.httpd.enable = true;
-      services.httpd.adminAddr = "foo@example.org";
-      services.httpd.documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
-
-      services.xserver.displayManager.kdm.enable = true;
-      services.xserver.displayManager.kdm.extraConfig =
-        ''
-          [X-:0-Core]
-          AutoLoginEnable=true
-          AutoLoginUser=alice
-          AutoLoginPass=foobar
-        '';
-
-      services.xserver.desktopManager.kde4.enable = true;
-
-      # Include most of KDE. We don't really test these here, but at
-      # least they should build.
-      environment.systemPackages =
-        [ pkgs.kde4.kdemultimedia
-          pkgs.kde4.kdegraphics
-          pkgs.kde4.kdeutils
-          pkgs.kde4.kdegames
-          #pkgs.kde4.kdeedu
-          pkgs.kde4.kdeaccessibility
-          pkgs.kde4.kdeadmin
-          pkgs.kde4.kdenetwork
-          pkgs.kde4.kdetoys
-          pkgs.kde4.kdewebdev
-          pkgs.xorg.xmessage
-        ];
-    };
-
-  testScript = ''
-      $machine->waitUntilSucceeds("pgrep plasma-desktop");
-      $machine->succeed("xauth merge ~alice/.Xauthority");
-      $machine->waitForWindow(qr/plasma-desktop/);
-
-      # Check that logging in has given the user ownership of devices.
-      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
-
-      $machine->execute("su - alice -c 'DISPLAY=:0.0 kwrite /var/log/messages &'");
-      $machine->waitForWindow(qr/messages.*KWrite/);
-
-      $machine->execute("su - alice -c 'DISPLAY=:0.0 konqueror http://localhost/ &'");
-      $machine->waitForWindow(qr/Valgrind.*Konqueror/);
-
-      $machine->execute("su - alice -c 'DISPLAY=:0.0 gwenview ${pkgs.kde4.kde_wallpapers}/share/wallpapers/Hanami/contents/images/1280x1024.jpg &'");
-      $machine->waitForWindow(qr/Gwenview/);
-
-      $machine->sleep(10);
-
-      $machine->screenshot("screen");
-    '';
-
-})
diff --git a/nixos/tests/leaps.nix b/nixos/tests/leaps.nix
index 3c390e1a1691..6163fed56b6f 100644
--- a/nixos/tests/leaps.nix
+++ b/nixos/tests/leaps.nix
@@ -24,6 +24,7 @@ import ./make-test.nix ({ pkgs,  ... }:
     ''
       startAll;
       $server->waitForOpenPort(6666);
-      $client->succeed("curl http://server:6666/leaps/ | grep -i 'leaps'"); 
+      $client->waitForUnit("network.target");
+      $client->succeed("${pkgs.curl}/bin/curl http://server:6666/leaps/ | grep -i 'leaps'");
     '';
 })
diff --git a/nixos/tests/mesos.nix b/nixos/tests/mesos.nix
index 3610603aeba2..6e9af126f032 100644
--- a/nixos/tests/mesos.nix
+++ b/nixos/tests/mesos.nix
@@ -1,32 +1,91 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "simple";
+import ./make-test.nix ({ pkgs, ...} : rec {
+  name = "mesos";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ offline ];
+    maintainers = [ offline kamilchm cstrahan ];
   };
 
-  machine = { config, pkgs, ... }: {
-    services.zookeeper.enable = true;
-    virtualisation.docker.enable = true;
-    services.mesos = {
-      slave = {
-        enable = true;
-        master = "zk://localhost:2181/mesos";
-        attributes = {
-          tag1 = "foo";
-          tag2 = "bar";
-        };
+  nodes = {
+    master = { config, pkgs, ... }: {
+      networking.firewall.enable = false;
+      services.zookeeper.enable = true;
+      services.mesos.master = {
+          enable = true;
+          zk = "zk://master:2181/mesos";
       };
-      master = {
-        enable = true;
-        zk = "zk://localhost:2181/mesos";
+    };
+
+    slave = { config, pkgs, ... }: {
+      networking.firewall.enable = false;
+      networking.nat.enable = true;
+      virtualisation.docker.enable = true;
+      services.mesos = {
+        slave = {
+          enable = true;
+          master = "master:5050";
+          dockerRegistry = registry;
+          executorEnvironmentVariables = {
+            PATH = "/run/current-system/sw/bin";
+          };
+        };
       };
     };
   };
 
+  simpleDocker = pkgs.dockerTools.buildImage {
+    name = "echo";
+    contents = [ pkgs.stdenv.shellPackage pkgs.coreutils ];
+    config = {
+      Env = [
+        # When shell=true, mesos invokes "sh -c '<cmd>'", so make sure "sh" is
+        # on the PATH.
+        "PATH=${pkgs.stdenv.shellPackage}/bin:${pkgs.coreutils}/bin"
+      ];
+      Entrypoint = [ "echo" ];
+    };
+  };
+
+  registry = pkgs.runCommand "registry" { } ''
+    mkdir -p $out
+    cp ${simpleDocker} $out/echo:latest.tar
+  '';
+
+  testFramework = pkgs.pythonPackages.buildPythonPackage {
+    name = "mesos-tests";
+    propagatedBuildInputs = [ pkgs.mesos ];
+    catchConflicts = false;
+    src = ./mesos_test.py;
+    phases = [ "installPhase" "fixupPhase" ];
+    installPhase = ''
+      mkdir $out
+      cp $src $out/mesos_test.py
+      chmod +x $out/mesos_test.py
+
+      echo "done" > test.result
+      tar czf $out/test.tar.gz test.result
+    '';
+  };
+
   testScript =
     ''
       startAll;
-      $machine->waitForUnit("mesos-master.service");
-      $machine->waitForUnit("mesos-slave.service");
+      $master->waitForUnit("mesos-master.service");
+      $slave->waitForUnit("mesos-slave.service");
+
+      $master->waitForOpenPort(5050);
+      $slave->waitForOpenPort(5051);
+
+      # is slave registred? 
+      $master->waitUntilSucceeds("curl -s --fail http://master:5050/master/slaves".
+                                 " | grep -q \"\\\"hostname\\\":\\\"slave\\\"\"");
+
+      # try to run docker image 
+      $master->succeed("${pkgs.mesos}/bin/mesos-execute --master=master:5050".
+                       " --resources=\"cpus:0.1;mem:32\" --name=simple-docker".
+                       " --containerizer=mesos --docker_image=echo:latest".
+                       " --shell=true --command=\"echo done\" | grep -q TASK_FINISHED");
+
+      # simple command with .tar.gz uri
+      $master->succeed("${testFramework}/mesos_test.py master ".
+                       "${testFramework}/test.tar.gz");
     '';
 })
diff --git a/nixos/tests/mesos_test.py b/nixos/tests/mesos_test.py
new file mode 100644
index 000000000000..be8bb32e49a7
--- /dev/null
+++ b/nixos/tests/mesos_test.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+import uuid
+import time
+import subprocess
+import os
+
+import sys
+
+from mesos.interface import Scheduler
+from mesos.native import MesosSchedulerDriver
+from mesos.interface import mesos_pb2
+
+def log(msg):
+    process = subprocess.Popen("systemd-cat", stdin=subprocess.PIPE)
+    (out,err) = process.communicate(msg)
+
+class NixosTestScheduler(Scheduler):
+    def __init__(self):
+        self.master_ip = sys.argv[1]
+        self.download_uri = sys.argv[2]
+
+    def resourceOffers(self, driver, offers):
+        log("XXX got resource offer")
+
+        offer = offers[0]
+        task = self.new_task(offer)
+        uri = task.command.uris.add()
+        uri.value = self.download_uri
+        task.command.value = "cat test.result"
+        driver.launchTasks(offer.id, [task])
+
+    def statusUpdate(self, driver, update):
+        log("XXX status update")
+        if update.state == mesos_pb2.TASK_FAILED:
+            log("XXX test task failed with message: " + update.message)
+            driver.stop()
+            sys.exit(1)
+        elif update.state == mesos_pb2.TASK_FINISHED:
+            driver.stop()
+            sys.exit(0)
+
+    def new_task(self, offer):
+        task = mesos_pb2.TaskInfo()
+        id = uuid.uuid4()
+        task.task_id.value = str(id)
+        task.slave_id.value = offer.slave_id.value
+        task.name = "task {}".format(str(id))
+
+        cpus = task.resources.add()
+        cpus.name = "cpus"
+        cpus.type = mesos_pb2.Value.SCALAR
+        cpus.scalar.value = 0.1
+
+        mem = task.resources.add()
+        mem.name = "mem"
+        mem.type = mesos_pb2.Value.SCALAR
+        mem.scalar.value = 32
+
+        return task
+
+if __name__ == '__main__':
+    log("XXX framework started")
+
+    framework = mesos_pb2.FrameworkInfo()
+    framework.user = "root"
+    framework.name = "nixos-test-framework"
+    driver = MesosSchedulerDriver(
+        NixosTestScheduler(),
+        framework,
+        sys.argv[1] + ":5050"
+    )
+    driver.run()
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index cd4086cb8f62..b926a62194b4 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -25,8 +25,6 @@ import ./make-test.nix ({ pkgs, ...} : {
         };
       users.users.sybil = { isNormalUser = true; group = "wheel"; };
       security.sudo = { enable = true; wheelNeedsPassword = false; };
-      security.hideProcessInformation = true;
-      users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; };
     };
 
   testScript =
@@ -119,12 +117,5 @@ import ./make-test.nix ({ pkgs, ...} : {
       subtest "sudo", sub {
           $machine->succeed("su - sybil -c 'sudo true'");
       };
-
-      # Test hidepid
-      subtest "hidepid", sub {
-          $machine->succeed("grep -Fq hidepid=2 /etc/mtab");
-          $machine->succeed("[ `su - sybil -c 'pgrep -c -u root'` = 0 ]");
-          $machine->succeed("[ `su - alice -c 'pgrep -c -u root'` != 0 ]");
-      };
     '';
 })
diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix
index 50746d17b451..40fafc625146 100644
--- a/nixos/tests/munin.nix
+++ b/nixos/tests/munin.nix
@@ -29,6 +29,7 @@ import ./make-test.nix ({ pkgs, ...} : {
     startAll;
 
     $one->waitForUnit("munin-node.service");
+    $one->succeed('systemctl start munin-cron');
     $one->waitForFile("/var/lib/munin/one/one-uptime-uptime-g.rrd");
     $one->waitForFile("/var/www/munin/one/index.html");
   '';
diff --git a/nixos/tests/mysql-replication.nix b/nixos/tests/mysql-replication.nix
index b20bce8edce6..75c6d793febc 100644
--- a/nixos/tests/mysql-replication.nix
+++ b/nixos/tests/mysql-replication.nix
@@ -56,11 +56,19 @@ in
   testScript = ''
     $master->start;
     $master->waitForUnit("mysql");
+    $master->waitForOpenPort(3306);
     $slave1->start;
     $slave2->start;
     $slave1->waitForUnit("mysql");
+    $slave1->waitForOpenPort(3306);
     $slave2->waitForUnit("mysql");
-    $slave2->sleep(100); # Hopefully this is long enough!!
+    $slave2->waitForOpenPort(3306);
     $slave2->succeed("echo 'use testdb; select * from tests' | mysql -u root -N | grep 4");
+    $slave2->succeed("systemctl stop mysql");
+    $master->succeed("echo 'insert into testdb.tests values (123, 456);' | mysql -u root -N");
+    $slave2->succeed("systemctl start mysql");
+    $slave2->waitForUnit("mysql");
+    $slave2->waitForOpenPort(3306);
+    $slave2->succeed("echo 'select * from testdb.tests where Id = 123;' | mysql -u root -N | grep 456");
   '';
 })
diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix
index 4fbf64462682..74e20bff8d81 100644
--- a/nixos/tests/nat.nix
+++ b/nixos/tests/nat.nix
@@ -3,34 +3,47 @@
 # client on the inside network, a server on the outside network, and a
 # router connected to both that performs Network Address Translation
 # for the client.
-import ./make-test.nix ({ pkgs, withFirewall, ... }:
+import ./make-test.nix ({ pkgs, lib, withFirewall, withConntrackHelpers ? false, ... }:
   let
     unit = if withFirewall then "firewall" else "nat";
   in
   {
-    name = "nat${if withFirewall then "WithFirewall" else "Standalone"}";
-  meta = with pkgs.stdenv.lib.maintainers; {
+    name = "nat" + (if withFirewall then "WithFirewall" else "Standalone")
+                 + (lib.optionalString withConntrackHelpers "withConntrackHelpers");
+    meta = with pkgs.stdenv.lib.maintainers; {
       maintainers = [ eelco chaoflow rob wkennington ];
     };
 
     nodes =
       { client =
           { config, pkgs, nodes, ... }:
-          { virtualisation.vlans = [ 1 ];
-            networking.firewall.allowPing = true;
-            networking.defaultGateway =
-              (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ip4).address;
-          };
+          lib.mkMerge [
+            { virtualisation.vlans = [ 1 ];
+              networking.firewall.allowPing = true;
+              networking.defaultGateway =
+                (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ip4).address;
+            }
+            (lib.optionalAttrs withConntrackHelpers {
+              networking.firewall.connectionTrackingModules = [ "ftp" ];
+              networking.firewall.autoLoadConntrackHelpers = true;
+            })
+          ];
 
         router =
           { config, pkgs, ... }:
-          { virtualisation.vlans = [ 2 1 ];
-            networking.firewall.enable = withFirewall;
-            networking.firewall.allowPing = true;
-            networking.nat.enable = true;
-            networking.nat.internalIPs = [ "192.168.1.0/24" ];
-            networking.nat.externalInterface = "eth1";
-          };
+          lib.mkMerge [
+            { virtualisation.vlans = [ 2 1 ];
+              networking.firewall.enable = withFirewall;
+              networking.firewall.allowPing = true;
+              networking.nat.enable = true;
+              networking.nat.internalIPs = [ "192.168.1.0/24" ];
+              networking.nat.externalInterface = "eth1";
+            }
+            (lib.optionalAttrs withConntrackHelpers {
+              networking.firewall.connectionTrackingModules = [ "ftp" ];
+              networking.firewall.autoLoadConntrackHelpers = true;
+            })
+          ];
 
         server =
           { config, pkgs, ... }:
@@ -66,7 +79,8 @@ import ./make-test.nix ({ pkgs, withFirewall, ... }:
         $client->succeed("curl -v ftp://server/foo.txt >&2");
 
         # Test whether active FTP works.
-        $client->succeed("curl -v -P - ftp://server/foo.txt >&2");
+        $client->${if withConntrackHelpers then "succeed" else "fail"}(
+          "curl -v -P - ftp://server/foo.txt >&2");
 
         # Test ICMP.
         $client->succeed("ping -c 1 router >&2");
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 17d4a878d3a4..6a7e628d8ef1 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -1,4 +1,6 @@
-{ system ? builtins.currentSystem, networkd }:
+{ system ? builtins.currentSystem
+# bool: whether to use networkd in the tests
+, networkd }:
 
 with import ../lib/testing.nix { inherit system; };
 with pkgs.lib;
@@ -10,29 +12,61 @@ let
       vlanIfs = range 1 (length config.virtualisation.vlans);
     in {
       virtualisation.vlans = [ 1 2 3 ];
+      boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
       networking = {
         useDHCP = false;
         useNetworkd = networkd;
         firewall.allowPing = true;
+        firewall.checkReversePath = true;
+        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;
           })));
       };
-      services.dhcpd = {
+      services.dhcpd4 = {
         enable = true;
         interfaces = map (n: "eth${toString n}") vlanIfs;
         extraConfig = ''
-          option subnet-mask 255.255.255.0;
+          authoritative;
         '' + flip concatMapStrings vlanIfs (n: ''
           subnet 192.168.${toString n}.0 netmask 255.255.255.0 {
-            option broadcast-address 192.168.${toString n}.255;
             option routers 192.168.${toString n}.1;
+            # XXX: technically it's _not guaranteed_ that IP addresses will be
+            # issued from the first item in range onwards! We assume that in
+            # our tests however.
             range 192.168.${toString n}.2 192.168.${toString n}.254;
           }
         '');
       };
+      services.radvd = {
+        enable = true;
+        config = flip concatMapStrings vlanIfs (n: ''
+          interface eth${toString n} {
+            AdvSendAdvert on;
+            AdvManagedFlag on;
+            AdvOtherConfigFlag on;
+
+            prefix fd00:1234:5678:${toString n}::/64 {
+              AdvAutonomous off;
+            };
+          };
+        '');
+      };
+      services.dhcpd6 = {
+        enable = true;
+        interfaces = map (n: "eth${toString n}") vlanIfs;
+        extraConfig = ''
+          authoritative;
+        '' + flip concatMapStrings vlanIfs (n: ''
+          subnet6 fd00:1234:5678:${toString n}::/64 {
+            range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2;
+          }
+        '');
+      };
     };
 
   testCases = {
@@ -108,8 +142,14 @@ let
           useNetworkd = networkd;
           firewall.allowPing = true;
           useDHCP = true;
-          interfaces.eth1.ip4 = mkOverride 0 [ ];
-          interfaces.eth2.ip4 = mkOverride 0 [ ];
+          interfaces.eth1 = {
+            ip4 = mkOverride 0 [ ];
+            ip6 = mkOverride 0 [ ];
+          };
+          interfaces.eth2 = {
+            ip4 = mkOverride 0 [ ];
+            ip6 = mkOverride 0 [ ];
+          };
         };
       };
       testScript = { nodes, ... }:
@@ -121,21 +161,31 @@ let
 
           # Wait until we have an ip address on each interface
           $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'");
+          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
           $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'");
+          $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'");
 
           # Test vlan 1
           $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
           $client->waitUntilSucceeds("ping -c 1 192.168.1.2");
+          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
+          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2");
 
           $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
           $router->waitUntilSucceeds("ping -c 1 192.168.1.2");
+          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
+          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2");
 
           # Test vlan 2
           $client->waitUntilSucceeds("ping -c 1 192.168.2.1");
           $client->waitUntilSucceeds("ping -c 1 192.168.2.2");
+          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1");
+          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2");
 
           $router->waitUntilSucceeds("ping -c 1 192.168.2.1");
           $router->waitUntilSucceeds("ping -c 1 192.168.2.2");
+          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1");
+          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2");
         '';
     };
     dhcpOneIf = {
@@ -188,8 +238,8 @@ let
           firewall.allowPing = true;
           useDHCP = false;
           bonds.bond = {
-            mode = "balance-rr";
             interfaces = [ "eth1" "eth2" ];
+            driverOptions.mode = "balance-rr";
           };
           interfaces.eth1.ip4 = mkOverride 0 [ ];
           interfaces.eth2.ip4 = mkOverride 0 [ ];
@@ -342,11 +392,11 @@ let
           $client2->succeed("ip addr >&2");
 
           # Test ipv6
-          $client1->waitUntilSucceeds("ping6 -c 1 fc00::1");
-          $client1->waitUntilSucceeds("ping6 -c 1 fc00::2");
+          $client1->waitUntilSucceeds("ping -c 1 fc00::1");
+          $client1->waitUntilSucceeds("ping -c 1 fc00::2");
 
-          $client2->waitUntilSucceeds("ping6 -c 1 fc00::1");
-          $client2->waitUntilSucceeds("ping6 -c 1 fc00::2");
+          $client2->waitUntilSucceeds("ping -c 1 fc00::1");
+          $client2->waitUntilSucceeds("ping -c 1 fc00::2");
         '';
     };
     vlan = let
diff --git a/nixos/tests/nfs.nix b/nixos/tests/nfs.nix
index 36cd6a395779..6ed1995f262a 100644
--- a/nixos/tests/nfs.nix
+++ b/nixos/tests/nfs.nix
@@ -40,7 +40,7 @@ in
 
   testScript =
     ''
-      $server->waitForUnit("nfsd");
+      $server->waitForUnit("nfs-server");
       $server->succeed("systemctl start network-online.target");
       $server->waitForUnit("network-online.target");
 
@@ -54,8 +54,8 @@ in
       $client2->succeed("echo bla > /data/bar");
       $server->succeed("test -e /data/bar");
 
-      # Test whether restarting ‘nfsd’ works correctly.
-      $server->succeed("systemctl restart nfsd");
+      # Test whether restarting ‘nfs-server’ works correctly.
+      $server->succeed("systemctl restart nfs-server");
       $client2->succeed("echo bla >> /data/bar"); # will take 90 seconds due to the NFS grace period
 
       # Test whether we can get a lock.
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
new file mode 100644
index 000000000000..c2beb5590ef7
--- /dev/null
+++ b/nixos/tests/nginx.nix
@@ -0,0 +1,42 @@
+# verifies:
+#   1. nginx generates config file with shared http context definitions above
+#      generated virtual hosts config.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "jenkins";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ mbbx6spp ];
+  };
+
+  nodes = {
+    webserver =
+      { config, pkgs, ... }:
+      { services.nginx.enable = true;
+        services.nginx.commonHttpConfig = ''
+        log_format ceeformat '@cee: {"status":"$status",'
+          '"request_time":$request_time,'
+          '"upstream_response_time":$upstream_response_time,'
+          '"pipe":"$pipe","bytes_sent":$bytes_sent,'
+          '"connection":"$connection",'
+          '"remote_addr":"$remote_addr",'
+          '"host":"$host",'
+          '"timestamp":"$time_iso8601",'
+          '"request":"$request",'
+          '"http_referer":"$http_referer",'
+          '"upstream_addr":"$upstream_addr"}';
+        '';
+        services.nginx.virtualHosts."0.my.test" = {
+          extraConfig = ''
+            access_log syslog:server=unix:/dev/log,facility=user,tag=mytag,severity=info ceeformat;
+          '';
+        };
+      };
+  };
+
+  testScript = ''
+    startAll;
+
+    $webserver->waitForUnit("nginx");
+    $webserver->waitForOpenPort("80");
+  '';
+})
diff --git a/nixos/tests/pam-oath-login.nix b/nixos/tests/pam-oath-login.nix
new file mode 100644
index 000000000000..4364d6e354a6
--- /dev/null
+++ b/nixos/tests/pam-oath-login.nix
@@ -0,0 +1,126 @@
+import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
+
+let
+  oathSnakeoilSecret = "cdd4083ef8ff1fa9178c6d46bfb1a3";
+
+  # With HOTP mode the password is calculated based on a counter of
+  # how many passwords have been made. In this env, we'll always be on
+  # the 0th counter, so the password is static.
+  #
+  # Generated in nix-shell -p oathToolkit
+  # via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3
+  # and picking a the first 4:
+  oathSnakeOilPassword1 = "143349";
+  oathSnakeOilPassword2 = "801753";
+  oathSnakeOilPassword3 = "019933";
+  oathSnakeOilPassword4 = "403895";
+
+  alicePassword = "foobar";
+  # Generated via: mkpasswd -m sha-512 and passing in "foobar"
+  hashedAlicePassword = "$6$MsMrE1q.1HrCgTS$Vq2e/uILzYjSN836TobAyN9xh9oi7EmCmucnZID25qgPoibkw8qTCugiAPnn4eCGvn1A.7oEBFJaaGUaJsQQY.";
+
+in
+{
+  name = "pam-oath-login";
+
+  machine =
+    { config, pkgs, lib, ... }:
+    {
+      security.pam.oath = {
+        enable = true;
+      };
+
+      users.extraUsers.alice = {
+        isNormalUser = true;
+        name = "alice";
+        uid = 1000;
+        hashedPassword = hashedAlicePassword;
+        extraGroups = [ "wheel" ];
+        createHome = true;
+        home = "/home/alice";
+      };
+
+
+      systemd.services.setupOathSnakeoilFile = {
+        wantedBy = [ "default.target" ];
+        before = [ "default.target" ];
+        unitConfig = {
+          type = "oneshot";
+          RemainAfterExit = true;
+        };
+        script = ''
+          touch /etc/users.oath
+          chmod 600 /etc/users.oath
+          chown root /etc/users.oath
+          echo "HOTP/E/6 alice - ${oathSnakeoilSecret}" > /etc/users.oath
+        '';
+      };
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit('multi-user.target');
+      $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'");
+      $machine->screenshot("postboot");
+
+
+      subtest "Invalid password", sub {
+        $machine->fail("pgrep -f 'agetty.*tty2'");
+        $machine->sendKeys("alt-f2");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]");
+        $machine->waitForUnit('getty@tty2.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'");
+
+        $machine->waitUntilTTYMatches(2, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(2, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+
+        $machine->waitUntilTTYMatches(2, "One-time password");
+        $machine->sendChars("${oathSnakeOilPassword1}\n");
+        $machine->waitUntilTTYMatches(2, "Password: ");
+        $machine->sendChars("blorg\n");
+        $machine->waitUntilTTYMatches(2, "Login incorrect");
+      };
+
+      subtest "Invalid oath token", sub {
+        $machine->fail("pgrep -f 'agetty.*tty3'");
+        $machine->sendKeys("alt-f3");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 3 ]");
+        $machine->waitForUnit('getty@tty3.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty3'");
+
+        $machine->waitUntilTTYMatches(3, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(3, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+        $machine->waitUntilTTYMatches(3, "One-time password");
+        $machine->sendChars("000000\n");
+        $machine->waitUntilTTYMatches(3, "Login incorrect");
+        $machine->waitUntilTTYMatches(3, "login:");
+      };
+
+      subtest "Happy path (both passwords are mandatory to get us in)", sub {
+        $machine->fail("pgrep -f 'agetty.*tty4'");
+        $machine->sendKeys("alt-f4");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 4 ]");
+        $machine->waitForUnit('getty@tty4.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty4'");
+
+        $machine->waitUntilTTYMatches(4, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(4, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+        $machine->waitUntilTTYMatches(4, "One-time password");
+        $machine->sendChars("${oathSnakeOilPassword2}\n");
+        $machine->waitUntilTTYMatches(4, "Password: ");
+        $machine->sendChars("${alicePassword}\n");
+
+        $machine->waitUntilSucceeds("pgrep -u alice bash");
+        $machine->sendChars("touch  done4\n");
+        $machine->waitForFile("/home/alice/done4");
+      };
+
+    '';
+
+})
diff --git a/nixos/tests/pgjwt.nix b/nixos/tests/pgjwt.nix
new file mode 100644
index 000000000000..2cf2963ae316
--- /dev/null
+++ b/nixos/tests/pgjwt.nix
@@ -0,0 +1,42 @@
+import ./make-test.nix ({ pkgs, ...} : 
+let
+  test = pkgs.writeText "test.sql" ''
+    CREATE EXTENSION pgcrypto;
+    CREATE EXTENSION pgjwt;
+    select sign('{"sub":"1234567890","name":"John Doe","admin":true}', 'secret');
+    select * from verify('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ', 'secret');
+  '';
+in
+{
+  name = "pgjwt";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ spinus ];
+  };
+
+  nodes = {
+    master =
+      { pkgs, config, ... }:
+
+      {
+        services.postgresql = let mypg = pkgs.postgresql95; in {
+            enable = true;
+            package = mypg;
+            extraPlugins =[pkgs.pgjwt];
+            initialScript =  pkgs.writeText "postgresql-init.sql"
+          ''
+          CREATE ROLE postgres WITH superuser login createdb;
+          '';
+          };
+      };
+  };
+
+  testScript = ''
+    startAll;
+    $master->waitForUnit("postgresql");
+    $master->succeed("timeout 10 bash -c 'while ! psql postgres -c \"SELECT 1;\";do sleep 1;done;'");
+    $master->succeed("cat ${test} | psql postgres");
+    # I can't make original test working :[
+    # $master->succeed("${pkgs.perlPackages.TAPParserSourceHandlerpgTAP}/bin/pg_prove -d postgres ${pkgs.pgjwt.src}/test.sql");
+
+  '';
+})
diff --git a/nixos/tests/phabricator.nix b/nixos/tests/phabricator.nix
index 3bf83ab66655..fdc39393faea 100644
--- a/nixos/tests/phabricator.nix
+++ b/nixos/tests/phabricator.nix
@@ -54,7 +54,7 @@ import ./make-test.nix ({ pkgs, ... }: {
     client =
       { config, pkgs, ... }:
       { imports = [ ./common/x11.nix ];
-        services.xserver.desktopManager.kde4.enable = true;
+        services.xserver.desktopManager.plasma5.enable = true;
       };
   };
 
diff --git a/nixos/tests/kde5.nix b/nixos/tests/plasma5.nix
index 2b61d6f3f0a1..f561fc8c3c4c 100644
--- a/nixos/tests/kde5.nix
+++ b/nixos/tests/plasma5.nix
@@ -1,31 +1,34 @@
 import ./make-test.nix ({ pkgs, ...} :
 
 {
-  name = "kde5";
+  name = "plasma5";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ ttuegel ];
   };
 
   machine = { lib, ... }: {
     imports = [ ./common/user-account.nix ];
-    virtualisation.memorySize = 1024;
     services.xserver.enable = true;
-    services.xserver.displayManager.sddm = {
-      enable = true;
-      autoLogin = {
-        enable = true;
-        user = "alice";
-      };
-    };
-    services.xserver.desktopManager.kde5.enable = true;
-    virtualisation.writableStore = false; # FIXME
+    services.xserver.displayManager.sddm.enable = true;
+    services.xserver.desktopManager.plasma5.enable = true;
+    services.xserver.desktopManager.default = "plasma5";
+    virtualisation.memorySize = 1024;
   };
 
-  testScript = { nodes, ... }:
-  let xdo = "${pkgs.xdotool}/bin/xdotool"; in
-   ''
+  enableOCR = true;
+
+  testScript = { nodes, ... }: let
+    user = nodes.machine.config.users.extraUsers.alice;
+    xdo = "${pkgs.xdotool}/bin/xdotool";
+  in ''
     startAll;
 
+    # Wait for display manager to start
+    $machine->waitForText(qr/${user.description}/);
+    $machine->screenshot("sddm");
+
+    # Log in
+    $machine->sendChars("${user.password}\n");
     $machine->waitForFile("/home/alice/.Xauthority");
     $machine->succeed("xauth merge ~alice/.Xauthority");
 
diff --git a/nixos/tests/postgresql.nix b/nixos/tests/postgresql.nix
index f17384b44ba6..1f4f43a26669 100644
--- a/nixos/tests/postgresql.nix
+++ b/nixos/tests/postgresql.nix
@@ -1,26 +1,46 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "postgresql";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ zagy ];
-  };
-
-  nodes = {
-    master =
-      { pkgs, config, ... }:
+{ system ? builtins.currentSystem }:
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
+let
+  postgresql-versions = pkgs.callPackages ../../pkgs/servers/sql/postgresql { };
+  test-sql = pkgs.writeText "postgresql-test" ''
+    CREATE EXTENSION pgcrypto; -- just to check if lib loading works
+    CREATE TABLE sth (
+      id int
+    );
+    INSERT INTO sth (id) VALUES (1);
+    INSERT INTO sth (id) VALUES (1);
+    INSERT INTO sth (id) VALUES (1);
+    INSERT INTO sth (id) VALUES (1);
+    INSERT INTO sth (id) VALUES (1);
+  '';
+  make-postgresql-test = postgresql-name: postgresql-package: {
+    name = postgresql-name;
+    meta = with pkgs.stdenv.lib.maintainers; {
+      maintainers = [ zagy ];
+    };
 
+    machine = {pkgs, config, ...}:
       {
+        services.postgresql.package=postgresql-package;
         services.postgresql.enable = true;
-        services.postgresql.initialScript =  pkgs.writeText "postgresql-init.sql"
-          ''
-          CREATE ROLE postgres WITH superuser login createdb;
-          '';
       };
-  };
 
-  testScript = ''
-    startAll;
-    $master->waitForUnit("postgresql");
-    $master->sleep(10); # Hopefully this is long enough!!
-    $master->succeed("echo 'select 1' | sudo -u postgres psql");
-  '';
-})
+    testScript = ''
+      $machine->start;
+      $machine->waitForUnit("postgresql");
+      # postgresql should be available just after unit start
+      $machine->succeed("cat ${test-sql} | psql postgres");
+      $machine->shutdown; # make sure that postgresql survive restart (bug #1735)
+      sleep(2);
+      $machine->start;
+      $machine->waitForUnit("postgresql");
+      $machine->fail('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 3');
+      $machine->succeed('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 5');
+      $machine->fail('test $(psql postgres -tAc "SELECT * FROM sth;"|wc -l) -eq 4');
+      $machine->shutdown;
+    '';
+
+  };
+in
+  mapAttrs' (p-name: p-package: {name=p-name; value=make-postgresql-test p-name p-package;}) postgresql-versions
diff --git a/nixos/tests/pump.io.nix b/nixos/tests/pump.io.nix
index 18da52b5134b..0991ed3fce1f 100644
--- a/nixos/tests/pump.io.nix
+++ b/nixos/tests/pump.io.nix
@@ -51,16 +51,6 @@ import ./make-test.nix ({ pkgs, ...} : let
     Vd5WD2HJhLb9u0UxVp9vfWIUDgydopV5ZmWCQ5YvNepb1w==
     -----END CERTIFICATE-----
   '';
-
-  makePump = { opts ? { } }:
-    {
-      enable = true;
-      sslCert = pkgs.writeText "snakeoil.cert" snakeOilCert;
-      sslKey = pkgs.writeText "snakeoil.pem" snakeOilKey;
-      secret = "test";
-      site = "test";
-    } // opts;
-
 in {
   name = "pumpio";
   meta = with pkgs.stdenv.lib.maintainers; {
@@ -72,9 +62,14 @@ in {
       { config, pkgs, ... }:
         {
           services = {
-           pumpio = makePump { opts = {
+           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
diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix
new file mode 100644
index 000000000000..4c2ed8456ddd
--- /dev/null
+++ b/nixos/tests/radicale.nix
@@ -0,0 +1,80 @@
+let
+  port = 5232;
+  radicaleOverlay = self: super: {
+    radicale = super.radicale.overrideAttrs (oldAttrs: {
+      propagatedBuildInputs = with self.pythonPackages;
+        (oldAttrs.propagatedBuildInputs or []) ++ [
+          passlib
+        ];
+    });
+  };
+  common = { config, pkgs, ...}: {
+    services.radicale = {
+      enable = true;
+      config = let home = config.users.extraUsers.radicale.home; in ''
+        [server]
+        hosts = 127.0.0.1:${builtins.toString port}
+        daemon = False
+        [encoding]
+        [well-known]
+        [auth]
+        type = htpasswd
+        htpasswd_filename = /etc/radicale/htpasswd
+        htpasswd_encryption = bcrypt
+        [git]
+        [rights]
+        [storage]
+        type = filesystem
+        filesystem_folder = ${home}/collections
+        [logging]
+        [headers]
+      '';
+    };
+    # WARNING: DON'T DO THIS IN PRODUCTION!
+    # This puts secrets (albeit hashed) directly into the Nix store for ease of testing.
+    environment.etc."radicale/htpasswd".source = with pkgs; let
+      py = python.withPackages(ps: with ps; [ passlib ]);
+    in runCommand "htpasswd" {} ''
+        ${py}/bin/python -c "
+from passlib.apache import HtpasswdFile
+ht = HtpasswdFile(
+    '$out',
+    new=True,
+    default_scheme='bcrypt'
+)
+ht.set_password('someuser', 'really_secret_password')
+ht.save()
+"
+    '';
+  };
+
+in import ./make-test.nix ({ lib, ... }: {
+  name = "radicale";
+  meta.maintainers = with lib.maintainers; [ aneeshusa ];
+
+  # Test radicale with bcrypt-based htpasswd authentication
+  nodes = {
+    py2 = { config, pkgs, ... }@args: (common args) // {
+      nixpkgs.overlays = [
+        radicaleOverlay
+      ];
+    };
+    py3 = { config, pkgs, ... }@args: (common args) // {
+      nixpkgs.overlays = [
+        (self: super: {
+          python = self.python3;
+          pythonPackages = self.python3.pkgs;
+        })
+        radicaleOverlay
+      ];
+    };
+  };
+
+  testScript = ''
+    for my $machine ($py2, $py3) {
+      $machine->waitForUnit('radicale.service');
+      $machine->waitForOpenPort(${builtins.toString port});
+      $machine->succeed('curl -s http://someuser:really_secret_password@127.0.0.1:${builtins.toString port}/someuser/calendar.ics/');
+    }
+  '';
+})
diff --git a/nixos/tests/samba.nix b/nixos/tests/samba.nix
index d6658ef0400b..e446284fc0ef 100644
--- a/nixos/tests/samba.nix
+++ b/nixos/tests/samba.nix
@@ -37,12 +37,11 @@ import ./make-test.nix ({ pkgs, ... }:
   testScript =
     ''
       $server->start;
-      $server->waitForUnit("samba-smbd");
-      $server->waitForUnit("samba-nmbd");
+      $server->waitForUnit("samba.target");
       $server->succeed("mkdir -p /public; echo bar > /public/foo");
 
       $client->start;
-      $client->waitForUnit("network.target");
+      $client->waitForUnit("remote-fs.target");
       $client->succeed("[[ \$(cat /public/foo) = bar ]]");
     '';
 })
diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix
index 041d88fbeae6..82be9bc1d727 100644
--- a/nixos/tests/sddm.nix
+++ b/nixos/tests/sddm.nix
@@ -1,28 +1,66 @@
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "sddm";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ ttuegel ];
-  };
+{ system ? builtins.currentSystem }:
+
+with import ../lib/testing.nix { inherit system; };
+
+let
+  inherit (pkgs) lib;
+
+  tests = {
+    default = {
+      name = "sddm";
 
-  machine = { lib, ... }: {
-    imports = [ ./common/user-account.nix ];
-    services.xserver.enable = true;
-    services.xserver.displayManager.sddm = {
-      enable = true;
-      autoLogin = {
-        enable = true;
-        user = "alice";
+      machine = { lib, ... }: {
+        imports = [ ./common/user-account.nix ];
+        services.xserver.enable = true;
+        services.xserver.displayManager.sddm.enable = true;
+        services.xserver.windowManager.default = "icewm";
+        services.xserver.windowManager.icewm.enable = true;
+        services.xserver.desktopManager.default = "none";
       };
+
+      enableOCR = true;
+
+      testScript = { nodes, ... }: let
+        user = nodes.machine.config.users.extraUsers.alice;
+      in ''
+        startAll;
+        $machine->waitForText(qr/ALICE/);
+        $machine->screenshot("sddm");
+        $machine->sendChars("${user.password}\n");
+        $machine->waitForFile("/home/alice/.Xauthority");
+        $machine->succeed("xauth merge ~alice/.Xauthority");
+        $machine->waitForWindow("^IceWM ");
+      '';
     };
-    services.xserver.windowManager.default = "icewm";
-    services.xserver.windowManager.icewm.enable = true;
-    services.xserver.desktopManager.default = "none";
-  };
 
-  testScript = { nodes, ... }: ''
-    startAll;
-    $machine->waitForFile("/home/alice/.Xauthority");
-    $machine->succeed("xauth merge ~alice/.Xauthority");
-    $machine->waitForWindow("^IceWM ");
-  '';
-})
+    autoLogin = {
+      name = "sddm-autologin";
+      meta = with pkgs.stdenv.lib.maintainers; {
+        maintainers = [ ttuegel ];
+      };
+
+      machine = { lib, ... }: {
+        imports = [ ./common/user-account.nix ];
+        services.xserver.enable = true;
+        services.xserver.displayManager.sddm = {
+          enable = true;
+          autoLogin = {
+            enable = true;
+            user = "alice";
+          };
+        };
+        services.xserver.windowManager.default = "icewm";
+        services.xserver.windowManager.icewm.enable = true;
+        services.xserver.desktopManager.default = "none";
+      };
+
+      testScript = { nodes, ... }: ''
+        startAll;
+        $machine->waitForFile("/home/alice/.Xauthority");
+        $machine->succeed("xauth merge ~alice/.Xauthority");
+        $machine->waitForWindow("^IceWM ");
+      '';
+    };
+  };
+in
+  lib.mapAttrs (lib.const makeTest) tests
diff --git a/nixos/tests/slim.nix b/nixos/tests/slim.nix
new file mode 100644
index 000000000000..7b939d836381
--- /dev/null
+++ b/nixos/tests/slim.nix
@@ -0,0 +1,66 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "slim";
+
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ aszlig ];
+  };
+
+  machine = { pkgs, lib, ... }: {
+    imports = [ ./common/user-account.nix ];
+    services.xserver.enable = true;
+    services.xserver.windowManager.default = "icewm";
+    services.xserver.windowManager.icewm.enable = true;
+    services.xserver.desktopManager.default = "none";
+    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
+      '';
+    };
+  };
+
+  enableOCR = true;
+
+  testScript = { nodes, ... }: let
+    user = nodes.machine.config.users.extraUsers.alice;
+  in ''
+    startAll;
+    $machine->waitForText(qr/Username:/);
+    $machine->sendChars("${user.name}\n");
+    $machine->waitForText(qr/Password:/);
+    $machine->sendChars("${user.password}\n");
+
+    $machine->waitForFile('${user.home}/.Xauthority');
+    $machine->succeed('xauth merge ${user.home}/.Xauthority');
+    $machine->waitForWindow('^IceWM ');
+
+    # Make sure SLiM doesn't create a log file
+    $machine->fail('test -e /var/log/slim.log');
+  '';
+})
diff --git a/nixos/tests/smokeping.nix b/nixos/tests/smokeping.nix
index 9de3030417f9..4c77e4b78613 100644
--- a/nixos/tests/smokeping.nix
+++ b/nixos/tests/smokeping.nix
@@ -14,7 +14,7 @@ import ./make-test.nix ({ pkgs, ...} : {
           mailHost = "127.0.0.2";
           probeConfig = ''
             + FPing
-            binary = /var/setuid-wrappers/fping
+            binary = /run/wrappers/bin/fping
             offset = 0%
           '';
         };
diff --git a/nixos/tests/taskserver.nix b/nixos/tests/taskserver.nix
index d770b20a7757..cdccb11d8887 100644
--- a/nixos/tests/taskserver.nix
+++ b/nixos/tests/taskserver.nix
@@ -1,4 +1,62 @@
-import ./make-test.nix {
+import ./make-test.nix ({ pkgs, ... }: let
+  snakeOil = pkgs.runCommand "snakeoil-certs" {
+    outputs = [ "out" "cacert" "cert" "key" "crl" ];
+    buildInputs = [ pkgs.gnutls.bin ];
+    caTemplate = pkgs.writeText "snakeoil-ca.template" ''
+      cn = server
+      expiration_days = -1
+      cert_signing_key
+      ca
+    '';
+    certTemplate = pkgs.writeText "snakeoil-cert.template" ''
+      cn = server
+      expiration_days = -1
+      tls_www_server
+      encryption_key
+      signing_key
+    '';
+    crlTemplate = pkgs.writeText "snakeoil-crl.template" ''
+      expiration_days = -1
+    '';
+    userCertTemplace = pkgs.writeText "snakoil-user-cert.template" ''
+      organization = snakeoil
+      cn = server
+      expiration_days = -1
+      tls_www_client
+      encryption_key
+      signing_key
+    '';
+  } ''
+    certtool -p --bits 4096 --outfile ca.key
+    certtool -s --template "$caTemplate" --load-privkey ca.key \
+                --outfile "$cacert"
+    certtool -p --bits 4096 --outfile "$key"
+    certtool -c --template "$certTemplate" \
+                --load-ca-privkey ca.key \
+                --load-ca-certificate "$cacert" \
+                --load-privkey "$key" \
+                --outfile "$cert"
+    certtool --generate-crl --template "$crlTemplate" \
+                            --load-ca-privkey ca.key \
+                            --load-ca-certificate "$cacert" \
+                            --outfile "$crl"
+
+    mkdir "$out"
+
+    # Stripping key information before the actual PEM-encoded values is solely
+    # to make test output a bit less verbose when copying the client key to the
+    # actual client.
+    certtool -p --bits 4096 | sed -n \
+      -e '/^----* *BEGIN/,/^----* *END/p' > "$out/alice.key"
+
+    certtool -c --template "$userCertTemplace" \
+                --load-privkey "$out/alice.key" \
+                --load-ca-privkey ca.key \
+                --load-ca-certificate "$cacert" \
+                --outfile "$out/alice.cert"
+  '';
+
+in {
   name = "taskserver";
 
   nodes = rec {
@@ -12,6 +70,23 @@ import ./make-test.nix {
       };
     };
 
+    # New generation of the server with manual config
+    newServer = { lib, nodes, ... }: {
+      imports = [ server ];
+      services.taskserver.pki.manual = {
+        ca.cert = snakeOil.cacert;
+        server.cert = snakeOil.cert;
+        server.key = snakeOil.key;
+        server.crl = snakeOil.crl;
+      };
+      # This is to avoid assigning a different network address to the new
+      # generation.
+      networking = lib.mapAttrs (lib.const lib.mkForce) {
+        inherit (nodes.server.config.networking)
+          hostName interfaces primaryIPAddress extraHosts;
+      };
+    };
+
     client1 = { pkgs, ... }: {
       environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ];
       users.users.alice.isNormalUser = true;
@@ -26,6 +101,8 @@ import ./make-test.nix {
   testScript = { nodes, ... }: let
     cfg = nodes.server.config.services.taskserver;
     portStr = toString cfg.listenPort;
+    newServerSystem = nodes.newServer.config.system.build.toplevel;
+    switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
   in ''
     sub su ($$) {
       my ($user, $cmd) = @_;
@@ -33,8 +110,8 @@ import ./make-test.nix {
       return "su - $user -c '$esc'";
     }
 
-    sub setupClientsFor ($$) {
-      my ($org, $user) = @_;
+    sub setupClientsFor ($$;$) {
+      my ($org, $user, $extraInit) = @_;
 
       for my $client ($client1, $client2) {
         $client->nest("initialize client for user $user", sub {
@@ -58,6 +135,8 @@ import ./make-test.nix {
             }
           });
 
+          eval { &$extraInit($client, $org, $user) };
+
           $client->succeed(su $user,
             "task config taskd.server server:${portStr} >&2"
           );
@@ -104,7 +183,10 @@ import ./make-test.nix {
       return su $user, $cmd;
     }
 
-    startAll;
+    # Explicitly start the VMs so that we don't accidentally start newServer
+    $server->start;
+    $client1->start;
+    $client2->start;
 
     $server->waitForUnit("taskserver.service");
 
@@ -162,5 +244,42 @@ import ./make-test.nix {
       restartServer;
       testSync "bar";
     };
+
+    subtest "check manual configuration", sub {
+      $server->succeed('${switchToNewServer} >&2');
+      $server->waitForUnit("taskserver.service");
+      $server->waitForOpenPort(${portStr});
+
+      $server->succeed(
+        "nixos-taskserver org add manualOrg",
+        "nixos-taskserver user add manualOrg alice"
+      );
+
+      setupClientsFor "manualOrg", "alice", sub {
+        my ($client, $org, $user) = @_;
+        my $cfgpath = "/home/$user/.task";
+
+        $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert");
+        for my $file ('alice.key', 'alice.cert') {
+          $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file");
+        }
+
+        for my $file ("$user.key", "$user.cert") {
+          $client->copyFileFromHost(
+            "${snakeOil}/$file", "$cfgpath/$file"
+          );
+        }
+        $client->copyFileFromHost(
+          "${snakeOil.cacert}", "$cfgpath/ca.cert"
+        );
+        $client->succeed(
+          (su "alice", "task config taskd.ca $cfgpath/ca.cert"),
+          (su "alice", "task config taskd.key $cfgpath/$user.key"),
+          (su $user, "task config taskd.certificate $cfgpath/$user.cert")
+        );
+      };
+
+      testSync "alice";
+    };
   '';
-}
+})
diff --git a/nixos/tests/trac.nix b/nixos/tests/trac.nix
index e7d9759ae0cc..d426bbde68d2 100644
--- a/nixos/tests/trac.nix
+++ b/nixos/tests/trac.nix
@@ -45,7 +45,7 @@ import ./make-test.nix ({ pkgs, ... }: {
     client =
       { config, pkgs, ... }:
       { imports = [ ./common/x11.nix ];
-        services.xserver.desktopManager.kde4.enable = true;
+        services.xserver.desktopManager.plasma5.enable = true;
       };
   };
 
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index 376c4f21dc04..4f7cb176d96f 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -65,7 +65,7 @@ let
       touch /mnt-root/boot-done
       hostname "${vmName}"
       mkdir -p /nix/store
-      unshare -m "@shell@" -c '
+      unshare -m ${escapeShellArg pkgs.stdenv.shell} -c '
         mount -t vboxsf nixstore /nix/store
         exec "$stage2Init"
       '
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
index afee1f7f6dd4..c51306a8c7a0 100644
--- a/nixos/tests/wordpress.nix
+++ b/nixos/tests/wordpress.nix
@@ -10,14 +10,10 @@ import ./make-test.nix ({ pkgs, ... }:
     { web =
         { config, pkgs, ... }:
         {
-          services.mysql.enable = true;
-          services.mysql.package = pkgs.mysql;
-          services.mysql.initialScript = pkgs.writeText "start.sql" ''
-            CREATE DATABASE wordpress;
-	    CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wordpress';
-            GRANT ALL on wordpress.* TO 'wordpress'@'localhost';
-          '';
-
+          services.mysql = {
+            enable = true;
+            package = pkgs.mysql;
+          };
           services.httpd = {
             enable = true;
             logPerVirtualHost = true;
diff --git a/nixos/tests/xrdp.nix b/nixos/tests/xrdp.nix
new file mode 100644
index 000000000000..c997e36cc442
--- /dev/null
+++ b/nixos/tests/xrdp.nix
@@ -0,0 +1,45 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "xrdp";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ volth ];
+  };
+
+  nodes = {
+    server = { lib, pkgs, ... }: {
+      imports = [ ./common/user-account.nix ];
+      services.xrdp.enable = true;
+      services.xrdp.defaultWindowManager = "${pkgs.xterm}/bin/xterm";
+      networking.firewall.allowedTCPPorts = [ 3389 ];
+    };
+
+    client = { lib, pkgs, ... }: {
+      imports = [ ./common/x11.nix ./common/user-account.nix ];
+      services.xserver.displayManager.auto.user = "alice";
+      environment.systemPackages = [ pkgs.freerdp ];
+      services.xrdp.enable = true;
+      services.xrdp.defaultWindowManager = "${pkgs.icewm}/bin/icewm";
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    startAll;
+
+    $client->waitForX;
+    $client->waitForFile("/home/alice/.Xauthority");
+    $client->succeed("xauth merge ~alice/.Xauthority");
+
+    $client->sleep(5);
+
+    $client->execute("xterm &");
+    $client->sleep(1);
+    $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:127.0.0.1 /u:alice /p:foobar\n");
+    $client->sleep(5);
+    $client->screenshot("localrdp");
+
+    $client->execute("xterm &");
+    $client->sleep(1);
+    $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:server /u:alice /p:foobar\n");
+    $client->sleep(5);
+    $client->screenshot("remoterdp");
+  '';
+})