summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorParnell Springmeyer <parnell@digitalmentat.com>2017-01-25 11:08:05 -0800
committerParnell Springmeyer <parnell@digitalmentat.com>2017-01-25 11:08:05 -0800
commitbae00e8aa8f3faff90e28e19cd5074b8c26d0d0e (patch)
tree56abaf30f11ad2f24b9fb7729f74c5fff50fbd93 /nixos
parent1f9494b752082ec3ac048e56d1c6364a2e23a675 (diff)
parent104c3db6594043dbb81005303f055b02145305a5 (diff)
downloadnixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.gz
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.bz2
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.lz
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.xz
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.tar.zst
nixpkgs-bae00e8aa8f3faff90e28e19cd5074b8c26d0d0e.zip
setcap-wrapper: Merging with upstream master and resolving conflicts
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/administration/container-networking.xml8
-rw-r--r--nixos/doc/manual/configuration/customizing-packages.xml27
-rw-r--r--nixos/doc/manual/configuration/ipv6-config.xml9
-rw-r--r--nixos/doc/manual/configuration/modularity.xml2
-rw-r--r--nixos/doc/manual/configuration/network-manager.xml18
-rw-r--r--nixos/doc/manual/default.nix77
-rw-r--r--nixos/doc/manual/development/development.xml2
-rw-r--r--nixos/doc/manual/development/option-declarations.xml160
-rw-r--r--nixos/doc/manual/development/option-types.xml446
-rwxr-xr-xnixos/doc/manual/development/releases.xml241
-rw-r--r--nixos/doc/manual/development/writing-documentation.xml147
-rw-r--r--nixos/doc/manual/development/writing-modules.xml1
-rw-r--r--nixos/doc/manual/installation/obtaining.xml2
-rw-r--r--nixos/doc/manual/installation/upgrading.xml5
-rw-r--r--nixos/doc/manual/man-nixos-rebuild.xml2
-rw-r--r--nixos/doc/manual/release-notes/release-notes.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-1509.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-1609.xml164
-rw-r--r--nixos/doc/manual/release-notes/rl-1703.xml106
-rw-r--r--nixos/lib/build-vms.nix3
-rw-r--r--nixos/lib/eval-config.nix2
-rw-r--r--nixos/lib/make-disk-image.nix21
-rw-r--r--nixos/lib/make-squashfs.nix2
-rw-r--r--nixos/lib/make-system-tarball.sh5
-rw-r--r--nixos/lib/test-driver/Machine.pm55
-rw-r--r--nixos/lib/test-driver/test-driver.pl6
-rw-r--r--nixos/lib/testing.nix25
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh10
-rw-r--r--nixos/modules/config/debug-info.nix6
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix12
-rw-r--r--nixos/modules/config/fonts/fonts.nix12
-rw-r--r--nixos/modules/config/gnu.nix10
-rw-r--r--nixos/modules/config/i18n.nix3
-rw-r--r--nixos/modules/config/networking.nix40
-rw-r--r--nixos/modules/config/nsswitch.nix48
-rw-r--r--nixos/modules/config/power-management.nix2
-rw-r--r--nixos/modules/config/pulseaudio.nix18
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/config/system-environment.nix2
-rw-r--r--nixos/modules/config/system-path.nix7
-rw-r--r--nixos/modules/config/timezone.nix5
-rw-r--r--nixos/modules/config/users-groups.nix60
-rw-r--r--nixos/modules/hardware/opengl.nix8
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix54
-rw-r--r--nixos/modules/hardware/video/bumblebee.nix93
-rw-r--r--nixos/modules/hardware/video/displaylink.nix61
-rw-r--r--nixos/modules/i18n/input-method/default.nix29
-rw-r--r--nixos/modules/i18n/input-method/default.xml12
-rw-r--r--nixos/modules/i18n/input-method/fcitx.nix7
-rw-r--r--nixos/modules/i18n/input-method/ibus.nix9
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix95
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-minimal.nix8
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix1
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix1
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix2
-rw-r--r--nixos/modules/installer/netboot/netboot.nix30
-rw-r--r--nixos/modules/installer/tools/auto-upgrade.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix5
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl20
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-option.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh10
-rw-r--r--nixos/modules/installer/tools/tools.nix14
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/ids.nix42
-rw-r--r--nixos/modules/misc/locate.nix11
-rw-r--r--nixos/modules/misc/version.nix4
-rw-r--r--nixos/modules/module-list.nix61
-rw-r--r--nixos/modules/profiles/base.nix8
-rw-r--r--nixos/modules/profiles/installation-device.nix6
-rw-r--r--nixos/modules/profiles/minimal.nix5
-rw-r--r--nixos/modules/programs/adb.nix30
-rw-r--r--nixos/modules/programs/bash/bash.nix2
-rw-r--r--nixos/modules/programs/gphoto2.nix31
-rw-r--r--nixos/modules/programs/info.nix30
-rw-r--r--nixos/modules/programs/java.nix58
-rw-r--r--nixos/modules/programs/mosh.nix16
-rw-r--r--nixos/modules/programs/oblogout.nix160
-rw-r--r--nixos/modules/programs/shadow.nix1
-rw-r--r--nixos/modules/programs/ssh.nix4
-rw-r--r--nixos/modules/programs/vim.nix24
-rw-r--r--nixos/modules/programs/zsh/zsh.nix37
-rw-r--r--nixos/modules/rename.nix28
-rw-r--r--nixos/modules/security/acme.nix8
-rw-r--r--nixos/modules/security/acme.xml41
-rw-r--r--nixos/modules/security/apparmor.nix18
-rw-r--r--nixos/modules/security/audit.nix6
-rw-r--r--nixos/modules/security/ca.nix28
-rw-r--r--nixos/modules/security/duosec.nix12
-rw-r--r--nixos/modules/security/grsecurity.nix85
-rw-r--r--nixos/modules/security/grsecurity.xml82
-rw-r--r--nixos/modules/security/hidepid.nix26
-rw-r--r--nixos/modules/security/hidepid.xml33
-rw-r--r--nixos/modules/security/pam.nix45
-rw-r--r--nixos/modules/security/permissions-wrappers/setuid-wrappers.nix3
-rw-r--r--nixos/modules/security/rngd.nix2
-rw-r--r--nixos/modules/security/sudo.nix2
-rw-r--r--nixos/modules/services/audio/alsa.nix41
-rw-r--r--nixos/modules/services/audio/mopidy.nix8
-rw-r--r--nixos/modules/services/audio/mpd.nix20
-rw-r--r--nixos/modules/services/audio/ympd.nix57
-rw-r--r--nixos/modules/services/backup/bacula.nix10
-rw-r--r--nixos/modules/services/backup/crashplan.nix2
-rw-r--r--nixos/modules/services/backup/rsnapshot.nix3
-rw-r--r--nixos/modules/services/backup/tarsnap.nix156
-rw-r--r--nixos/modules/services/cluster/fleet.nix2
-rw-r--r--nixos/modules/services/cluster/kubernetes.nix598
-rw-r--r--nixos/modules/services/cluster/panamax.nix2
-rw-r--r--nixos/modules/services/computing/boinc/client.nix88
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix250
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix51
-rw-r--r--nixos/modules/services/continuous-integration/gocd-agent/default.nix3
-rw-r--r--nixos/modules/services/continuous-integration/gocd-server/default.nix15
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix2
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix4
-rw-r--r--nixos/modules/services/databases/4store.nix3
-rw-r--r--nixos/modules/services/databases/cassandra.nix2
-rw-r--r--nixos/modules/services/databases/couchdb.nix2
-rw-r--r--nixos/modules/services/databases/influxdb.nix15
-rw-r--r--nixos/modules/services/databases/monetdb.nix88
-rw-r--r--nixos/modules/services/databases/mongodb.nix18
-rw-r--r--nixos/modules/services/databases/mysql.nix17
-rw-r--r--nixos/modules/services/databases/neo4j.nix94
-rw-r--r--nixos/modules/services/databases/openldap.nix9
-rw-r--r--nixos/modules/services/databases/postgresql.nix4
-rw-r--r--nixos/modules/services/databases/riak-cs.nix202
-rw-r--r--nixos/modules/services/databases/riak.nix17
-rw-r--r--nixos/modules/services/databases/stanchion.nix212
-rw-r--r--nixos/modules/services/databases/virtuoso.nix3
-rw-r--r--nixos/modules/services/desktops/accountsservice.nix8
-rw-r--r--nixos/modules/services/desktops/gnome3/evolution-data-server.nix2
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix44
-rw-r--r--nixos/modules/services/desktops/gnome3/gvfs.nix2
-rw-r--r--nixos/modules/services/desktops/gnome3/tracker.nix2
-rw-r--r--nixos/modules/services/desktops/profile-sync-daemon.nix6
-rw-r--r--nixos/modules/services/editors/emacs.nix10
-rw-r--r--nixos/modules/services/editors/emacs.xml79
-rw-r--r--nixos/modules/services/editors/infinoted.nix158
-rw-r--r--nixos/modules/services/games/ghost-one.nix3
-rw-r--r--nixos/modules/services/games/terraria.nix6
-rw-r--r--nixos/modules/services/hardware/brltty.nix4
-rw-r--r--nixos/modules/services/hardware/pommed.nix36
-rw-r--r--nixos/modules/services/hardware/sane.nix111
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan4.nix5
-rw-r--r--nixos/modules/services/hardware/tlp.nix2
-rw-r--r--nixos/modules/services/hardware/udev.nix26
-rw-r--r--nixos/modules/services/logging/logcheck.nix72
-rw-r--r--nixos/modules/services/logging/syslogd.nix6
-rw-r--r--nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixos/modules/services/mail/freepops.nix3
-rw-r--r--nixos/modules/services/mail/offlineimap.nix (renamed from nixos/modules/services/networking/offlineimap.nix)2
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix20
-rw-r--r--nixos/modules/services/mail/postgrey.nix194
-rw-r--r--nixos/modules/services/mail/postsrsd.nix34
-rw-r--r--nixos/modules/services/mail/rmilter.nix2
-rw-r--r--nixos/modules/services/misc/apache-kafka.nix2
-rw-r--r--nixos/modules/services/misc/autofs.nix2
-rw-r--r--nixos/modules/services/misc/bepasty.nix2
-rwxr-xr-x[-rw-r--r--]nixos/modules/services/misc/confd.nix2
-rw-r--r--nixos/modules/services/misc/dictd.nix13
-rw-r--r--nixos/modules/services/misc/disnix.nix1
-rw-r--r--nixos/modules/services/misc/docker-registry.nix33
-rw-r--r--nixos/modules/services/misc/emby.nix2
-rw-r--r--nixos/modules/services/misc/errbot.nix101
-rw-r--r--nixos/modules/services/misc/etcd.nix75
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix26
-rw-r--r--nixos/modules/services/misc/leaps.nix62
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix133
-rw-r--r--nixos/modules/services/misc/mesos-master.nix2
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix2
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix8
-rw-r--r--nixos/modules/services/misc/nix-gc.nix2
-rw-r--r--nixos/modules/services/misc/nix-optimise.nix49
-rw-r--r--nixos/modules/services/misc/nscd-sssd.conf36
-rw-r--r--nixos/modules/services/misc/parsoid.nix40
-rw-r--r--nixos/modules/services/misc/plex.nix15
-rw-r--r--nixos/modules/services/misc/redmine.nix2
-rw-r--r--nixos/modules/services/misc/rippled.nix78
-rw-r--r--nixos/modules/services/misc/sssd.nix97
-rw-r--r--nixos/modules/services/misc/svnserve.nix2
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix6
-rw-r--r--nixos/modules/services/misc/zookeeper.nix2
-rw-r--r--nixos/modules/services/monitoring/bosun.nix2
-rw-r--r--nixos/modules/services/monitoring/cadvisor.nix1
-rw-r--r--nixos/modules/services/monitoring/collectd.nix5
-rw-r--r--nixos/modules/services/monitoring/graphite.nix18
-rw-r--r--nixos/modules/services/monitoring/monit.nix13
-rw-r--r--nixos/modules/services/monitoring/munin.nix1
-rw-r--r--nixos/modules/services/monitoring/nagios.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix116
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix67
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix465
-rw-r--r--nixos/modules/services/monitoring/prometheus/json-exporter.nix74
-rw-r--r--nixos/modules/services/monitoring/prometheus/nginx-exporter.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/node-exporter.nix81
-rw-r--r--nixos/modules/services/monitoring/prometheus/snmp-exporter.nix127
-rw-r--r--nixos/modules/services/monitoring/prometheus/varnish-exporter.nix61
-rw-r--r--nixos/modules/services/monitoring/riemann-tools.nix1
-rw-r--r--nixos/modules/services/monitoring/smartd.nix3
-rw-r--r--nixos/modules/services/monitoring/telegraf.nix71
-rw-r--r--nixos/modules/services/monitoring/ups.nix10
-rw-r--r--nixos/modules/services/monitoring/zabbix-agent.nix1
-rw-r--r--nixos/modules/services/network-filesystems/cachefilesd.nix59
-rw-r--r--nixos/modules/services/network-filesystems/drbd.nix4
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix125
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix2
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix13
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix336
-rw-r--r--nixos/modules/services/network-filesystems/xtreemfs.nix5
-rw-r--r--nixos/modules/services/network-filesystems/yandex-disk.nix13
-rw-r--r--nixos/modules/services/networking/amuled.nix3
-rw-r--r--nixos/modules/services/networking/asterisk.nix60
-rw-r--r--nixos/modules/services/networking/atftpd.nix24
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix30
-rw-r--r--nixos/modules/services/networking/bind.nix8
-rw-r--r--nixos/modules/services/networking/bird.nix123
-rw-r--r--nixos/modules/services/networking/bitlbee.nix7
-rw-r--r--nixos/modules/services/networking/chrony.nix5
-rw-r--r--nixos/modules/services/networking/cjdns-hosts.sh11
-rw-r--r--nixos/modules/services/networking/cjdns.nix79
-rw-r--r--nixos/modules/services/networking/cntlm.nix1
-rw-r--r--nixos/modules/services/networking/connman.nix12
-rw-r--r--nixos/modules/services/networking/dante.nix61
-rw-r--r--nixos/modules/services/networking/ddclient.nix19
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix26
-rw-r--r--nixos/modules/services/networking/dhcpd.nix10
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix130
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.xml13
-rw-r--r--nixos/modules/services/networking/docker-registry-server.nix98
-rw-r--r--nixos/modules/services/networking/fakeroute.nix63
-rw-r--r--nixos/modules/services/networking/ferm.nix1
-rw-r--r--nixos/modules/services/networking/firewall.nix17
-rw-r--r--nixos/modules/services/networking/flannel.nix154
-rw-r--r--nixos/modules/services/networking/git-daemon.nix3
-rw-r--r--nixos/modules/services/networking/gvpe.nix2
-rw-r--r--nixos/modules/services/networking/hostapd.nix7
-rw-r--r--nixos/modules/services/networking/htpdate.nix80
-rw-r--r--nixos/modules/services/networking/i2pd.nix285
-rw-r--r--nixos/modules/services/networking/iodine.nix6
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/builder.sh4
-rw-r--r--nixos/modules/services/networking/kippo.nix8
-rw-r--r--nixos/modules/services/networking/miredo.nix93
-rw-r--r--nixos/modules/services/networking/mjpg-streamer.nix8
-rw-r--r--nixos/modules/services/networking/murmur.nix6
-rw-r--r--nixos/modules/services/networking/nat.nix32
-rw-r--r--nixos/modules/services/networking/networkmanager.nix14
-rw-r--r--nixos/modules/services/networking/nntp-proxy.nix4
-rw-r--r--nixos/modules/services/networking/nsd.nix62
-rw-r--r--nixos/modules/services/networking/ntpd.nix10
-rw-r--r--nixos/modules/services/networking/oidentd.nix2
-rw-r--r--nixos/modules/services/networking/openfire.nix4
-rw-r--r--nixos/modules/services/networking/openntpd.nix2
-rw-r--r--nixos/modules/services/networking/openvpn.nix80
-rw-r--r--nixos/modules/services/networking/powerdns.nix49
-rw-r--r--nixos/modules/services/networking/prayer.nix1
-rw-r--r--nixos/modules/services/networking/privoxy.nix23
-rw-r--r--nixos/modules/services/networking/prosody.nix4
-rw-r--r--nixos/modules/services/networking/quagga.nix187
-rw-r--r--nixos/modules/services/networking/quassel.nix12
-rw-r--r--nixos/modules/services/networking/radicale.nix7
-rw-r--r--nixos/modules/services/networking/skydns.nix2
-rw-r--r--nixos/modules/services/networking/smokeping.nix240
-rw-r--r--nixos/modules/services/networking/softether.nix16
-rw-r--r--nixos/modules/services/networking/ssh/lshd.nix2
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix86
-rw-r--r--nixos/modules/services/networking/supplicant.nix206
-rw-r--r--nixos/modules/services/networking/syncthing.nix115
-rw-r--r--nixos/modules/services/networking/tcpcrypt.nix2
-rw-r--r--nixos/modules/services/networking/tftpd.nix3
-rw-r--r--nixos/modules/services/networking/tinc.nix174
-rw-r--r--nixos/modules/services/networking/toxvpn.nix2
-rw-r--r--nixos/modules/services/networking/unbound.nix35
-rw-r--r--nixos/modules/services/networking/vsftpd.nix11
-rw-r--r--nixos/modules/services/networking/wicd.nix4
-rw-r--r--nixos/modules/services/networking/wireguard.nix231
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix21
-rw-r--r--nixos/modules/services/networking/xinetd.nix128
-rw-r--r--nixos/modules/services/networking/zerobin.nix6
-rw-r--r--nixos/modules/services/networking/znc.nix173
-rw-r--r--nixos/modules/services/printing/cupsd.nix9
-rw-r--r--nixos/modules/services/scheduling/chronos.nix2
-rw-r--r--nixos/modules/services/scheduling/marathon.nix2
-rw-r--r--nixos/modules/services/search/elasticsearch.nix2
-rw-r--r--nixos/modules/services/search/hound.nix125
-rw-r--r--nixos/modules/services/search/kibana.nix2
-rw-r--r--nixos/modules/services/security/clamav.nix95
-rw-r--r--nixos/modules/services/security/fail2ban.nix2
-rw-r--r--nixos/modules/services/security/haveged.nix18
-rw-r--r--nixos/modules/services/security/oauth2_proxy.nix2
-rw-r--r--nixos/modules/services/system/cgmanager.nix27
-rw-r--r--nixos/modules/services/system/dbus.nix62
-rw-r--r--nixos/modules/services/system/nscd.nix13
-rw-r--r--nixos/modules/services/torrent/deluge.nix57
-rw-r--r--nixos/modules/services/torrent/flexget.nix6
-rw-r--r--nixos/modules/services/torrent/opentracker.nix45
-rw-r--r--nixos/modules/services/torrent/peerflix.nix2
-rw-r--r--nixos/modules/services/ttys/agetty.nix3
-rw-r--r--nixos/modules/services/web-apps/atlassian/confluence.nix141
-rw-r--r--nixos/modules/services/web-apps/atlassian/crowd.nix156
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix149
-rw-r--r--nixos/modules/services/web-apps/nixbot.nix149
-rw-r--r--nixos/modules/services/web-apps/quassel-webserver.nix101
-rw-r--r--nixos/modules/services/web-apps/selfoss.nix166
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix158
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix62
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mediawiki.nix7
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/moodle.nix5
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix25
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/trac.nix3
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/wordpress.nix13
-rw-r--r--nixos/modules/services/web-servers/fcgiwrap.nix2
-rw-r--r--nixos/modules/services/web-servers/lighttpd/inginious.nix3
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix48
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix4
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix35
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix18
-rw-r--r--nixos/modules/services/web-servers/uwsgi.nix1
-rw-r--r--nixos/modules/services/web-servers/winstone.nix3
-rw-r--r--nixos/modules/services/web-servers/zope2.nix3
-rw-r--r--nixos/modules/services/x11/compton.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix3
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix10
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde4.nix37
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde5.nix400
-rw-r--r--nixos/modules/services/x11/desktop-managers/lumina.nix52
-rw-r--r--nixos/modules/services/x11/desktop-managers/lxqt.nix66
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix3
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix18
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix12
-rw-r--r--nixos/modules/services/x11/display-managers/kdm.nix24
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix9
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix9
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix16
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix8
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix4
-rw-r--r--nixos/modules/services/x11/hardware/wacom.nix4
-rw-r--r--nixos/modules/services/x11/unclutter-xfixes.nix58
-rw-r--r--nixos/modules/services/x11/urxvtd.nix50
-rw-r--r--nixos/modules/services/x11/window-managers/awesome.nix9
-rw-r--r--nixos/modules/services/x11/window-managers/bspwm.nix83
-rw-r--r--nixos/modules/services/x11/window-managers/i3.nix50
-rw-r--r--nixos/modules/services/x11/xserver.nix43
-rw-r--r--nixos/modules/system/activation/activation-script.nix31
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl40
-rw-r--r--nixos/modules/system/activation/top-level.nix2
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix14
-rw-r--r--nixos/modules/system/boot/kernel.nix26
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix4
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix151
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl28
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix7
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py111
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix2
-rw-r--r--nixos/modules/system/boot/luksroot.nix304
-rw-r--r--nixos/modules/system/boot/networkd.nix97
-rw-r--r--nixos/modules/system/boot/plymouth.nix4
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh5
-rw-r--r--nixos/modules/system/boot/stage-1.nix27
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh12
-rw-r--r--nixos/modules/system/boot/systemd-lib.nix2
-rw-r--r--nixos/modules/system/boot/systemd-nspawn.nix122
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix25
-rw-r--r--nixos/modules/system/boot/systemd.nix108
-rw-r--r--nixos/modules/system/boot/timesyncd.nix23
-rw-r--r--nixos/modules/system/etc/etc.nix8
-rw-r--r--nixos/modules/tasks/filesystems.nix15
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix16
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix50
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix108
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces.nix555
-rw-r--r--nixos/modules/testing/test-instrumentation.nix9
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix3
-rw-r--r--nixos/modules/virtualisation/azure-agent.nix3
-rw-r--r--nixos/modules/virtualisation/azure-bootstrap-blobs.nix3
-rw-r--r--nixos/modules/virtualisation/azure-common.nix2
-rw-r--r--nixos/modules/virtualisation/brightbox-image.nix8
-rw-r--r--nixos/modules/virtualisation/containers.nix301
-rw-r--r--nixos/modules/virtualisation/docker.nix121
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix52
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix8
-rw-r--r--nixos/modules/virtualisation/grow-partition.nix2
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix2
-rw-r--r--nixos/modules/virtualisation/lxc.nix6
-rw-r--r--nixos/modules/virtualisation/lxcfs.nix49
-rw-r--r--nixos/modules/virtualisation/nova-image.nix2
-rw-r--r--nixos/modules/virtualisation/openstack/common.nix84
-rw-r--r--nixos/modules/virtualisation/openstack/glance.nix245
-rw-r--r--nixos/modules/virtualisation/openstack/keystone.nix220
-rw-r--r--nixos/modules/virtualisation/parallels-guest.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix38
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix9
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix2
-rw-r--r--nixos/modules/virtualisation/vmware-guest.nix10
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix18
-rw-r--r--nixos/modules/virtualisation/xen-domU.nix2
-rw-r--r--nixos/release-combined.nix2
-rw-r--r--nixos/release.nix17
-rw-r--r--nixos/tests/blivet.nix3
-rw-r--r--nixos/tests/boot-stage1.nix4
-rw-r--r--nixos/tests/cadvisor.nix10
-rw-r--r--nixos/tests/chromium.nix2
-rw-r--r--nixos/tests/cjdns.nix2
-rw-r--r--nixos/tests/containers-hosts.nix52
-rw-r--r--nixos/tests/containers-macvlans.nix82
-rw-r--r--nixos/tests/containers-physical_interfaces.nix133
-rw-r--r--nixos/tests/containers-tmpfs.nix79
-rw-r--r--nixos/tests/dnscrypt-proxy.nix2
-rw-r--r--nixos/tests/docker-registry.nix4
-rw-r--r--nixos/tests/docker.nix3
-rw-r--r--nixos/tests/ecryptfs.nix21
-rw-r--r--nixos/tests/etcd-cluster.nix157
-rw-r--r--nixos/tests/etcd.nix108
-rw-r--r--nixos/tests/ferm.nix72
-rw-r--r--nixos/tests/firewall.nix53
-rw-r--r--nixos/tests/flannel.nix55
-rw-r--r--nixos/tests/glance.nix77
-rw-r--r--nixos/tests/gnome3.nix2
-rw-r--r--nixos/tests/grsecurity.nix20
-rw-r--r--nixos/tests/hibernate.nix4
-rw-r--r--nixos/tests/hound.nix58
-rw-r--r--nixos/tests/influxdb.nix12
-rw-r--r--nixos/tests/ipfs.nix37
-rw-r--r--nixos/tests/kde5.nix (renamed from nixos/tests/sddm-kde5.nix)38
-rw-r--r--nixos/tests/keystone.nix82
-rw-r--r--nixos/tests/kubernetes.nix550
-rw-r--r--nixos/tests/leaps.nix29
-rw-r--r--nixos/tests/login.nix5
-rw-r--r--nixos/tests/logstash.nix2
-rw-r--r--nixos/tests/mongodb.nix34
-rw-r--r--nixos/tests/mpich.nix41
-rw-r--r--nixos/tests/mumble.nix11
-rw-r--r--nixos/tests/networking.nix18
-rw-r--r--nixos/tests/openssh.nix23
-rw-r--r--nixos/tests/postgis.nix30
-rw-r--r--nixos/tests/prometheus.nix29
-rw-r--r--nixos/tests/pump.io.nix4
-rw-r--r--nixos/tests/quagga.nix97
-rw-r--r--nixos/tests/samba.nix48
-rw-r--r--nixos/tests/sddm.nix2
-rw-r--r--nixos/tests/smokeping.nix4
-rwxr-xr-xnixos/tests/test-config-examples.sh14
-rw-r--r--nixos/tests/tomcat.nix5
-rw-r--r--nixos/tests/virtualbox.nix29
-rw-r--r--nixos/tests/wordpress.nix60
449 files changed, 15461 insertions, 4560 deletions
diff --git a/nixos/doc/manual/administration/container-networking.xml b/nixos/doc/manual/administration/container-networking.xml
index 1b1576d3bab..d89d262eff4 100644
--- a/nixos/doc/manual/administration/container-networking.xml
+++ b/nixos/doc/manual/administration/container-networking.xml
@@ -47,4 +47,12 @@ where <literal>eth0</literal> should be replaced with the desired
 external interface. Note that <literal>ve-+</literal> is a wildcard
 that matches all container interfaces.</para>
 
+<para>If you are using Network Manager, you need to explicitly prevent
+it from managing container interfaces:
+
+<programlisting>
+networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
+</programlisting>
+</para>
+
 </section>
diff --git a/nixos/doc/manual/configuration/customizing-packages.xml b/nixos/doc/manual/configuration/customizing-packages.xml
index 6ee7a95dc6f..8aa01fb57a0 100644
--- a/nixos/doc/manual/configuration/customizing-packages.xml
+++ b/nixos/doc/manual/configuration/customizing-packages.xml
@@ -42,29 +42,30 @@ construction, so without them,
 elements.)</para>
 
 <para>Even greater customisation is possible using the function
-<varname>overrideDerivation</varname>.  While the
+<varname>overrideAttrs</varname>.  While the
 <varname>override</varname> mechanism above overrides the arguments of
-a package function, <varname>overrideDerivation</varname> allows
-changing the <emphasis>result</emphasis> of the function.  This
-permits changing any aspect of the package, such as the source code.
+a package function, <varname>overrideAttrs</varname> allows
+changing the <emphasis>attributes</emphasis> passed to <literal>mkDerivation</literal>.
+This permits changing any aspect of the package, such as the source code.
 For instance, if you want to override the source code of Emacs, you
 can say:
 
 <programlisting>
-environment.systemPackages =
-  [ (pkgs.lib.overrideDerivation pkgs.emacs (attrs: {
-      name = "emacs-25.0-pre";
-      src = /path/to/my/emacs/tree;
-    }))
-  ];
+environment.systemPackages = [
+  (pkgs.emacs.overrideAttrs (oldAttrs: {
+    name = "emacs-25.0-pre";
+    src = /path/to/my/emacs/tree;
+  }))
+];
 </programlisting>
 
-Here, <varname>overrideDerivation</varname> takes the Nix derivation
+Here, <varname>overrideAttrs</varname> takes the Nix derivation
 specified by <varname>pkgs.emacs</varname> and produces a new
 derivation in which the original’s <literal>name</literal> and
 <literal>src</literal> attribute have been replaced by the given
-values.  The original attributes are accessible via
-<varname>attrs</varname>.</para>
+values by re-calling <literal>stdenv.mkDerivation</literal>.
+The original attributes are accessible via the function argument,
+which is conventionally named <varname>oldAttrs</varname>.</para>
 
 <para>The overrides shown above are not global.  They do not affect
 the original package; other packages in Nixpkgs continue to depend on
diff --git a/nixos/doc/manual/configuration/ipv6-config.xml b/nixos/doc/manual/configuration/ipv6-config.xml
index 592bf20e545..bf86926f9bf 100644
--- a/nixos/doc/manual/configuration/ipv6-config.xml
+++ b/nixos/doc/manual/configuration/ipv6-config.xml
@@ -12,8 +12,15 @@ can disable IPv6 support globally by setting:
 
 <programlisting>
 networking.enableIPv6 = false;
-</programlisting>
+</programlisting></para>
+
+<para>You can disable IPv6 on a single interface using a normal sysctl (in this
+example, we use interface <varname>eth0</varname>):
 
+<programlisting>
+boot.kernel.sysctl."net.ipv6.conf.eth0.disable_ipv6" = true;
+</programlisting>
 </para>
 
+
 </section>
diff --git a/nixos/doc/manual/configuration/modularity.xml b/nixos/doc/manual/configuration/modularity.xml
index d95091bd162..59a4e3b33ba 100644
--- a/nixos/doc/manual/configuration/modularity.xml
+++ b/nixos/doc/manual/configuration/modularity.xml
@@ -129,7 +129,7 @@ default; run <literal>nix-env -i nix-repl</literal> to get it.  A
 typical use:
 
 <screen>
-$ nix-repl '&lt;nixos>'
+$ nix-repl '&lt;nixpkgs/nixos>'
 
 nix-repl> config.networking.hostName
 "mandark"
diff --git a/nixos/doc/manual/configuration/network-manager.xml b/nixos/doc/manual/configuration/network-manager.xml
index b7e47b8729f..dafbcfcb1e5 100644
--- a/nixos/doc/manual/configuration/network-manager.xml
+++ b/nixos/doc/manual/configuration/network-manager.xml
@@ -16,12 +16,22 @@ networking.networkmanager.enable = true;
 some desktop managers (e.g., GNOME) enable NetworkManager
 automatically for you.</para>
 
-<para>All users that should have permission to change network settings
-must belong to the <code>networkmanager</code> group.</para>
+<para>All users that should have permission to change network settings must
+belong to the <code>networkmanager</code> group:
+
+<programlisting>
+users.extraUsers.youruser.extraGroups = [ "networkmanager" ];
+</programlisting>
+</para>
+
+<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>
 
 <note><para><code>networking.networkmanager</code> and
-<code>networking.wireless</code> can not be enabled at the same time:
-you can still connect to the wireless networks using
+<code>networking.wireless</code> (WPA Supplicant) cannot be enabled at the same
+time: you can still connect to the wireless networks using
 NetworkManager.</para></note>
 
 </section>
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 13668dfd8eb..40d49f1541b 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -94,14 +94,11 @@ let
     "--stringparam chunk.toc ${toc}"
   ];
 
-  olinkDB = stdenv.mkDerivation {
-    name = "manual-olinkdb";
-
-    inherit sources;
-
-    buildInputs = [ libxml2 libxslt ];
-
-    buildCommand = ''
+  olinkDB = runCommand "manual-olinkdb"
+    { inherit sources;
+      buildInputs = [ libxml2 libxslt ];
+    }
+    ''
       ${copySources}
 
       xsltproc \
@@ -133,15 +130,14 @@ let
       </targetset>
       EOF
     '';
-  };
 
 in rec {
 
   # The NixOS options in JSON format.
-  optionsJSON = stdenv.mkDerivation {
-    name = "options-json";
-
-    buildCommand = ''
+  optionsJSON = runCommand "options-json"
+    { meta.description = "List of NixOS options in JSON format";
+    }
+    ''
       # Export list of options in different format.
       dst=$out/share/doc/nixos
       mkdir -p $dst
@@ -154,18 +150,14 @@ in rec {
       echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
     ''; # */
 
-    meta.description = "List of NixOS options in JSON format";
-  };
-
   # Generate the NixOS manual.
-  manual = stdenv.mkDerivation {
-    name = "nixos-manual";
-
-    inherit sources;
-
-    buildInputs = [ libxml2 libxslt ];
-
-    buildCommand = ''
+  manual = runCommand "nixos-manual"
+    { inherit sources;
+      buildInputs = [ libxml2 libxslt ];
+      meta.description = "The NixOS manual in HTML format";
+      allowedReferences = ["out"];
+    }
+    ''
       ${copySources}
 
       # Check the validity of the manual sources.
@@ -192,20 +184,12 @@ in rec {
       echo "doc manual $dst" >> $out/nix-support/hydra-build-products
     ''; # */
 
-    meta.description = "The NixOS manual in HTML format";
-
-    allowedReferences = ["out"];
-  };
 
-
-  manualEpub = stdenv.mkDerivation {
-    name = "nixos-manual-epub";
-
-    inherit sources;
-
-    buildInputs = [ libxml2 libxslt zip ];
-
-    buildCommand = ''
+  manualEpub = runCommand "nixos-manual-epub"
+    { inherit sources;
+      buildInputs = [ libxml2 libxslt zip ];
+    }
+    ''
       ${copySources}
 
       # Check the validity of the manual sources.
@@ -234,17 +218,15 @@ in rec {
       mkdir -p $out/nix-support
       echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
     '';
-  };
 
-  # Generate the NixOS manpages.
-  manpages = stdenv.mkDerivation {
-    name = "nixos-manpages";
 
-    inherit sources;
-
-    buildInputs = [ libxml2 libxslt ];
-
-    buildCommand = ''
+  # Generate the NixOS manpages.
+  manpages = runCommand "nixos-manpages"
+    { inherit sources;
+      buildInputs = [ libxml2 libxslt ];
+      allowedReferences = ["out"];
+    }
+    ''
       ${copySources}
 
       # Check the validity of the man pages sources.
@@ -264,7 +246,4 @@ in rec {
         ./man-pages.xml
     '';
 
-    allowedReferences = ["out"];
-  };
-
 }
diff --git a/nixos/doc/manual/development/development.xml b/nixos/doc/manual/development/development.xml
index 2983c76c770..47343d93cde 100644
--- a/nixos/doc/manual/development/development.xml
+++ b/nixos/doc/manual/development/development.xml
@@ -14,8 +14,10 @@ NixOS.</para>
 <xi:include href="sources.xml" />
 <xi:include href="writing-modules.xml" />
 <xi:include href="building-parts.xml" />
+<xi:include href="writing-documentation.xml" />
 <xi:include href="building-nixos.xml" />
 <xi:include href="nixos-tests.xml" />
 <xi:include href="testing-installer.xml" />
+<xi:include href="releases.xml" />
 
 </part>
diff --git a/nixos/doc/manual/development/option-declarations.xml b/nixos/doc/manual/development/option-declarations.xml
index b0689aa1d97..ce432a7fa6c 100644
--- a/nixos/doc/manual/development/option-declarations.xml
+++ b/nixos/doc/manual/development/option-declarations.xml
@@ -31,9 +31,9 @@ options = {
   <varlistentry>
     <term><varname>type</varname></term>
     <listitem>
-      <para>The type of the option (see below).  It may be omitted,
-      but that’s not advisable since it may lead to errors that are
-      hard to diagnose.</para>
+      <para>The type of the option (see <xref linkend='sec-option-types' />).
+      It may be omitted, but that’s not advisable since it may lead to errors
+      that are hard to diagnose.</para>
     </listitem>
   </varlistentry>
 
@@ -65,86 +65,92 @@ options = {
 
 </para>
 
-<para>Here is a non-exhaustive list of option types:
+<section xml:id="sec-option-declarations-eot"><title>Extensible Option 
+    Types</title>
 
-<variablelist>
-
-  <varlistentry>
-    <term><varname>types.bool</varname></term>
-    <listitem>
-      <para>A Boolean.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>types.int</varname></term>
-    <listitem>
-      <para>An integer.</para>
-    </listitem>
-  </varlistentry>
+  <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 
+    <literal>enum</literal> and <literal>submodules</literal> and any composed
+    forms of them.</para>
 
-  <varlistentry>
-    <term><varname>types.str</varname></term>
-    <listitem>
-      <para>A string.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>types.lines</varname></term>
-    <listitem>
-      <para>A string.  If there are multiple definitions, they are
-      concatenated, with newline characters in between.</para>
-    </listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>types.path</varname></term>
-    <listitem>
-      <para>A path, defined as anything that, when coerced to a
-      string, starts with a slash.  This includes derivations.</para>
-    </listitem>
-  </varlistentry>
+  <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>
 
-  <varlistentry>
-    <term><varname>types.package</varname></term>
-    <listitem>
-      <para>A derivation (such as <literal>pkgs.hello</literal>) or a
-      store path (such as
-      <filename>/nix/store/1ifi1cfbfs5iajmvwgrbmrnrw3a147h9-hello-2.10</filename>).</para>
-    </listitem>
-  </varlistentry>
+  <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 ...).
+  </para>
 
-  <varlistentry>
-    <term><varname>types.listOf</varname> <replaceable>t</replaceable></term>
-    <listitem>
-      <para>A list of elements of type <replaceable>t</replaceable>
-      (e.g., <literal>types.listOf types.str</literal> is a list of
-      strings).  Multiple definitions are concatenated together.</para>
-    </listitem>
-  </varlistentry>
+  <para>There are two approach to this module structure:
 
-  <varlistentry>
-    <term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term>
-    <listitem>
-      <para>A set of elements of type <replaceable>t</replaceable>
-      (e.g., <literal>types.attrsOf types.int</literal> is a set of
-      name/value pairs, the values being integers).</para>
+  <itemizedlist>
+    <listitem><para>Managing the display managers independently by adding an
+        enable option to every display manager module backend. (NixOS)</para>
     </listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>types.nullOr</varname> <replaceable>t</replaceable></term>
-    <listitem>
-      <para>Either the value <literal>null</literal> or something of
-      type <replaceable>t</replaceable>.</para>
+    <listitem><para>Managing the display managers in the central module by
+        adding an option to select which display manager backend to use.</para>
     </listitem>
-  </varlistentry>
-
-</variablelist>
-
-You can also create new types using the function
-<varname>mkOptionType</varname>.  See
-<filename>lib/types.nix</filename> in Nixpkgs for details.</para>
+  </itemizedlist>
+  </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
+    each backend <literal>enable</literal> option. As a result, this restriction
+    has to be done explicitely by adding assertions in each display manager
+    backend module.</para>
+
+  <para>On the other hand, managing the display managers backends in the
+    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>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 
+    placeholder in the service module</title>
+<screen>
+services.xserver.displayManager.enable = mkOption {
+  description = "Display manager to use";
+  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 
+    <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> 
+    module</title>
+<screen>
+services.xserver.displayManager.enable = mkOption {
+  type = with types; nullOr (enum [ "kdm" ]);
+};</screen></example>
+
+<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> 
+  or <literal>with types; listOf (enum [ "foo" "bar" ])</literal>.</para>
 
 </section>
+</section>
diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml
new file mode 100644
index 00000000000..8e6ac53ad48
--- /dev/null
+++ b/nixos/doc/manual/development/option-types.xml
@@ -0,0 +1,446 @@
+<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-option-types">
+
+<title>Options Types</title>
+
+  <para>Option types are a way to put constraints on the values a module option 
+    can take.
+    Types are also responsible of how values are merged in case of multiple 
+    value definitions.</para>
+  <section><title>Basic Types</title>
+
+    <para>Basic types are the simplest available types in the module system.
+      Basic types include multiple string types that mainly differ in how 
+      definition merging is handled.</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>types.bool</varname></term>
+    <listitem><para>A boolean, its values can be <literal>true</literal> or 
+        <literal>false</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.int</varname></term>
+    <listitem><para>An integer.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.path</varname></term>
+    <listitem><para>A filesystem path, defined as anything that when coerced to 
+        a string starts with a slash. Even if derivations can be considered as 
+        path, the more specific <literal>types.package</literal> should be 
+        preferred.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.package</varname></term>
+    <listitem><para>A derivation or a store path.</para></listitem>
+  </varlistentry>
+</variablelist>
+
+<para>String related types:</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>types.str</varname></term>
+    <listitem><para>A string. Multiple definitions cannot be 
+        merged.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.lines</varname></term>
+    <listitem><para>A string. Multiple definitions are concatenated with a new 
+        line <literal>"\n"</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.commas</varname></term>
+    <listitem><para>A string. Multiple definitions are concatenated with a comma 
+        <literal>","</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.envVar</varname></term>
+    <listitem><para>A string. Multiple definitions are concatenated with a 
+        collon <literal>":"</literal>.</para></listitem>
+  </varlistentry>
+</variablelist>
+
+ </section>
+
+ <section><title>Value Types</title>
+
+   <para>Value types are type that take a value parameter. The only value type 
+     in the library is <literal>enum</literal>.</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>types.enum</varname> <replaceable>l</replaceable></term>
+    <listitem><para>One element of the list <replaceable>l</replaceable>, e.g. 
+        <literal>types.enum [ "left" "right" ]</literal>. Multiple definitions 
+        cannot be merged.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.separatedString</varname>
+      <replaceable>sep</replaceable></term>
+    <listitem><para>A string with a custom separator
+        <replaceable>sep</replaceable>, e.g. <literal>types.separatedString
+          "|"</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.submodule</varname> <replaceable>o</replaceable></term>
+    <listitem><para>A set of sub options <replaceable>o</replaceable>.
+        <replaceable>o</replaceable> can be an attribute set or a function
+        returning an attribute set. Submodules are used in composed types to
+        create modular options. Submodule are detailed in <xref
+          linkend='section-option-types-submodule' />.</para></listitem>
+  </varlistentry>
+</variablelist>
+ </section>
+
+ <section><title>Composed Types</title>
+
+   <para>Composed types are types that take a type as parameter. <literal>listOf 
+       int</literal> and <literal>either int str</literal> are examples of 
+     composed types.</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>types.listOf</varname> <replaceable>t</replaceable></term>
+    <listitem><para>A list of <replaceable>t</replaceable> type, e.g. 
+        <literal>types.listOf int</literal>. Multiple definitions are merged 
+        with list concatenation.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term>
+    <listitem><para>An attribute set of where all the values are of 
+        <replaceable>t</replaceable> type. Multiple definitions result in the 
+        joined attribute set.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.loaOf</varname> <replaceable>t</replaceable></term>
+    <listitem><para>An attribute set or a list of <replaceable>t</replaceable> 
+        type. Multiple definitions are merged according to the 
+        value.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.nullOr</varname> <replaceable>t</replaceable></term>
+    <listitem><para><literal>null</literal> or type 
+        <replaceable>t</replaceable>. Multiple definitions are merged according 
+        to type <replaceable>t</replaceable>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.uniq</varname> <replaceable>t</replaceable></term>
+    <listitem><para>Ensures that type <replaceable>t</replaceable> cannot be 
+        merged. It is used to ensure option definitions are declared only 
+        once.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>types.either</varname> <replaceable>t1</replaceable> 
+      <replaceable>t2</replaceable></term>
+    <listitem><para>Type <replaceable>t1</replaceable> or type 
+        <replaceable>t2</replaceable>, e.g. <literal>with types; either int 
+          str</literal>. Multiple definitions cannot be 
+        merged.</para></listitem>
+  </varlistentry>
+</variablelist>
+
+</section>
+
+<section xml:id='section-option-types-submodule'><title>Submodule</title>
+
+  <para>Submodule is a very powerful type that defines a set of sub-options that 
+    are handled like a separate module.
+    It is especially interesting when used with composed types like 
+    <literal>attrsOf</literal> or <literal>listOf</literal>.</para>
+
+  <para>The submodule type take a parameter <replaceable>o</replaceable>, that 
+    should be a set, or a function returning a set with an 
+    <literal>options</literal> key defining the sub-options.
+    The option set can be defined directly (<xref linkend='ex-submodule-direct' 
+      />) or as reference (<xref linkend='ex-submodule-reference' />).</para>
+
+  <para>Submodule option definitions are type-checked accordingly to the options 
+    declarations. It is possible to declare submodule options inside a submodule 
+    sub-options for even higher modularity.</para>
+
+<example xml:id='ex-submodule-direct'><title>Directly defined submodule</title>
+<screen>
+options.mod = mkOption {
+  name = "mod";
+  description = "submodule example";
+  type = with types; listOf (submodule {
+    options = {
+      foo = mkOption {
+        type = int;
+      };
+      bar = mkOption {
+        type = str;
+      };
+    };
+  });
+};</screen></example>
+
+<example xml:id='ex-submodule-reference'><title>Submodule defined as a 
+    reference</title>
+<screen>
+let
+  modOptions = {
+    options = {
+      foo = mkOption {
+        type = int;
+      };
+      bar = mkOption {
+        type = int;
+      };
+    };
+  };
+in
+options.mod = mkOption {
+  description = "submodule example";
+  type = with types; listOf (submodule modOptions);
+};</screen></example>
+
+<section><title>Composed with <literal>listOf</literal></title>
+
+  <para>When composed with <literal>listOf</literal>, submodule allows multiple 
+    definitions of the submodule option set.</para>
+
+<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list 
+    of submodules</title>
+<screen>
+options.mod = mkOption {
+  description = "submodule example";
+  type = with types; listOf (submodule {
+    options = {
+      foo = mkOption {
+        type = int;
+      };
+      bar = mkOption {
+        type = str;
+      };
+    };
+  });
+};</screen></example>
+
+<example xml:id='ex-submodule-listof-definition'><title>Definition of a list of 
+    submodules</title>
+<screen>
+config.mod = [
+  { foo = 1; bar = "one"; }
+  { foo = 2; bar = "two"; }
+];</screen></example>
+
+</section>
+
+
+<section><title>Composed with <literal>attrsOf</literal></title>
+
+  <para>When composed with <literal>attrsOf</literal>, submodule allows multiple 
+    named definitions of the submodule option set.</para>
+
+<example xml:id='ex-submodule-attrsof-declaration'><title>Declaration of 
+    attribute sets of submodules</title>
+<screen>
+options.mod = mkOption {
+  description = "submodule example";
+  type = with types; attrsOf (submodule {
+    options = {
+      foo = mkOption {
+        type = int;
+      };
+      bar = mkOption {
+        type = str;
+      };
+    };
+  });
+};</screen></example>
+
+<example xml:id='ex-submodule-attrsof-definition'><title>Declaration of 
+    attribute sets of submodules</title>
+<screen>
+config.mod.one = { foo = 1; bar = "one"; };
+config.mod.two = { foo = 2; bar = "two"; };</screen></example>
+
+</section>
+</section>
+
+<section><title>Extending types</title>
+
+  <para>Types are mainly characterized by their <literal>check</literal> and 
+    <literal>merge</literal> functions.</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>check</varname></term>
+    <listitem><para>The function to type check the value. Takes a value as 
+        parameter and return a boolean.
+        It is possible to extend a type check with the 
+        <literal>addCheck</literal> function (<xref 
+          linkend='ex-extending-type-check-1' />), or to fully override the 
+        check function (<xref linkend='ex-extending-type-check-2' />).</para>
+
+<example xml:id='ex-extending-type-check-1'><title>Adding a type check</title>
+<screen>
+byte = mkOption {
+  description = "An integer between 0 and 255.";
+  type = addCheck (x: x &gt;= 0 &amp;&amp; x &lt;= 255) types.int;
+};</screen></example>
+
+<example xml:id='ex-extending-type-check-2'><title>Overriding a type 
+    check</title>
+<screen>
+nixThings = mkOption {
+  description = "words that start with 'nix'";
+  type = types.str // {
+    check = (x: lib.hasPrefix "nix" x)
+  };
+};</screen></example>
+    </listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>merge</varname></term>
+    <listitem><para>Function to merge the options values when multiple values 
+        are set.
+The function takes two parameters, <literal>loc</literal> the option path as a 
+list of strings, and <literal>defs</literal> the list of defined values as a 
+list.
+It is possible to override a type merge function for custom 
+needs.</para></listitem>
+  </varlistentry>
+</variablelist>
+
+</section>
+
+<section><title>Custom Types</title>
+
+<para>Custom types can be created with the <literal>mkOptionType</literal> 
+  function.
+As type creation includes some more complex topics such as submodule handling, 
+it is recommended to get familiar with <filename 
+  xlink:href="https://github.com/NixOS/nixpkgs/blob/master/lib/types.nix">types.nix</filename> 
+code before creating a new type.</para>
+
+<para>The only required parameter is <literal>name</literal>.</para>
+
+<variablelist>
+  <varlistentry>
+    <term><varname>name</varname></term>
+    <listitem><para>A string representation of the type function 
+        name.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>definition</varname></term>
+    <listitem><para>Description of the type used in documentation. Give 
+        information of the type and any of its arguments.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>check</varname></term>
+    <listitem><para>A function to type check the definition value. Takes the 
+        definition value as a parameter and returns a boolean indicating the 
+        type check result, <literal>true</literal> for success and 
+        <literal>false</literal> for failure.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>merge</varname></term>
+    <listitem><para>A function to merge multiple definitions values. Takes two 
+        parameters:</para>
+      <variablelist>
+        <varlistentry>
+          <term><replaceable>loc</replaceable></term>
+          <listitem><para>The option path as a list of strings, e.g. 
+              <literal>["boot" "loader "grub" 
+                "enable"]</literal>.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><replaceable>defs</replaceable></term>
+          <listitem><para>The list of sets of defined <literal>value</literal> 
+              and <literal>file</literal> where the value was defined, e.g. 
+              <literal>[ { file = "/foo.nix"; value = 1; } { file = "/bar.nix"; 
+                value = 2 } ]</literal>. The <literal>merge</literal> function 
+              should return the merged value or throw an error in case the 
+              values are impossible or not meant to be merged.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>getSubOptions</varname></term>
+    <listitem><para>For composed types that can take a submodule as type 
+        parameter, this function generate sub-options documentation. It takes 
+        the current option prefix as a list and return the set of sub-options. 
+        Usually defined in a recursive manner by adding a term to the prefix, 
+        e.g. <literal>prefix: elemType.getSubOptions (prefix ++ 
+          [<replaceable>"prefix"</replaceable>])</literal> where 
+        <replaceable>"prefix"</replaceable> is the newly added 
+        prefix.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>getSubModules</varname></term>
+    <listitem><para>For composed types that can take a submodule as type 
+        parameter, this function should return the type parameters submodules. 
+        If the type parameter is called <literal>elemType</literal>, the 
+        function should just recursively look into submodules by returning 
+        <literal>elemType.getSubModules;</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>substSubModules</varname></term>
+    <listitem><para>For composed types that can take a submodule as type 
+        parameter, this function can be used to substitute the parameter of a 
+        submodule type. It takes a module as parameter and return the type with 
+        the submodule options substituted. It is usally defined as a type 
+        function call with a recursive call to 
+        <literal>substSubModules</literal>, e.g for a type 
+        <literal>composedType</literal> that take an <literal>elemtype</literal> 
+        type parameter, this function should be defined as <literal>m: 
+          composedType (elemType.substSubModules m)</literal>.</para></listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>typeMerge</varname></term>
+    <listitem><para>A function to merge multiple type declarations. Takes the 
+        type to merge <literal>functor</literal> as parameter. A 
+        <literal>null</literal> return value means that type cannot be 
+        merged.</para>
+      <variablelist>
+        <varlistentry>
+          <term><replaceable>f</replaceable></term>
+          <listitem><para>The type to merge  
+              <literal>functor</literal>.</para></listitem>
+        </varlistentry>
+      </variablelist>
+      <para>Note: There is a generic <literal>defaultTypeMerge</literal> that 
+        work with most of value and composed types.</para>
+    </listitem>
+  </varlistentry>
+  <varlistentry>
+    <term><varname>functor</varname></term>
+    <listitem><para>An attribute set representing the type. It is used for type 
+        operations and has the following keys:</para>
+      <variablelist>
+        <varlistentry>
+          <term><varname>type</varname></term>
+          <listitem><para>The type function.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>wrapped</varname></term>
+          <listitem><para>Holds the type parameter for composed types.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>payload</varname></term>
+          <listitem><para>Holds the value parameter for value types. 
+              The types that have a <literal>payload</literal> are the
+              <literal>enum</literal>, <literal>separatedString</literal> and
+              <literal>submodule</literal> types.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>binOp</varname></term>
+          <listitem><para>A binary operation that can merge the payloads of two 
+              same types. Defined as a function that take two payloads as 
+              parameters and return the payloads merged.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </listitem>
+  </varlistentry>
+</variablelist>
+
+</section>
+</section>
diff --git a/nixos/doc/manual/development/releases.xml b/nixos/doc/manual/development/releases.xml
new file mode 100755
index 00000000000..01c3dbc22f1
--- /dev/null
+++ b/nixos/doc/manual/development/releases.xml
@@ -0,0 +1,241 @@
+<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="ch-releases">
+
+<title>Releases</title>
+
+<section xml:id="release-process">
+  <title>Release process</title>
+
+  <para>
+    Going through an example of releasing NixOS 15.09:
+  </para>
+
+  <section xml:id="one-month-before-the-beta">
+    <title>One month before the beta</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          Send an email to nix-dev mailinglist as a warning about upcoming beta "feature freeze" in a month.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Discuss with Eelco Dolstra and the community (via IRC, ML) about what will reach the deadline.
+          Any issue or Pull Request targeting the release should have assigned milestone.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section xml:id="at-beta-release-time">
+    <title>At beta release time</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          Rename <literal>rl-unstable.xml</literal> -&gt;
+          <literal>rl-1509.xml</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>git tag -a -m &quot;Release 15.09-beta&quot; 15.09-beta &amp;&amp; git push --tags</literal>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          From the master branch run <literal>git checkout -B release-15.09</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixos-org-configurations/pull/18">
+            Make sure channel is created at http://nixos.org/channels/.
+          </link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/settings/branches">
+            Lock the branch on github (so developers can’t force push)
+          </link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/compare/bdf161ed8d21...6b63c4616790">bump
+          <literal>system.defaultChannel</literal> attribute in
+          <literal>nixos/modules/misc/version.nix</literal></link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/d6b08acd1ccac0d9d502c4b635e00b04d3387f06">update
+          <literal>versionSuffix</literal> in
+          <literal>nixos/release.nix</literal></link>, use
+          <literal>git log --format=%an|wc -l</literal> to get commit
+          count
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>echo -n &quot;16.03&quot; &gt; .version</literal> in
+          master.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/b8a4095003e27659092892a4708bb3698231a842">pick
+          a new name for unstable branch.</link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/issues/13559">Create
+            an issue for tracking Zero Hydra Failures progress. ZHF is an effort
+            to get build failures down to zero.</link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Use https://lwn.net/Vulnerabilities/ and 
+          <link xlink:href="https://github.com/NixOS/nixpkgs/search?utf8=%E2%9C%93&amp;q=vulnerabilities&amp;type=Issues">triage vulnerabilities in an issue</link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Create two Hydra jobsets: release-15.09 and release-15.09-small with <literal>stableBranch</literal> set to false
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Edit changelog at
+          <literal>nixos/doc/manual/release-notes/rl-1509.xml</literal>
+          (double check desktop versions are noted)
+        </para>
+        <itemizedlist spacing="compact">
+          <listitem>
+            <para>
+              Get all new NixOS modules
+              <literal>git diff release-14.12..release-15.09 nixos/modules/module-list.nix|grep ^+</literal>
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              Note systemd, kernel, glibc and Nix upgrades.
+            </para>
+          </listitem>
+        </itemizedlist>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section xml:id="before-the-final-release">
+    <title>Before the final release</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          Release Nix (currently only Eelco Dolstra can do that).
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/53710c752a85f00658882531bc90a23a3d1287e4">
+            Make sure fallback is updated.
+          </link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/NixOS/nixpkgs/commit/40fd9ae3ac8048758abdcfc7d28a78b5f22fe97e">
+            Update README.md with new stable NixOS version information.
+          </link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Change <literal>stableBranch</literal> to true and wait for channel to update.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section xml:id="at-final-release-time">
+    <title>At final release time</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          <literal>git tag -s -a -m &quot;Release 15.09&quot; 15.09</literal>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Update http://nixos.org/nixos/download.html and http://nixos.org/nixos/manual in https://github.com/NixOS/nixos-org-configurations
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Get number of commits for the release:
+          <literal>git log release-14.04..release-14.12 --format=%an|wc -l</literal>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Commits by contributor:
+          <literal>git log release-14.04..release-14.12 --format=%an|sort|uniq -c|sort -rn</literal>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Send an email to nix-dev to announce the release with above information. Best to check how previous email was formulated
+          to see what needs to be included.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
+</section>
+
+<section xml:id="release-schedule">
+  <title>Release schedule</title>
+
+  <informaltable>
+    <tgroup cols="2">
+      <colspec align="left" />
+      <colspec align="left" />
+      <thead>
+        <row>
+          <entry>
+            Date
+          </entry>
+          <entry>
+            Event
+          </entry>
+        </row>
+      </thead>
+      <tbody>
+        <row>
+          <entry>
+            2016-07-25
+          </entry>
+          <entry>
+            Send email to nix-dev about upcoming branch-off
+          </entry>
+        </row>
+        <row>
+          <entry>
+            2016-09-01
+          </entry>
+          <entry>
+            <literal>release-16.09</literal> branch and corresponding jobsets are created,
+            change freeze
+          </entry>
+        </row>
+        <row>
+          <entry>
+            2016-09-30
+          </entry>
+          <entry>
+            NixOS 16.09 released
+          </entry>
+        </row>
+      </tbody>
+    </tgroup>
+  </informaltable>
+</section>
+
+</chapter>
diff --git a/nixos/doc/manual/development/writing-documentation.xml b/nixos/doc/manual/development/writing-documentation.xml
new file mode 100644
index 00000000000..59a287717ac
--- /dev/null
+++ b/nixos/doc/manual/development/writing-documentation.xml
@@ -0,0 +1,147 @@
+<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-writing-documentation">
+
+<title>Writing NixOS Documentation</title>
+
+<para>
+  As NixOS grows, so too does the need for a catalogue and explanation
+  of its extensive functionality. Collecting pertinent information
+  from disparate sources and presenting it in an accessible style
+  would be a worthy contribution to the project.
+</para>
+
+<section>
+<title>Building the Manual</title>
+<para>
+  The DocBook sources of the <xref linkend="book-nixos-manual"/> are in the
+  <link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual"><filename>nixos/doc/manual</filename></link>
+  subdirectory of the Nixpkgs repository. If you make modifications to
+  the manual, it's important to build it before committing. You can do
+  that as follows:
+
+  <screen>nix-build nixos/release.nix -A manual.x86_64-linux</screen>
+</para>
+
+<para>
+  When this command successfully finishes, it will tell you where the
+  manual got generated. The HTML will be accessible through the
+  <filename>result</filename> symlink at
+  <filename>./result/share/doc/nixos/index.html</filename>.
+</para>
+</section>
+
+<section>
+<title>Editing DocBook XML</title>
+
+<para>
+  For general information on how to write in DocBook, see
+  <link xlink:href="http://www.docbook.org/tdg5/en/html/docbook.html">
+    DocBook 5: The Definitive Guide</link>.
+</para>
+
+<para>
+  Emacs nXML Mode is very helpful for editing DocBook XML because it
+  validates the document as you write, and precisely locates
+  errors. To use it, see <xref linkend="sec-emacs-docbook-xml"/>.
+</para>
+
+<para>
+  <link xlink:href="http://pandoc.org">Pandoc</link> can generate
+  DocBook XML from a multitude of formats, which makes a good starting
+  point.
+
+  <example xml:id="ex-pandoc-xml-conv">
+    <title>Pandoc invocation to convert GitHub-Flavoured MarkDown to DocBook 5 XML</title>
+    <screen>pandoc -f markdown_github -t docbook5 docs.md -o my-section.md</screen>
+  </example>
+
+  Pandoc can also quickly convert a single
+  <filename>section.xml</filename> to HTML, which is helpful when
+  drafting.
+</para>
+
+<para>
+  Sometimes writing valid DocBook is simply too difficult. In this
+  case, submit your documentation updates in a <link
+  xlink:href="https://github.com/NixOS/nixpkgs/issues/new">GitHub
+  Issue</link> and someone will handle the conversion to XML for you.
+</para>
+</section>
+
+<section>
+<title>Creating a Topic</title>
+
+<para>
+ You can use an existing topic as a basis for the new topic or create a topic from scratch.
+</para>
+
+<para>
+Keep the following guidelines in mind when you create and add a topic:
+
+<itemizedlist>
+  <listitem><para>
+    The NixOS <link xlink:href="http://www.docbook.org/tdg5/en/html/book.html"><tag>book</tag></link>
+    element is in <filename>nixos/doc/manual/manual.xml</filename>.
+    It includes several
+    <link xlink:href="http://www.docbook.org/tdg5/en/html/book.html"><tag>part</tag>s</link>
+    which are in subdirectories.
+  </para></listitem>
+
+  <listitem><para>
+    Store the topic file in the same directory as the <tag>part</tag>
+    to which it belongs. If your topic is about configuring a NixOS
+    module, then the XML file can be stored alongside the module
+    definition <filename>nix</filename> file.
+  </para></listitem>
+
+  <listitem><para>
+    If you include multiple words in the file name, separate the words
+    with a dash. For example: <filename>ipv6-config.xml</filename>.
+  </para></listitem>
+
+  <listitem><para>
+    Make sure that the <tag>xml:id</tag> value is unique. You can use
+    abbreviations if the ID is too long. For example:
+    <varname>nixos-config</varname>.
+  </para></listitem>
+
+  <listitem><para>
+    Determine whether your topic is a chapter or a section. If you are
+    unsure, open an existing topic file and check whether the main
+    element is chapter or section.
+  </para></listitem>
+
+</itemizedlist>
+
+</para>
+</section>
+
+<section>
+<title>Adding a Topic to the Book</title>
+
+<para>
+  Open the parent XML file and add an <varname>xi:include</varname>
+  element to the list of chapters with the file name of the topic that
+  you created. If you created a <tag>section</tag>, you add the file to
+  the <tag>chapter</tag> file. If you created a <tag>chapter</tag>, you
+  add the file to the <tag>part</tag> file.
+</para>
+
+<para>
+  If the topic is about configuring a NixOS module, it can be
+  automatically included in the manual by using the
+  <varname>meta.doc</varname> attribute. See <xref
+  linkend="sec-meta-attributes"/> for an explanation.
+</para>
+
+</section>
+
+
+
+
+
+
+</chapter>
diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml
index a68b122ce02..ef6920160e6 100644
--- a/nixos/doc/manual/development/writing-modules.xml
+++ b/nixos/doc/manual/development/writing-modules.xml
@@ -176,6 +176,7 @@ in {
 </example>
 
 <xi:include href="option-declarations.xml" />
+<xi:include href="option-types.xml" />
 <xi:include href="option-def.xml" />
 <xi:include href="meta-attributes.xml" />
 
diff --git a/nixos/doc/manual/installation/obtaining.xml b/nixos/doc/manual/installation/obtaining.xml
index f6e8b218e2b..20a4838be88 100644
--- a/nixos/doc/manual/installation/obtaining.xml
+++ b/nixos/doc/manual/installation/obtaining.xml
@@ -32,7 +32,7 @@ running NixOS system through several other means:
   <listitem>
     <para>Using AMIs for Amazon’s EC2.  To find one for your region
     and instance type, please refer to the <link
-    xlink:href="https://github.com/NixOS/nixops/blob/master/nix/ec2-amis.nix">list
+    xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/ec2-amis.nix">list
     of most recent AMIs</link>.</para>
   </listitem>
   <listitem>
diff --git a/nixos/doc/manual/installation/upgrading.xml b/nixos/doc/manual/installation/upgrading.xml
index 65d395b0c88..c974523f886 100644
--- a/nixos/doc/manual/installation/upgrading.xml
+++ b/nixos/doc/manual/installation/upgrading.xml
@@ -101,6 +101,11 @@ channel by running
 which is equivalent to the more verbose <literal>nix-channel --update
 nixos; nixos-rebuild switch</literal>.</para>
 
+<note><para>Channels are set per user. This means that running <literal>
+nix-channel --add</literal> as a non root user (or without sudo) will not
+affect configuration in <literal>/etc/nixos/configuration.nix</literal>
+</para></note>
+
 <warning><para>It is generally safe to switch back and forth between
 channels.  The only exception is that a newer NixOS may also have a
 newer Nix version, which may involve an upgrade of Nix’s database
diff --git a/nixos/doc/manual/man-nixos-rebuild.xml b/nixos/doc/manual/man-nixos-rebuild.xml
index d01e2e060bd..f74788353e6 100644
--- a/nixos/doc/manual/man-nixos-rebuild.xml
+++ b/nixos/doc/manual/man-nixos-rebuild.xml
@@ -68,7 +68,7 @@ desired operation.  It must be one of the following:
     <listitem>
       <para>Build and activate the new configuration, and make it the
       boot default.  That is, the configuration is added to the GRUB
-      boot menu as the default meny entry, so that subsequent reboots
+      boot menu as the default menu entry, so that subsequent reboots
       will boot the system into the new configuration.  Previous
       configurations activated with <command>nixos-rebuild
       switch</command> or <command>nixos-rebuild boot</command> remain
diff --git a/nixos/doc/manual/release-notes/release-notes.xml b/nixos/doc/manual/release-notes/release-notes.xml
index 31a7ae04a4f..20d3f74f94b 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-1703.xml" />
 <xi:include href="rl-1609.xml" />
 <xi:include href="rl-1603.xml" />
 <xi:include href="rl-1509.xml" />
diff --git a/nixos/doc/manual/release-notes/rl-1509.xml b/nixos/doc/manual/release-notes/rl-1509.xml
index a68baa0d807..e0271485c36 100644
--- a/nixos/doc/manual/release-notes/rl-1509.xml
+++ b/nixos/doc/manual/release-notes/rl-1509.xml
@@ -471,7 +471,7 @@ in
   <listitem> <para>
     A newly packaged TeX Live 2015 is provided in <literal>pkgs.texlive</literal>,
     split into 6500 nix packages. For basic user documentation see
-    <link xlink:href="https://github.com/NixOS/nixpkgs/blob/release-15.09/pkgs/tools/typesetting/tex/texlive-new/default.nix#L1"
+    <link xlink:href="https://github.com/NixOS/nixpkgs/blob/release-15.09/pkgs/tools/typesetting/tex/texlive/default.nix#L1"
       >the source</link>.
     Beware of <link xlink:href="https://github.com/NixOS/nixpkgs/issues/9757"
       >an issue</link> when installing a too large package set.
diff --git a/nixos/doc/manual/release-notes/rl-1609.xml b/nixos/doc/manual/release-notes/rl-1609.xml
index 70759ee25f8..ade7d5581ce 100644
--- a/nixos/doc/manual/release-notes/rl-1609.xml
+++ b/nixos/doc/manual/release-notes/rl-1609.xml
@@ -4,7 +4,7 @@
          version="5.0"
          xml:id="sec-release-16.09">
 
-<title>Release 16.09 (“Flounder”, 2016/09/??)</title>
+<title>Release 16.09 (“Flounder”, 2016/09/30)</title>
 
 <para>In addition to numerous new and upgraded packages, this release
 has the following highlights: </para>
@@ -12,22 +12,45 @@ has the following highlights: </para>
 <itemizedlist>
 
   <listitem>
-    <para>PXE "netboot" media has landed in <link xlink:href="https://github.com/NixOS/nixpkgs/pull/14740" />.
-    See <xref linkend="sec-booting-from-pxe" /> for documentation.</para>
+    <para>Many NixOS configurations and Nix packages now use
+    significantly less disk space, thanks to the <link
+    xlink:href="https://github.com/NixOS/nixpkgs/issues/7117">extensive
+    work on closure size reduction</link>. For example, the closure
+    size of a minimal NixOS container went down from ~424 MiB in 16.03
+    to ~212 MiB in 16.09, while the closure size of Firefox went from
+    ~651 MiB to ~259 MiB.</para>
   </listitem>
 
   <listitem>
-    <para>Xorg-server-1.18.*. If you choose <literal>"ati_unfree"</literal> driver,
-    1.17.* is still used due to ABI incompatibility.</para>
+    <para>To improve security, packages are now <link
+    xlink:href="https://github.com/NixOS/nixpkgs/pull/12895">built
+    using various hardening features</link>. See the Nixpkgs manual
+    for more information.</para>
   </listitem>
+
+  <listitem>
+    <para>Support for PXE netboot.  See <xref
+    linkend="sec-booting-from-pxe" /> for documentation.</para>
+  </listitem>
+
+  <listitem>
+    <para>X.org server 1.18. If you use the
+    <literal>ati_unfree</literal> driver, 1.17 is still used due to an
+    ABI incompatibility.</para>
+  </listitem>
+
+  <listitem>
+    <para>This release is based on Glibc 2.24, GCC 5.4.0 and systemd
+    231. The default Linux kernel remains 4.4.</para>
+  </listitem>
+
 </itemizedlist>
 
 <para>The following new services were added since the last release:</para>
 
-  <itemizedlist>
-    <listitem><para><literal>(this will get automatically generated at release time)</literal></para></listitem>
-  </itemizedlist>
-
+<itemizedlist>
+  <listitem><para><literal>(this will get automatically generated at release time)</literal></para></listitem>
+</itemizedlist>
 
 <para>When upgrading from a previous release, please be aware of the
 following incompatible changes:</para>
@@ -36,7 +59,8 @@ following incompatible changes:</para>
 
   <listitem>
     <para>A large number of packages have been converted to use the multiple outputs feature
-      of Nix to greatly reduce the amount of required disk space. This may require changes
+      of Nix to greatly reduce the amount of required disk space, as
+      mentioned above. This may require changes
       to any custom packages to make them build again; see the relevant chapter in the
       Nixpkgs manual for more information. (Additional caveat to packagers: some packaging conventions
       related to multiple-output packages
@@ -46,6 +70,25 @@ following incompatible changes:</para>
   </listitem>
 
   <listitem>
+    <para>Previous versions of Nixpkgs had support for all versions of the LTS
+    Haskell package set. That support has been dropped. The previously provided
+    <literal>haskell.packages.lts-x_y</literal> package sets still exist in
+    name to aviod breaking user code, but these package sets don't actually
+    contain the versions mandated by the corresponding LTS release. Instead,
+    our package set it loosely based on the latest available LTS release, i.e.
+    LTS 7.x at the time of this writing. New releases of NixOS and Nixpkgs will
+    drop those old names entirely. <link
+    xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2016-June/020585.html">The
+    motivation for this change</link> has been discussed at length on the
+    <literal>nix-dev</literal> mailing list and in <link
+    xlink:href="https://github.com/NixOS/nixpkgs/issues/14897">Github issue
+    #14897</link>. Development strategies for Haskell hackers who want to rely
+    on Nix and NixOS have been described in <link
+    xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2016-June/020642.html">another
+    nix-dev article</link>.</para>
+  </listitem>
+
+  <listitem>
     <para>Shell aliases for systemd sub-commands
     <link xlink:href="https://github.com/NixOS/nixpkgs/pull/15598">were dropped</link>:
     <command>start</command>, <command>stop</command>,
@@ -58,16 +101,20 @@ following incompatible changes:</para>
   </listitem>
 
   <listitem>
-    <para>/var/setuid-wrappers/
-      <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18124">is now a symlink so
-      it can be atomically updated</link>
-      and it's not mounted as tmpfs anymore since setuid binaries are located on /run/ as tmpfs.
+    <para>
+      <literal>/var/empty</literal> is now immutable. Activation script runs <command>chattr +i</command>
+      to forbid any modifications inside the folder. See <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18365">
+      the pull request</link> for what bugs this caused.
     </para>
   </listitem>
 
   <listitem>
-    <para>Gitlab's maintainence script gitlab-runner was removed and split up into the more clearer
-      gitlab-run and gitlab-rake scripts because gitlab-runner is a component of Gitlab CI.</para>
+    <para>Gitlab's maintainance script
+    <command>gitlab-runner</command> was removed and split up into the
+    more clearer <command>gitlab-run</command> and
+    <command>gitlab-rake</command> scripts, because
+    <command>gitlab-runner</command> is a component of Gitlab
+    CI.</para>
   </listitem>
 
   <listitem>
@@ -80,16 +127,43 @@ following incompatible changes:</para>
   <listitem>
     <para><literal>fonts.fontconfig.ultimate.rendering</literal> was removed
     because our presets were obsolete for some time. New presets are hardcoded
-    into freetype; one selects a preset via <literal>fonts.fontconfig.ultimate.preset</literal>.
+    into FreeType; you can select a preset via <literal>fonts.fontconfig.ultimate.preset</literal>.
     You can customize those presets via ordinary environment variables, using
     <literal>environment.variables</literal>.</para>
   </listitem>
 
   <listitem>
     <para>The <literal>audit</literal> service is no longer enabled by default.
-    Use <literal>security.audit.enable = true;</literal> to explicitly enable it.</para>
+    Use <literal>security.audit.enable = true</literal> to explicitly enable it.</para>
   </listitem>
 
+  <listitem>
+    <para>
+      <literal>pkgs.linuxPackages.virtualbox</literal> now contains only the
+      kernel modules instead of the VirtualBox user space binaries.
+      If you want to reference the user space binaries, you have to use the new
+      <literal>pkgs.virtualbox</literal> instead.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para><literal>goPackages</literal> was replaced with separated Go
+    applications in appropriate <literal>nixpkgs</literal>
+    categories. Each Go package uses its own dependency set. There's
+    also a new <literal>go2nix</literal> tool introduced to generate a
+    Go package definition from its Go source automatically.</para>
+  </listitem>
+
+  <listitem>
+    <para><literal>services.mongodb.extraConfig</literal> configuration format
+    was changed to YAML.</para>
+  </listitem>
+
+  <listitem>
+    <para>
+      PHP has been upgraded to 7.0
+    </para>
+  </listitem>
 </itemizedlist>
 
 
@@ -105,6 +179,60 @@ following incompatible changes:</para>
   functionality.  See <xref linkend="sec-grsecurity" /> for documentation
   </para></listitem>
 
+  <listitem><para>Special filesystems, like <literal>/proc</literal>,
+  <literal>/run</literal> and others, now have the same mount options
+  as recommended by systemd and are unified across different places in
+  NixOS.  Mount options are updated during <command>nixos-rebuild
+  switch</command> if possible. One benefit from this is improved
+  security — most such filesystems are now mounted with
+  <literal>noexec</literal>, <literal>nodev</literal> and/or
+  <literal>nosuid</literal> options.</para></listitem>
+
+  <listitem><para>The reverse path filter was interfering with DHCPv4 server
+  operation in the past. An exception for DHCPv4 and a new option to log
+  packets that were dropped due to the reverse path filter was added
+  (<literal>networking.firewall.logReversePathDrops</literal>) for easier
+  debugging.</para></listitem>
+
+  <listitem><para>Containers configuration within
+  <literal>containers.&lt;name&gt;.config</literal> is <link
+  xlink:href="https://github.com/NixOS/nixpkgs/pull/17365">now
+  properly typed and checked</link>. In particular, partial
+  configurations are merged correctly.</para></listitem>
+
+  <listitem>
+    <para>The directory container setuid wrapper programs,
+    <filename>/var/setuid-wrappers</filename>, <link
+    xlink:href="https://github.com/NixOS/nixpkgs/pull/18124">is now
+    updated atomically to prevent failures if the switch to a new
+    configuration is interrupted.</link></para>
+  </listitem>
+
+  <listitem>
+    <para><literal>services.xserver.startGnuPGAgent</literal>
+      has been removed due to GnuPG 2.1.x bump. See <link
+        xlink:href="https://github.com/NixOS/nixpkgs/commit/5391882ebd781149e213e8817fba6ac3c503740c">
+        how to achieve similar behavior</link>. You might need to
+      <literal>pkill gpg-agent</literal> after the upgrade
+      to prevent a stale agent being in the way.
+    </para>
+  </listitem>
+
+  <listitem><para>
+    <link xlink:href="https://github.com/NixOS/nixpkgs/commit/e561edc322d275c3687fec431935095cfc717147">
+    Declarative users could share the uid due to the bug in
+    the script handling conflict resolution.
+    </link>
+  </para></listitem>
+
+  <listitem><para>
+    Gummi boot has been replaced using systemd-boot.
+  </para></listitem>
+
+  <listitem><para>
+    Hydra package and NixOS module were added for convenience.
+  </para></listitem>
+
 </itemizedlist>
 
 
diff --git a/nixos/doc/manual/release-notes/rl-1703.xml b/nixos/doc/manual/release-notes/rl-1703.xml
new file mode 100644
index 00000000000..d8b0ae01e33
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-1703.xml
@@ -0,0 +1,106 @@
+<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">
+
+<title>Release 17.03 (“XXX”, 2017/03/??)</title>
+
+<para>In addition to numerous new and upgraded packages, this release
+has the following highlights: </para>
+
+<itemizedlist>
+  <listitem>
+    <para></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>
+      <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>.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+      The attribute name of the Radicale daemon has been changed from
+      <literal>pythonPackages.radicale</literal> to
+      <literal>radicale</literal>.
+    </para>
+  </listitem>
+
+  <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
+      <literal>strippedName</literal>.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>PHP now scans for extra configuration .ini files in /etc/php.d
+    instead of /etc. This prevents accidentally loading non-PHP .ini files
+    that may be in /etc.
+    </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
+     or attribute sets as specified in parsoid's documentation.
+    </para>
+  </listitem>
+
+  <listitem>
+    <para>
+     <literal>Ntpd</literal> was replaced by
+     <literal>systemd-timesyncd</literal> as the default service to synchronize
+     system time with a remote NTP server. The old behavior can be restored by
+     setting <literal>services.ntp.enable</literal> to <literal>true</literal>.
+     Upstream time servers for all NTP implementations are now configured using
+     <literal>networking.timeServers</literal>.
+   </para>
+  </listitem>
+</itemizedlist>
+
+
+<para>Other notable improvements:</para>
+
+<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>
+
+
+</section>
diff --git a/nixos/lib/build-vms.nix b/nixos/lib/build-vms.nix
index 4e044deca21..1a17a080ba4 100644
--- a/nixos/lib/build-vms.nix
+++ b/nixos/lib/build-vms.nix
@@ -9,6 +9,8 @@ rec {
 
   inherit pkgs;
 
+  qemu = pkgs.qemu_test;
+
 
   # Build a virtual network from an attribute set `{ machine1 =
   # config1; ... machineN = configN; }', where `machineX' is the
@@ -27,6 +29,7 @@ rec {
         [ ../modules/virtualisation/qemu-vm.nix
           ../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs
           { key = "no-manual"; services.nixosManual.enable = false; }
+          { key = "qemu"; system.build.qemu = qemu; }
         ] ++ optional minimal ../modules/testing/minimal-kernel.nix;
       extraArgs = { inherit nodes; };
     };
diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix
index a87b285c5b7..2e7971cca81 100644
--- a/nixos/lib/eval-config.nix
+++ b/nixos/lib/eval-config.nix
@@ -1,4 +1,4 @@
-# From an end-user configuration file (`configuration'), build a NixOS
+# From an end-user configuration file (`configuration.nix'), build a NixOS
 # configuration object (`config') from which we can retrieve option
 # values.
 
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 58d0cb38d75..e279803f2ea 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -27,6 +27,10 @@
 
 , name ? "nixos-disk-image"
 
+  # This prevents errors while checking nix-store validity, see
+  # https://github.com/NixOS/nix/issues/1134
+, fixValidity ? true
+
 , format ? "raw"
 }:
 
@@ -61,9 +65,6 @@ pkgs.vmTools.runInLinuxVM (
 
       # Create an empty filesystem and mount it.
       mkfs.${fsType} -L nixos $rootDisk
-      ${optionalString (fsType == "ext4") ''
-        tune2fs -c 0 -i 0 $rootDisk
-      ''}
       mkdir /mnt
       mount $rootDisk /mnt
 
@@ -71,9 +72,11 @@ pkgs.vmTools.runInLinuxVM (
       printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
           ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group ""
 
-      # 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 ""
+      ${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…
       ln -s vda /dev/xvda
@@ -97,7 +100,9 @@ pkgs.vmTools.runInLinuxVM (
 
       umount /mnt
 
-      # Do a fsck to make sure resize2fs works.
-      fsck.${fsType} -f -y $rootDisk
+      # Make sure resize2fs works
+      ${optionalString (fsType == "ext4") ''
+        tune2fs -c 0 -i 0 $rootDisk
+      ''}
     ''
 )
diff --git a/nixos/lib/make-squashfs.nix b/nixos/lib/make-squashfs.nix
index 3b640334e17..4100af27bec 100644
--- a/nixos/lib/make-squashfs.nix
+++ b/nixos/lib/make-squashfs.nix
@@ -25,6 +25,6 @@ stdenv.mkDerivation {
 
       # Generate the squashfs image.
       mksquashfs nix-path-registration $storePaths $out \
-        -keep-as-directory -all-root
+        -keep-as-directory -all-root -b 1048576 -comp xz -Xdict-size 100%
     '';
 }
diff --git a/nixos/lib/make-system-tarball.sh b/nixos/lib/make-system-tarball.sh
index e04455e889b..73a009d8488 100644
--- a/nixos/lib/make-system-tarball.sh
+++ b/nixos/lib/make-system-tarball.sh
@@ -52,9 +52,10 @@ $extraCommands
 
 mkdir -p $out/tarball
 
-tar cvJf $out/tarball/$fileName.tar.xz * $extraArgs
+rm env-vars
+
+tar --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner -cvJf $out/tarball/$fileName.tar.xz * $extraArgs
 
 mkdir -p $out/nix-support
 echo $system > $out/nix-support/system
 echo "file system-tarball $out/tarball/$fileName.tar.xz" > $out/nix-support/hydra-build-products
-
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index 1a243918c22..14c39e859bc 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -504,6 +504,31 @@ sub screenshot {
     }, { image => $name } );
 }
 
+# Get the text of TTY<n>
+sub getTTYText {
+    my ($self, $tty) = @_;
+
+    my ($status, $out) = $self->execute("fold -w 80 /dev/vcs${tty}");
+    return $out;
+}
+
+# Wait until TTY<n>'s text matches a particular regular expression
+sub waitUntilTTYMatches {
+    my ($self, $tty, $regexp) = @_;
+
+    $self->nest("waiting for $regexp to appear on tty $tty", sub {
+        retry sub {
+            return 1 if $self->getTTYText($tty) =~ /$regexp/;
+        }
+    });
+}
+
+# Debugging: Dump the contents of the TTY<n>
+sub dumpTTYContents {
+    my ($self, $tty) = @_;
+
+    $self->execute("fold -w 80 /dev/vcs${tty} | systemd-cat");
+}
 
 # Take a screenshot and return the result as text using optical character
 # recognition.
@@ -586,11 +611,37 @@ sub copyFileFromHost {
 }
 
 
+my %charToKey = (
+    '!' => "shift-0x02",
+    '@' => "shift-0x03",
+    '#' => "shift-0x04",
+    '$' => "shift-0x05",
+    '%' => "shift-0x06",
+    '^' => "shift-0x07",
+    '&' => "shift-0x08",
+    '*' => "shift-0x09",
+    '(' => "shift-0x0A",
+    ')' => "shift-0x0B",
+    '-' => "0x0C", '_' => "shift-0x0C",
+    '=' => "0x0D", '+' => "shift-0x0D",
+    '[' => "0x1A", '{' => "shift-0x1A",
+    ']' => "0x1B", '}' => "shift-0x1B",
+    ';' => "0x27", ':' => "shift-0x27",
+   '\'' => "0x28", '"' => "shift-0x28",
+    '`' => "0x29", '~' => "shift-0x29",
+   '\\' => "0x2B", '|' => "shift-0x2B",
+    ',' => "0x33", '<' => "shift-0x33",
+    '.' => "0x34", '>' => "shift-0x34",
+    '/' => "0x35", '?' => "shift-0x35",
+    ' ' => "spc",
+   "\n" => "ret",
+);
+
+
 sub sendKeys {
     my ($self, @keys) = @_;
     foreach my $key (@keys) {
-        $key = "spc" if $key eq " ";
-        $key = "ret" if $key eq "\n";
+        $key = $charToKey{$key} if exists $charToKey{$key};
         $self->sendMonitorCommand("sendkey $key");
     }
 }
diff --git a/nixos/lib/test-driver/test-driver.pl b/nixos/lib/test-driver/test-driver.pl
index 8ad0d67f68c..854be99552a 100644
--- a/nixos/lib/test-driver/test-driver.pl
+++ b/nixos/lib/test-driver/test-driver.pl
@@ -8,6 +8,7 @@ use IO::Pty;
 use Logger;
 use Cwd;
 use POSIX qw(_exit dup2);
+use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
 
 $SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
 
@@ -179,7 +180,12 @@ END {
     $log->close();
 }
 
+my $now1 = clock_gettime(CLOCK_MONOTONIC);
 
 runTests;
 
+my $now2 = clock_gettime(CLOCK_MONOTONIC);
+
+printf STDERR "test script finished in %.2fs\n", $now2 - $now1;
+
 exit ($nrSucceeded < $nrTests ? 1 : 0);
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 61b214bfba8..c1cb5072aca 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -29,7 +29,7 @@ rec {
         cp ${./test-driver/Logger.pm} $libDir/Logger.pm
 
         wrapProgram $out/bin/nixos-test-driver \
-          --prefix PATH : "${lib.makeBinPath [ qemu_kvm vde2 netpbm coreutils ]}" \
+          --prefix PATH : "${lib.makeBinPath [ qemu vde2 netpbm coreutils ]}" \
           --prefix PERL5LIB : "${with perlPackages; lib.makePerlPath [ TermReadLineGnu XMLWriter IOTty FileSlurp ]}:$out/lib/perl5/site_perl"
       '';
   };
@@ -93,7 +93,7 @@ rec {
 
       vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 
-      ocrProg = tesseract.override { enableLanguages = [ "eng" ]; };
+      ocrProg = tesseract;
 
       # Generate onvenience wrappers for running the test driver
       # interactively with the specified network, and for starting the
@@ -157,9 +157,7 @@ rec {
         ${coreutils}/bin/mkdir -p $TMPDIR
         cd $TMPDIR
 
-        $origBuilder $origArgs
-
-        exit $?
+        exec $origBuilder $origArgs
       '';
 
       testScript = ''
@@ -172,9 +170,22 @@ rec {
       '';
 
       vmRunCommand = writeText "vm-run" ''
+        xchg=vm-state-client/xchg
         ${coreutils}/bin/mkdir $out
-        ${coreutils}/bin/mkdir -p vm-state-client/xchg
-        export > vm-state-client/xchg/saved-env
+        ${coreutils}/bin/mkdir -p $xchg
+
+        for i in $passAsFile; do
+          i2=''${i}Path
+          _basename=$(${coreutils}/bin/basename ''${!i2})
+          ${coreutils}/bin/cp ''${!i2} $xchg/$_basename
+          eval $i2=/tmp/xchg/$_basename
+          ${coreutils}/bin/ls -la $xchg
+        done
+
+        unset i i2 _basename
+        export | ${gnugrep}/bin/grep -v '^xchg=' > $xchg/saved-env
+        unset xchg
+
         export tests='${testScript}'
         ${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
       ''; # */
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index e26caa19164..0750a1b18c9 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -1,4 +1,8 @@
-#! /bin/sh -e
+#!/usr/bin/env nix-shell
+#! nix-shell -i bash -p qemu ec2_ami_tools jq ec2_api_tools awscli
+
+# To start with do: nix-shell -p awscli --run "aws configure"
+
 
 set -o pipefail
 #set -x
@@ -15,7 +19,7 @@ rm -f ec2-amis.nix
 
 types="hvm pv"
 stores="ebs s3"
-regions="eu-west-1 eu-central-1 us-east-1 us-west-1 us-west-2 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2 sa-east-1 ap-south-1"
+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"
 
 for type in $types; do
     link=$stateDir/$type
@@ -57,7 +61,7 @@ for type in $types; do
                     ami=$(aws ec2 copy-image \
                         --region "$region" \
                         --source-region "$prevRegion" --source-image-id "$prevAmi" \
-                        --name "$name" --description "$description" | json -q .ImageId)
+                        --name "$name" --description "$description" | jq -r '.ImageId')
                     if [ "$ami" = null ]; then break; fi
                 else
 
diff --git a/nixos/modules/config/debug-info.nix b/nixos/modules/config/debug-info.nix
index 17cb862d291..49991d22a93 100644
--- a/nixos/modules/config/debug-info.nix
+++ b/nixos/modules/config/debug-info.nix
@@ -17,12 +17,10 @@ with lib;
         where tools such as <command>gdb</command> can find them.
         If you need debug symbols for a package that doesn't
         provide them by default, you can enable them as follows:
-        <!-- FIXME: ugly, see #10721 -->
         <programlisting>
         nixpkgs.config.packageOverrides = pkgs: {
-          hello = overrideDerivation pkgs.hello (attrs: {
-            outputs = attrs.outputs or ["out"] ++ ["debug"];
-            buildInputs = attrs.buildInputs ++ [&lt;nixpkgs/pkgs/build-support/setup-hooks/separate-debug-info.sh>];
+          hello = pkgs.hello.overrideAttrs (oldAttrs: {
+            separateDebugInfo = true;
           });
         };
         </programlisting>
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 770c3a03f9d..52ad1e714fb 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -301,9 +301,7 @@ in
           };
 
           style = mkOption {
-            type = types.str // {
-              check = flip elem ["none" "slight" "medium" "full"];
-            };
+            type = types.enum ["none" "slight" "medium" "full"];
             default = "full";
             description = ''
               TrueType hinting style, one of <literal>none</literal>,
@@ -329,9 +327,7 @@ in
             default = "rgb";
             type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
             description = ''
-              Subpixel order, one of <literal>none</literal>,
-              <literal>rgb</literal>, <literal>bgr</literal>,
-              <literal>vrgb</literal>, or <literal>vbgr</literal>.
+              Subpixel order.
             '';
           };
 
@@ -339,9 +335,7 @@ in
             default = "default";
             type = types.enum ["none" "default" "light" "legacy"];
             description = ''
-              FreeType LCD filter, one of <literal>none</literal>,
-              <literal>default</literal>, <literal>light</literal>, or
-              <literal>legacy</literal>.
+              FreeType LCD filter.
             '';
           };
 
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index f913b8c33e5..af3d93fc1bc 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -11,17 +11,27 @@ with lib;
       # TODO: find another name for it.
       fonts = mkOption {
         type = types.listOf types.path;
+        default = [];
         example = literalExample "[ pkgs.dejavu_fonts ]";
         description = "List of primary font paths.";
       };
 
+      enableDefaultFonts = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable a basic set of fonts providing several font styles
+          and families and reasonable coverage of Unicode.
+        '';
+      };
+
     };
 
   };
 
   config = {
 
-    fonts.fonts =
+    fonts.fonts = mkIf config.fonts.enableDefaultFonts
       [
         pkgs.xorg.fontbhlucidatypewriter100dpi
         pkgs.xorg.fontbhlucidatypewriter75dpi
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
index f8c35b440d1..ef48ccb7b4f 100644
--- a/nixos/modules/config/gnu.nix
+++ b/nixos/modules/config/gnu.nix
@@ -7,11 +7,11 @@ with lib;
     gnu = mkOption {
       type = types.bool;
       default = false;
-      description =
-        '' When enabled, GNU software is chosen by default whenever a there is
-           a choice between GNU and non-GNU software (e.g., GNU lsh
-           vs. OpenSSH).
-        '';
+      description = ''
+        When enabled, GNU software is chosen by default whenever a there is
+        a choice between GNU and non-GNU software (e.g., GNU lsh
+        vs. OpenSSH).
+      '';
     };
   };
 
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index e341931aacc..799f0793c74 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -44,8 +44,9 @@ in
       consolePackages = mkOption {
         type = types.listOf types.package;
         default = with pkgs.kbdKeymaps; [ dvp neo ];
+        defaultText = ''with pkgs.kbdKeymaps; [ dvp neo ]'';
         description = ''
-	  List of additional packages that provide console fonts, keymaps and
+          List of additional packages that provide console fonts, keymaps and
           other resources.
         '';
       };
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 8a2e630a917..9e7cfbd686c 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -29,6 +29,19 @@ in
       '';
     };
 
+    networking.hostConf = lib.mkOption {
+      type = types.lines;
+      default = "multi on";
+      example = ''
+        multi on
+        reorder on
+        trim lan
+      '';
+      description = ''
+        The contents of <filename>/etc/host.conf</filename>. See also <citerefentry><refentrytitle>host.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+      '';
+    };
+
     networking.dnsSingleRequest = lib.mkOption {
       type = types.bool;
       default = false;
@@ -44,7 +57,7 @@ in
 
     networking.dnsExtensionMechanism = lib.mkOption {
       type = types.bool;
-      default = false;
+      default = true;
       description = ''
         Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With
         that option set, <code>glibc</code> supports use of the extension mechanisms for
@@ -71,6 +84,18 @@ in
       '';
     };
 
+    networking.timeServers = mkOption {
+      default = [
+        "0.nixos.pool.ntp.org"
+        "1.nixos.pool.ntp.org"
+        "2.nixos.pool.ntp.org"
+        "3.nixos.pool.ntp.org"
+      ];
+      description = ''
+        The set of NTP servers from which to synchronise.
+      '';
+    };
+
     networking.proxy = {
 
       default = lib.mkOption {
@@ -171,6 +196,9 @@ in
             ${cfg.extraHosts}
           '';
 
+        # /etc/host.conf: resolver configuration file
+        "host.conf".text = cfg.hostConf;
+
         # /etc/resolvconf.conf: Configuration for openresolv.
         "resolvconf.conf".text =
             ''
@@ -223,16 +251,16 @@ in
     # Install the proxy environment variables
     environment.sessionVariables = cfg.proxy.envVars;
 
-    # The ‘ip-up’ target is started when we have IP connectivity.  So
-    # services that depend on IP connectivity (like ntpd) should be
-    # pulled in by this target.
-    systemd.targets.ip-up.description = "Services Requiring IP Connectivity";
+    # 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
     # with /etc/resolv.conf directly don't break.
-    system.activationScripts.resolvconf = stringAfter [ "etc" "tmpfs" "var" ]
+    system.activationScripts.resolvconf = stringAfter [ "etc" "specialfs" "var" ]
       ''
         # Systemd resolved controls its own resolv.conf
         rm -f /run/resolvconf/interfaces/systemd
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index f30136be44e..4e5de421d8a 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -9,10 +9,28 @@ let
   inherit (config.services.avahi) nssmdns;
   inherit (config.services.samba) nsswins;
   ldap = (config.users.ldap.enable && config.users.ldap.nsswitch);
+  sssd = config.services.sssd.enable;
 
-in
+  hostArray = [ "files" "mymachines" ]
+    ++ optionals nssmdns [ "mdns_minimal [!UNAVAIL=return]" ]
+    ++ optionals nsswins [ "wins" ]
+    ++ [ "dns" ]
+    ++ optionals nssmdns [ "mdns" ]
+    ++ ["myhostname" ];
 
-{
+  passwdArray = [ "files" ]
+    ++ optional sssd "sss"
+    ++ optionals ldap [ "ldap" ]
+    ++ [ "mymachines" ];
+
+  shadowArray = [ "files" ]
+    ++ optional sssd "sss"
+    ++ optionals ldap [ "ldap" ];
+
+  servicesArray = [ "files" ]
+    ++ optional sssd "sss";
+
+in {
   options = {
 
     # NSS modules.  Hacky!
@@ -39,24 +57,26 @@ in
     # Name Service Switch configuration file.  Required by the C
     # library.  !!! Factor out the mdns stuff.  The avahi module
     # should define an option used by this module.
-    environment.etc."nsswitch.conf".text =
-      ''
-        passwd:    files ${optionalString ldap "ldap"}
-        group:     files ${optionalString ldap "ldap"}
-        shadow:    files ${optionalString ldap "ldap"}
-        hosts:     files ${optionalString nssmdns "mdns_minimal [NOTFOUND=return]"} dns ${optionalString nssmdns "mdns"} ${optionalString nsswins "wins"} myhostname mymachines
-        networks:  files dns
-        ethers:    files
-        services:  files
-        protocols: files
-      '';
+    environment.etc."nsswitch.conf".text = ''
+      passwd:    ${concatStringsSep " " passwdArray}
+      group:     ${concatStringsSep " " passwdArray}
+      shadow:    ${concatStringsSep " " shadowArray}
+
+      hosts:     ${concatStringsSep " " hostArray}
+      networks:  files
+
+      ethers:    files
+      services:  ${concatStringsSep " " servicesArray}
+      protocols: files
+      rpc:       files
+    '';
 
     # Systemd provides nss-myhostname to ensure that our hostname
     # always resolves to a valid IP address.  It returns all locally
     # configured IP addresses, or ::1 and 127.0.0.2 as
     # fallbacks. Systemd also provides nss-mymachines to return IP
     # addresses of local containers.
-    system.nssModules = [ config.systemd.package ];
+    system.nssModules = [ config.systemd.package.out ];
 
   };
 }
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
index fbd7867a095..a4a4d6e1a6a 100644
--- a/nixos/modules/config/power-management.nix
+++ b/nixos/modules/config/power-management.nix
@@ -69,7 +69,7 @@ in
 
   config = mkIf cfg.enable {
 
-    # FIXME: Implement powersave governor for sandy bridge or later Intel CPUs
+    # Leftover for old setups, should be set by nixos-generate-config now
     powerManagement.cpuFreqGovernor = mkDefault "ondemand";
 
     systemd.targets.post-resume = {
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 06ce5200420..742167fbf69 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -212,6 +212,7 @@ in {
       # Allow PulseAudio to get realtime priority using rtkit.
       security.rtkit.enable = true;
 
+      systemd.packages = [ cfg.package ];
     })
 
     (mkIf hasZeroconf {
@@ -227,31 +228,14 @@ in {
         target = "pulse/default.pa";
         source = myConfigFile;
       };
-
       systemd.user = {
         services.pulseaudio = {
-          description = "PulseAudio Server";
-          # NixOS doesn't support "Also" so we bring it in manually
-          wantedBy = [ "default.target" ];
           serviceConfig = {
-            Type = "notify";
-            ExecStart = binaryNoDaemon;
-            Restart = "on-failure";
             RestartSec = "500ms";
           };
           environment = { DISPLAY = ":${toString config.services.xserver.display}"; };
           restartIfChanged = true;
         };
-
-        sockets.pulseaudio = {
-          description = "PulseAudio Socket";
-          wantedBy = [ "sockets.target" ];
-          socketConfig = {
-            Priority = 6;
-            Backlog = 5;
-            ListenStream = "%t/pulse/native";
-          };
-        };
       };
     })
 
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 54dd6f6570f..7003c074522 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -41,7 +41,7 @@ in
         strings.  The latter is concatenated, interspersed with colon
         characters.
       '';
-      type = types.attrsOf (types.loeOf types.str);
+      type = with types; attrsOf (either str (listOf str));
       apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
     };
 
diff --git a/nixos/modules/config/system-environment.nix b/nixos/modules/config/system-environment.nix
index 3362400326d..6011e354ece 100644
--- a/nixos/modules/config/system-environment.nix
+++ b/nixos/modules/config/system-environment.nix
@@ -23,7 +23,7 @@ in
         strings.  The latter is concatenated, interspersed with colon
         characters.
       '';
-      type = types.attrsOf (types.loeOf types.str);
+      type = with types; attrsOf (either str (listOf str));
       apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
     };
 
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index 9708b5d9fe3..3ac5f634c7a 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -34,10 +34,10 @@ let
       config.programs.ssh.package
       pkgs.perl
       pkgs.procps
+      pkgs.rsync
       pkgs.strace
       pkgs.su
       pkgs.time
-      pkgs.texinfoInteractive
       pkgs.utillinux
       pkgs.which # 88K size
     ];
@@ -76,7 +76,7 @@ in
       extraOutputsToInstall = mkOption {
         type = types.listOf types.str;
         default = [ ];
-        example = [ "doc" "info" "docdev" ];
+        example = [ "doc" "info" "devdoc" ];
         description = "List of additional package outputs to be symlinked into <filename>/run/current-system/sw</filename>.";
       };
 
@@ -104,7 +104,6 @@ in
         "/etc/xdg"
         "/etc/gtk-2.0"
         "/etc/gtk-3.0"
-        "/info"
         "/lib" # FIXME: remove and update debug-info.nix
         "/sbin"
         "/share/applications"
@@ -112,7 +111,6 @@ in
         "/share/doc"
         "/share/emacs"
         "/share/icons"
-        "/share/info"
         "/share/menus"
         "/share/mime"
         "/share/nano"
@@ -120,6 +118,7 @@ in
         "/share/terminfo"
         "/share/themes"
         "/share/vim-plugins"
+        "/share/vulkan"
       ];
 
     system.path = pkgs.buildEnv {
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
index b9844b4adad..39a45042c6c 100644
--- a/nixos/modules/config/timezone.nix
+++ b/nixos/modules/config/timezone.nix
@@ -37,14 +37,15 @@ in
 
     environment.sessionVariables.TZDIR = "/etc/zoneinfo";
 
+    # This way services are restarted when tzdata changes.
     systemd.globalEnvironment.TZDIR = tzdir;
 
     environment.etc.localtime =
-      { source = "${tzdir}/${config.time.timeZone}";
+      { source = "/etc/zoneinfo/${config.time.timeZone}";
         mode = "direct-symlink";
       };
 
-    environment.etc.zoneinfo.source = "${pkgs.tzdata}/share/zoneinfo";
+    environment.etc.zoneinfo.source = tzdir;
 
   };
 
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index d92deb85d2a..57e4940378b 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -131,13 +131,12 @@ let
       };
 
       subUidRanges = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule subordinateUidRange);
         default = [];
         example = [
           { startUid = 1000; count = 1; }
           { startUid = 100001; count = 65534; }
         ];
-        options = [ subordinateUidRange ];
         description = ''
           Subordinate user ids that user is allowed to use.
           They are set into <filename>/etc/subuid</filename> and are used
@@ -146,13 +145,12 @@ let
       };
 
       subGidRanges = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule subordinateGidRange);
         default = [];
         example = [
           { startGid = 100; count = 1; }
           { startGid = 1001; count = 999; }
         ];
-        options = [ subordinateGidRange ];
         description = ''
           Subordinate group ids that user is allowed to use.
           They are set into <filename>/etc/subgid</filename> and are used
@@ -310,32 +308,36 @@ let
   };
 
   subordinateUidRange = {
-    startUid = mkOption {
-      type = types.int;
-      description = ''
-        Start of the range of subordinate user ids that user is
-        allowed to use.
-      '';
-    };
-    count = mkOption {
-      type = types.int;
-      default = 1;
-      description = ''Count of subordinate user ids'';
+    options = {
+      startUid = mkOption {
+        type = types.int;
+        description = ''
+          Start of the range of subordinate user ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''Count of subordinate user ids'';
+      };
     };
   };
 
   subordinateGidRange = {
-    startGid = mkOption {
-      type = types.int;
-      description = ''
-        Start of the range of subordinate group ids that user is
-        allowed to use.
-      '';
-    };
-    count = mkOption {
-      type = types.int;
-      default = 1;
-      description = ''Count of subordinate group ids'';
+    options = {
+      startGid = mkOption {
+        type = types.int;
+        description = ''
+          Start of the range of subordinate group ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''Count of subordinate group ids'';
+      };
     };
   };
 
@@ -428,7 +430,7 @@ in {
 
     users.users = mkOption {
       default = {};
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule userOpts);
       example = {
         alice = {
           uid = 1234;
@@ -444,7 +446,6 @@ in {
         Additional user accounts to be created automatically by the system.
         This can also be used to set options for root.
       '';
-      options = [ userOpts ];
     };
 
     users.groups = mkOption {
@@ -453,11 +454,10 @@ in {
         { students.gid = 1001;
           hackers = { };
         };
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule groupOpts);
       description = ''
         Additional groups to be created automatically by the system.
       '';
-      options = [ groupOpts ];
     };
 
     # FIXME: obsolete - will remove.
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index 0bc574d4819..c4fad9a6672 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -14,7 +14,7 @@ let
     name = "mesa-drivers+txc-${p.mesa_drivers.version}";
     paths =
       [ p.mesa_drivers
-        p.mesa_noglu # mainly for libGL
+        p.mesa_drivers.out # mainly for libGL
         (if cfg.s3tcSupport then p.libtxc_dxtn else p.libtxc_dxtn_s2tc)
       ];
   };
@@ -135,6 +135,12 @@ in
     environment.sessionVariables.LD_LIBRARY_PATH =
       [ "/run/opengl-driver/lib" "/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
+    '';
+
     hardware.opengl.package = mkDefault (makePackage pkgs);
     hardware.opengl.package32 = mkDefault (makePackage pkgs_i686);
 
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
new file mode 100644
index 00000000000..979810abf90
--- /dev/null
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -0,0 +1,54 @@
+# This module provides the proprietary AMDGPU-PRO drivers.
+
+{ config, lib, pkgs, pkgs_i686, ... }:
+
+with lib;
+
+let
+
+  drivers = config.services.xserver.videoDrivers;
+
+  enabled = elem "amdgpu-pro" drivers;
+
+  package = config.boot.kernelPackages.amdgpu-pro;
+  package32 = pkgs_i686.linuxPackages.amdgpu-pro.override { libsOnly = true; kernel = null; };
+
+  opengl = config.hardware.opengl;
+
+in
+
+{
+
+  config = mkIf enabled {
+
+    services.xserver.drivers = singleton
+      { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
+
+    hardware.opengl.package = package;
+    hardware.opengl.package32 = package32;
+
+    boot.extraModulePackages = [ package ];
+
+    boot.blacklistedKernelModules = [ "radeon" ];
+
+    hardware.firmware = [ package ];
+
+    system.activationScripts.setup-amdgpu-pro = ''
+      mkdir -p /run/lib
+      ln -sfn ${package}/lib ${package.libCompatDir}
+    '' + optionalString opengl.driSupport32Bit ''
+      ln -sfn ${package32}/lib ${package32.libCompatDir}
+    '';
+
+    environment.etc = {
+      "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/bumblebee.nix b/nixos/modules/hardware/video/bumblebee.nix
index 69db518ab21..3ce97ad31c2 100644
--- a/nixos/modules/hardware/video/bumblebee.nix
+++ b/nixos/modules/hardware/video/bumblebee.nix
@@ -13,6 +13,8 @@ let
     useDisplayDevice = cfg.connectDisplay;
   };
 
+  useBbswitch = cfg.pmMethod == "bbswitch";
+
   primus = pkgs.primus.override {
     inherit useNvidia;
   };
@@ -22,58 +24,69 @@ in
 {
 
   options = {
-    hardware.bumblebee.enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Enable the bumblebee daemon to manage Optimus hybrid video cards.
-        This should power off secondary GPU until its use is requested
-        by running an application with optirun.
-
-        Only nvidia driver is supported so far.
-      '';
-    };
-    hardware.bumblebee.group = mkOption {
-      default = "wheel";
-      example = "video";
-      type = types.str;
-      description = ''Group for bumblebee socket'';
-    };
+    hardware.bumblebee = {
 
-    hardware.bumblebee.connectDisplay = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Set to true if you intend to connect your discrete card to a
-        monitor. This option will set up your Nvidia card for EDID
-        discovery and to turn on the monitor signal.
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable the bumblebee daemon to manage Optimus hybrid video cards.
+          This should power off secondary GPU until its use is requested
+          by running an application with optirun.
+        '';
+      };
 
-        Only nvidia driver is supported so far.
-      '';
-    };
+      group = mkOption {
+        default = "wheel";
+        example = "video";
+        type = types.str;
+        description = ''Group for bumblebee socket'';
+      };
+
+      connectDisplay = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Set to true if you intend to connect your discrete card to a
+          monitor. This option will set up your Nvidia card for EDID
+          discovery and to turn on the monitor signal.
+
+          Only nvidia driver is supported so far.
+        '';
+      };
+
+      driver = mkOption {
+        default = "nvidia";
+        type = types.enum [ "nvidia" "nouveau" ];
+        description = ''
+          Set driver used by bumblebeed. Supported are nouveau and nvidia.
+        '';
+      };
+
+      pmMethod = mkOption {
+        default = "auto";
+        type = types.enum [ "auto" "bbswitch" "nouveau" "switcheroo" "none" ];
+        description = ''
+          Set preferred power management method for unused card.
+        '';
+      };
 
-    hardware.bumblebee.driver = mkOption {
-      default = "nvidia";
-      type = types.enum [ "nvidia" "nouveau" ];
-      description = ''
-        Set driver used by bumblebeed. Supported are nouveau and nvidia.
-      '';
     };
   };
 
-  config = mkIf config.hardware.bumblebee.enable {
-    boot.blacklistedKernelModules = [ "nouveau" "nvidia" ];
-    boot.kernelModules = [ "bbswitch" ];
-    boot.extraModulePackages = [ kernel.bbswitch ] ++ optional useNvidia kernel.nvidia_x11;
+  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;
 
     environment.systemPackages = [ bumblebee primus ];
 
     systemd.services.bumblebeed = {
       description = "Bumblebee Hybrid Graphics Switcher";
-      wantedBy = [ "display-manager.service" ];
-      path = [ kernel.bbswitch bumblebee ];
+      wantedBy = [ "multi-user.target" ];
+      before = [ "display-manager.service" ];
       serviceConfig = {
-        ExecStart = "${bumblebee}/bin/bumblebeed --use-syslog -g ${cfg.group} --driver ${cfg.driver}";
+        ExecStart = "${bumblebee}/bin/bumblebeed --use-syslog -g ${cfg.group} --driver ${cfg.driver} --pm-method ${cfg.pmMethod}";
       };
     };
   };
diff --git a/nixos/modules/hardware/video/displaylink.nix b/nixos/modules/hardware/video/displaylink.nix
new file mode 100644
index 00000000000..2a9382f3941
--- /dev/null
+++ b/nixos/modules/hardware/video/displaylink.nix
@@ -0,0 +1,61 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+  enabled = elem "displaylink" config.services.xserver.videoDrivers;
+
+  displaylink = config.boot.kernelPackages.displaylink;
+
+in
+
+{
+
+  config = mkIf enabled {
+
+    boot.extraModulePackages = [ displaylink ];
+
+    boot.kernelModules = [ "evdi" ];
+
+    # Those are taken from displaylink-installer.sh and from Arch Linux AUR package.
+
+    services.udev.extraRules = ''
+      ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{bNumInterfaces}=="*5", TAG+="uaccess"
+    '';
+
+    powerManagement.powerDownCommands = ''
+      #flush any bytes in pipe
+      while read -n 1 -t 1 SUSPEND_RESULT < /tmp/PmMessagesPort_out; do : ; done;
+
+      #suspend DisplayLinkManager
+      echo "S" > /tmp/PmMessagesPort_in
+
+      #wait until suspend of DisplayLinkManager finish
+      read -n 1 -t 10 SUSPEND_RESULT < /tmp/PmMessagesPort_out
+    '';
+
+    powerManagement.resumeCommands = ''
+      #resume DisplayLinkManager
+      echo "R" > /tmp/PmMessagesPort_in
+    '';
+
+    systemd.services.displaylink = {
+      description = "DisplayLink Manager Service";
+      after = [ "display-manager.service" ];
+      wantedBy = [ "graphical.target" ];
+
+      serviceConfig = {
+        ExecStart = "${displaylink}/bin/DisplayLinkManager";
+        Restart = "always";
+        RestartSec = 5;
+      };
+
+      preStart = ''
+        mkdir -p /var/log/displaylink
+      '';
+    };
+
+  };
+
+}
diff --git a/nixos/modules/i18n/input-method/default.nix b/nixos/modules/i18n/input-method/default.nix
index f3e568f1dde..7ed4a584d64 100644
--- a/nixos/modules/i18n/input-method/default.nix
+++ b/nixos/modules/i18n/input-method/default.nix
@@ -3,26 +3,27 @@
 with lib;
 let
   cfg = config.i18n.inputMethod;
-  gtk2_cache = pkgs.stdenv.mkDerivation {
-    preferLocalBuild = true; 
-    allowSubstitutes = false;
-    name = "gtk2-immodule.cache";
-    buildInputs = [ pkgs.gtk cfg.package ];
-    buildCommand = ''
+
+  gtk2_cache = pkgs.runCommand "gtk2-immodule.cache"
+    { preferLocalBuild = true;
+      allowSubstitutes = false;
+      buildInputs = [ pkgs.gtk2 cfg.package ];
+    }
+    ''
       mkdir -p $out/etc/gtk-2.0/
       GTK_PATH=${cfg.package}/lib/gtk-2.0/ gtk-query-immodules-2.0 > $out/etc/gtk-2.0/immodules.cache
     '';
-  };
-  gtk3_cache = pkgs.stdenv.mkDerivation {
-    preferLocalBuild = true; 
-    allowSubstitutes = false;
-    name = "gtk3-immodule.cache";
-    buildInputs = [ pkgs.gtk3 cfg.package ];
-    buildCommand = ''
+
+  gtk3_cache = pkgs.runCommand "gtk3-immodule.cache"
+    { preferLocalBuild = true;
+      allowSubstitutes = false;
+      buildInputs = [ pkgs.gtk3 cfg.package ];
+    }
+    ''
       mkdir -p $out/etc/gtk-3.0/
       GTK_PATH=${cfg.package}/lib/gtk-3.0/ gtk-query-immodules-3.0 > $out/etc/gtk-3.0/immodules.cache
     '';
-  };
+
 in
 {
   options.i18n = {
diff --git a/nixos/modules/i18n/input-method/default.xml b/nixos/modules/i18n/input-method/default.xml
index a32ed100df3..45d6daf068b 100644
--- a/nixos/modules/i18n/input-method/default.xml
+++ b/nixos/modules/i18n/input-method/default.xml
@@ -56,8 +56,18 @@ i18n.inputMethod = {
   <listitem><para>Table (<literal>ibus-engines.table</literal>): An input method 
       that load tables of input methods.</para></listitem>
   <listitem><para>table-others (<literal>ibus-engines.table-others</literal>): 
-      Various table-based input methods.</para></listitem>
+      Various table-based input methods. To use this, and any other table-based
+      input methods, it must appear in the list of engines along with
+      <literal>table</literal>. For example:
+<programlisting>
+ibus.engines = with pkgs.ibus-engines; [ table table-others ];
+</programlisting>
+  </para></listitem>
 </itemizedlist>
+
+<para>To use any input method, the package must be added in the configuration,
+  as shown above, and also (after running <literal>nixos-rebuild</literal>) the
+  input method must be added from IBus' preference dialog.</para>
 </section>
 
 <section><title>Fcitx</title>
diff --git a/nixos/modules/i18n/input-method/fcitx.nix b/nixos/modules/i18n/input-method/fcitx.nix
index e97bb9f80eb..440f13b4152 100644
--- a/nixos/modules/i18n/input-method/fcitx.nix
+++ b/nixos/modules/i18n/input-method/fcitx.nix
@@ -20,10 +20,9 @@ in
         example = literalExample "with pkgs.fcitx-engines; [ mozc hangul ]";
         description =
           let
-            engines =
-              lib.concatStringsSep ", "
-              (map (name: "<literal>${name}</literal>")
-               (lib.attrNames pkgs.fcitx-engines));
+            enginesDrv = filterAttrs (const isDerivation) pkgs.fcitx-engines;
+            engines = concatStringsSep ", "
+              (map (name: "<literal>${name}</literal>") (attrNames enginesDrv));
           in
             "Enabled Fcitx engines. Available engines are: ${engines}.";
       };
diff --git a/nixos/modules/i18n/input-method/ibus.nix b/nixos/modules/i18n/input-method/ibus.nix
index 097a662c3c6..e23e28aa25e 100644
--- a/nixos/modules/i18n/input-method/ibus.nix
+++ b/nixos/modules/i18n/input-method/ibus.nix
@@ -17,7 +17,7 @@ let
       [Desktop Entry]
       Name=IBus
       Type=Application
-      Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim --cache=refresh
+      Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim
     '';
   };
 in
@@ -30,10 +30,9 @@ in
         example = literalExample "with pkgs.ibus-engines; [ mozc hangul ]";
         description =
           let
-            engines =
-              lib.concatStringsSep ", "
-              (map (name: "<literal>${name}</literal>")
-               (lib.attrNames pkgs.ibus-engines));
+            enginesDrv = filterAttrs (const isDerivation) pkgs.ibus-engines;
+            engines = concatStringsSep ", "
+              (map (name: "<literal>${name}</literal>") (attrNames enginesDrv));
           in
             "Enabled IBus engines. Available engines are: ${engines}.";
       };
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 d14768bc107..c44dff3bb60 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix
@@ -1,20 +1,41 @@
 # This module defines a NixOS installation CD that contains X11 and
-# KDE 4.
+# KDE 5.
 
 { config, lib, pkgs, ... }:
 
 with lib;
 
 {
-  imports = [ ./installation-cd-base.nix ../../profiles/graphical.nix ];
+  imports = [ ./installation-cd-base.nix ];
 
-  # Provide wicd for easy wireless configuration.
-  #networking.wicd.enable = true;
+  services.xserver = {
+    enable = true;
+
+    # Automatically login as root.
+    displayManager.slim = {
+      enable = true;
+      defaultUser = "root";
+      autoLogin = true;
+    };
+
+    desktopManager.kde5 = {
+      enable = true;
+      enableQt4Support = false;
+    };
+
+    # Enable touchpad support for many laptops.
+    synaptics.enable = true;
+  };
 
   environment.systemPackages =
-    [ # Include gparted for partitioning disks.
+    [ pkgs.glxinfo
+
+      # Include gparted for partitioning disks.
       pkgs.gparted
 
+      # Firefox for reading the manual.
+      pkgs.firefox
+
       # Include some editors.
       pkgs.vim
       pkgs.bvi # binary editor
@@ -32,79 +53,21 @@ with lib;
   # Don't start the X server by default.
   services.xserver.autorun = mkForce false;
 
-  # Auto-login as root.
-  services.xserver.displayManager.kdm.extraConfig =
-    ''
-      [X-*-Core]
-      AllowRootLogin=true
-      AutoLoginEnable=true
-      AutoLoginUser=root
-      AutoLoginPass=""
-    '';
-
-  # Custom kde-workspace adding some icons on the desktop
-
   system.activationScripts.installerDesktop = let
-    openManual = pkgs.writeScript "nixos-manual.sh" ''
-      #!${pkgs.stdenv.shell}
-      cd ${config.system.build.manual.manual}/share/doc/nixos/
-      konqueror ./index.html
-    '';
-
     desktopFile = pkgs.writeText "nixos-manual.desktop" ''
       [Desktop Entry]
       Version=1.0
       Type=Application
       Name=NixOS Manual
-      Exec=${openManual}
-      Icon=konqueror
+      Exec=firefox ${config.system.build.manual.manual}/share/doc/nixos/index.html
+      Icon=text-html
     '';
 
   in ''
     mkdir -p /root/Desktop
     ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
-    ln -sfT ${pkgs.kde4.konsole}/share/applications/kde4/konsole.desktop /root/Desktop/konsole.desktop
+    ln -sfT ${pkgs.kde5.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
   '';
 
-  services.xserver.desktopManager.kde4.kdeWorkspacePackage = let
-    pkg = pkgs.kde4.kde_workspace;
-
-    plasmaInit = pkgs.writeText "00-defaultLayout.js" ''
-      loadTemplate("org.kde.plasma-desktop.defaultPanel")
-
-      for (var i = 0; i < screenCount; ++i) {
-        var desktop = new Activity
-        desktop.name = i18n("Desktop")
-        desktop.screen = i
-        desktop.wallpaperPlugin = 'image'
-        desktop.wallpaperMode = 'SingleImage'
-
-        var folderview = desktop.addWidget("folderview");
-        folderview.writeConfig("url", "desktop:/");
-
-        //Create more panels for other screens
-        if (i > 0){
-          var panel = new Panel
-          panel.screen = i
-          panel.location = 'bottom'
-          panel.height = screenGeometry(i).height > 1024 ? 35 : 27
-          var tasks = panel.addWidget("tasks")
-          tasks.writeConfig("showOnlyCurrentScreen", true);
-        }
-      }
-    '';
-
-  in
-    pkgs.stdenv.mkDerivation {
-      inherit (pkg) name meta;
-
-      buildCommand = ''
-        mkdir -p $out
-        cp -prf ${pkg}/* $out/
-        chmod a+w $out/share/apps/plasma-desktop/init
-        cp -f ${plasmaInit} $out/share/apps/plasma-desktop/init/00-defaultLayout.js
-      '';
-    };
-
 }
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
index 4641b8fcf9d..f4122ab0e51 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
@@ -1,11 +1,15 @@
 # This module defines a small NixOS installation CD.  It does not
 # contain any graphical stuff.
 
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 {
   imports =
     [ ./installation-cd-base.nix
-      ../../profiles/minimal.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 4fc8bf5428f..93dba0d882b 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -178,7 +178,7 @@ in
         image. It significantly increases image size. Use that when
         you want to be able to keep all the sources needed to build your
         system or when you are going to install the system on a computer
-        with slow on non-existent network connection.
+        with slow or non-existent network connection.
       '';
     };
 
@@ -232,8 +232,6 @@ in
     system.boot.loader.kernelFile = "bzImage";
     environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ];
 
-    boot.consoleLogLevel = mkDefault 7;
-
     # In stage 1 of the boot, mount the CD as the root FS by label so
     # that we don't need to know its device.  We pass the label of the
     # root filesystem on the kernel command line, rather than in
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 80fb4707228..456ef7c9f54 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -27,7 +27,6 @@ in
 
   boot.kernelPackages = pkgs.linuxPackages_latest;
   boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=ttyO0,115200n8" "console=tty0"];
-  boot.consoleLogLevel = 7;
 
   # FIXME: this probably should be in installation-device.nix
   users.extraUsers.root.initialHashedPassword = "";
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index dc196468139..e7163f10a3c 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -26,7 +26,6 @@ in
   boot.loader.generic-extlinux-compatible.enable = true;
 
   boot.kernelPackages = pkgs.linuxPackages_rpi;
-  boot.consoleLogLevel = 7;
 
   # FIXME: this probably should be in installation-device.nix
   users.extraUsers.root.initialHashedPassword = "";
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
index d984cb30717..ba84cd51098 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -61,7 +61,7 @@ in
       pkgs.cryptsetup # needed for dm-crypt volumes
 
       # Some networking tools.
-      pkgs.sshfsFuse
+      pkgs.sshfs-fuse
       pkgs.socat
       pkgs.screen
       pkgs.wpa_supplicant # !!! should use the wpa module
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
index 9e733241993..7ec09acd591 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
@@ -55,7 +55,7 @@ in
       pkgs.cryptsetup # needed for dm-crypt volumes
 
       # Some networking tools.
-      pkgs.sshfsFuse
+      pkgs.sshfs-fuse
       pkgs.socat
       pkgs.screen
       pkgs.wpa_supplicant # !!! should use the wpa module
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index 366591a8114..0f6046339b3 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -26,29 +26,43 @@ with lib;
     # here and it causes a cyclic dependency.
     boot.loader.grub.enable = false;
 
-    boot.initrd.postMountCommands = ''
-      mkdir -p /mnt-root/nix/store
-      mount -t squashfs /nix-store.squashfs /mnt-root/nix/store
-    '';
-
     # !!! Hack - attributes expected by other modules.
     system.boot.loader.kernelFile = "bzImage";
     environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ];
 
-    boot.consoleLogLevel = mkDefault 7;
-
     fileSystems."/" =
       { fsType = "tmpfs";
         options = [ "mode=0755" ];
       };
 
+    # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
+    # image) to make this a live CD.
+    fileSystems."/nix/.ro-store" =
+      { fsType = "squashfs";
+        device = "../nix-store.squashfs";
+        options = [ "loop" ];
+        neededForBoot = true;
+      };
+
+    fileSystems."/nix/.rw-store" =
+      { fsType = "tmpfs";
+        options = [ "mode=0755" ];
+        neededForBoot = true;
+      };
+
+    fileSystems."/nix/store" =
+      { fsType = "unionfs-fuse";
+        device = "unionfs";
+        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" ];
 
     boot.initrd.kernelModules = [ "loop" ];
 
     # Closures to be copied to the Nix store, namely the init
     # script and the top-level system configuration directory.
-   netboot.storeContents =
+    netboot.storeContents =
       [ config.system.build.toplevel ];
 
     # Create the squashfs image that contains the Nix store.
diff --git a/nixos/modules/installer/tools/auto-upgrade.nix b/nixos/modules/installer/tools/auto-upgrade.nix
index b21b80c666a..dfb43d1a1db 100644
--- a/nixos/modules/installer/tools/auto-upgrade.nix
+++ b/nixos/modules/installer/tools/auto-upgrade.nix
@@ -84,7 +84,7 @@ let cfg = config.system.autoUpgrade; in
         ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch ${toString cfg.flags}
       '';
 
-      startAt = optionalString cfg.enable cfg.dates;
+      startAt = optional cfg.enable cfg.dates;
     };
 
   };
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
new file mode 100644
index 00000000000..ddc624a77de
--- /dev/null
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -0,0 +1,5 @@
+{
+  x86_64-linux = "/nix/store/m8z91vpfxyszhjpq4wl8m1zwlqik4fkn-nix-1.11.5";
+  i686-linux = "/nix/store/vk71likl32igqg6apqsj52ln3vhkq1pa-nix-1.11.5";
+  x86_64-darwin = "/nix/store/qfwm0b5qkr8v8gsv9dh2z3arky9p1myg-nix-1.11.5";
+}
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 4da752e1905..bb379dafc64 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -94,6 +94,21 @@ sub hasCPUFeature {
 my $cpus = scalar (grep {/^processor\s*:/} (split '\n', $cpuinfo));
 
 
+# Determine CPU governor to use
+if (-e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors") {
+    my $governors = read_file("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors");
+    # ondemand governor is not available on sandy bridge or later Intel CPUs
+    my @desired_governors = ("ondemand", "powersave");
+    my $e;
+
+    foreach $e (@desired_governors) {
+        if (index($governors, $e) != -1) {
+            last if (push @attrs, "powerManagement.cpuFreqGovernor = \"$e\";");
+        }
+    }
+}
+
+
 # Virtualization support?
 push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
 push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
@@ -527,8 +542,11 @@ EOF
   # Use the GRUB 2 boot loader.
   boot.loader.grub.enable = true;
   boot.loader.grub.version = 2;
+  # boot.loader.grub.efiSupport = true;
+  # boot.loader.grub.efiInstallAsRemovable = true;
+  # boot.loader.efi.efiSysMountPoint = "/boot/efi";
   # Define on which hard drive you want to install Grub.
-  # boot.loader.grub.device = "/dev/sda";
+  # boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
 EOF
         }
 
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index 27c03ff9792..5250edd1500 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -260,7 +260,7 @@ chroot $mountPoint /nix/var/nix/profiles/system/activate
 
 
 # Ask the user to set a root password.
-if [ -z "$noRootPasswd" ] && [ -x $mountPoint/var/setuid-wrappers/passwd ] && [ -t 0 ]; then
+if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /var/setuid-wrappers/passwd ] && [ -t 0 ]; then
     echo "setting root password..."
     chroot $mountPoint /var/permissions-wrappers/passwd
 fi
diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh
index 17c17d05e28..27eacda48a8 100644
--- a/nixos/modules/installer/tools/nixos-option.sh
+++ b/nixos/modules/installer/tools/nixos-option.sh
@@ -256,7 +256,7 @@ if isOption opt then
   // optionalAttrs (opt ? default) { inherit (opt) default; }
   // optionalAttrs (opt ? example) { inherit (opt) example; }
   // optionalAttrs (opt ? description) { inherit (opt) description; }
-  // optionalAttrs (opt ? type) { typename = opt.type.name; }
+  // optionalAttrs (opt ? type) { typename = opt.type.description; }
   // optionalAttrs (opt ? options) { inherit (opt) options; }
   // {
     # to disambiguate the xml output.
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 80398978983..8e55a4f525f 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -126,9 +126,9 @@ targetHostCmd() {
 copyToTarget() {
     if ! [ "$targetHost" = "$buildHost" ]; then
         if [ -z "$targetHost" ]; then
-            NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --from "$buildHost" "$1"
+            NIX_SSHOPTS=$SSHOPTS nix-copy-closure --from "$buildHost" "$1"
         elif [ -z "$buildHost" ]; then
-            NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$targetHost" "$1"
+            NIX_SSHOPTS=$SSHOPTS nix-copy-closure --to "$targetHost" "$1"
         else
             buildHostCmd nix-copy-closure --to "$targetHost" "$1"
         fi
@@ -169,7 +169,7 @@ nixBuild() {
 
         local drv="$(nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")"
         if [ -a "$drv" ]; then
-            NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$buildHost" "$drv"
+            NIX_SSHOPTS=$SSHOPTS nix-copy-closure --to "$buildHost" "$drv"
             buildHostCmd nix-store -r "$drv" "${buildArgs[@]}"
         else
             echo "nix-instantiate failed"
@@ -261,9 +261,9 @@ fi
 prebuiltNix() {
     machine="$1"
     if [ "$machine" = x86_64 ]; then
-        echo /nix/store/xryr9g56h8yjddp89d6dw12anyb4ch7c-nix-1.10
+        echo @nix_x86_64_linux@
     elif [[ "$machine" =~ i.86 ]]; then
-        echo /nix/store/2w92k5wlpspf0q2k9mnf2z42prx3bwmv-nix-1.10
+        echo @nix_i686_linux@
     else
         echo "$0: unsupported platform"
         exit 1
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index fc39a653abd..a35f6ad8ae5 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -32,11 +32,15 @@ let
       "cp refs $out";
   };
 
-  nixos-rebuild = makeProg {
-    name = "nixos-rebuild";
-    src = ./nixos-rebuild.sh;
-    nix = config.nix.package.out;
-  };
+  nixos-rebuild =
+    let fallback = import ./nix-fallback-paths.nix; in
+    makeProg {
+      name = "nixos-rebuild";
+      src = ./nixos-rebuild.sh;
+      nix = config.nix.package.out;
+      nix_x86_64_linux = fallback.x86_64-linux;
+      nix_i686_linux = fallback.i686-linux;
+    };
 
   nixos-generate-config = makeProg {
     name = "nixos-generate-config";
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 49ec0899610..5316cfce906 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -18,5 +18,5 @@ with lib;
 
   # Add some more video drivers to give X11 a shot at working in
   # VMware and QEMU.
-  services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" ];
+  services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ];
 }
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 31c93028bc5..6ab4b24a349 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -58,7 +58,6 @@
       #utmp = 29; # unused
       ddclient = 30;
       davfs2 = 31;
-      privoxy = 32;
       #disnix = 33; # unused
       osgi = 34;
       tor = 35;
@@ -74,7 +73,6 @@
       rtkit = 45;
       dovecot2 = 46;
       dovenull2 = 47;
-      unbound = 48;
       prayer = 49;
       mpd = 50;
       clamav = 51;
@@ -85,7 +83,7 @@
       spamd = 56;
       #networkmanager = 57; # unused
       nslcd = 58;
-      #scanner = 59; # unused
+      scanner = 59;
       nginx = 60;
       chrony = 61;
       #systemd-journal = 62; # unused
@@ -120,7 +118,6 @@
       minidlna = 91;
       elasticsearch = 92;
       tcpcryptd = 93; # tcpcryptd uses a hard-coded uid. We patch it in Nixpkgs to match this choice.
-      #connman = 94; # unused
       firebird = 95;
       #keys = 96; # unused
       haproxy = 97;
@@ -141,7 +138,7 @@
       ngircd = 112;
       btsync = 113;
       minecraft = 114;
-      monetdb = 115;
+      #monetdb = 115; # unused (not packaged), removed 2016-09-19
       rippled = 116;
       murmur = 117;
       foundationdb = 118;
@@ -181,7 +178,6 @@
       systemd-timesync = 154;
       liquidsoap = 155;
       etcd = 156;
-      docker-registry = 157;
       hbase = 158;
       opentsdb = 159;
       scollector = 160;
@@ -215,7 +211,6 @@
       lambdabot = 191;
       asterisk = 192;
       plex = 193;
-      bird = 195;
       grafana = 196;
       skydns = 197;
       ripple-rest = 198;
@@ -275,6 +270,18 @@
       gocd-server = 252;
       terraria = 253;
       mattermost = 254;
+      prometheus = 255;
+      telegraf = 256;
+      gitlab-runner = 257;
+      postgrey = 258;
+      hound = 259;
+      leaps = 260;
+      ipfs  = 261;
+      stanchion = 262;
+      riak-cs = 263;
+      infinoted = 264;
+      keystone = 265;
+      glance = 266;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -315,7 +322,6 @@
       utmp = 29;
       #ddclient = 30; # unused
       davfs2 = 31;
-      privoxy = 32;
       disnix = 33;
       osgi = 34;
       tor = 35;
@@ -331,7 +337,6 @@
       #rtkit = 45; # unused
       dovecot2 = 46;
       #dovenull = 47; # unused
-      #unbound = 48; # unused
       prayer = 49;
       mpd = 50;
       clamav = 51;
@@ -377,7 +382,6 @@
       minidlna = 91;
       elasticsearch = 92;
       #tcpcryptd = 93; # unused
-      connman = 94;
       firebird = 95;
       keys = 96;
       haproxy = 97;
@@ -396,7 +400,7 @@
       #ngircd = 112; # unused
       btsync = 113;
       #minecraft = 114; # unused
-      monetdb = 115;
+      #monetdb = 115; # unused (not packaged), removed 2016-09-19
       #ripped = 116; # unused
       #murmur = 117; # unused
       foundationdb = 118;
@@ -436,7 +440,6 @@
       systemd-timesync = 154;
       liquidsoap = 155;
       #etcd = 156; # unused
-      #docker-registry = 157; # unused
       hbase = 158;
       opentsdb = 159;
       scollector = 160;
@@ -465,10 +468,9 @@
       ihaskell = 189;
       i2p = 190;
       lambdabot = 191;
-      #asterisk = 192; # unused
+      asterisk = 192;
       plex = 193;
       sabnzbd = 194;
-      bird = 195;
       #grafana = 196; #unused
       #skydns = 197; #unused
       #ripple-rest = 198; #unused
@@ -520,6 +522,18 @@
       gocd-server = 252;
       terraria = 253;
       mattermost = 254;
+      prometheus = 255;
+      #telegraf = 256; # unused
+      gitlab-runner = 257;
+      postgrey = 258;
+      hound = 259;
+      leaps = 260;
+      ipfs = 261;
+      stanchion = 262;
+      riak-cs = 263;
+      infinoted = 264;
+      keystone = 265;
+      glance = 266;
 
       # 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 27a0fa6cf3d..3cb5bb1a351 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -15,6 +15,15 @@ in {
       '';
     };
 
+    locate = mkOption {
+      type = types.package;
+      default = pkgs.findutils;
+      example = "pkgs.mlocate";
+      description = ''
+        The locate implementation to use
+      '';
+    };
+
     interval = mkOption {
       type = types.str;
       default = "02:15";
@@ -77,7 +86,7 @@ in {
         script =
           ''
             mkdir -m 0755 -p $(dirname ${toString cfg.output})
-            exec updatedb \
+            exec ${cfg.locate}/bin/updatedb \
               --localuser=${cfg.localuser} \
               ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \
               --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 2ecdbdbf392..70cd3fb9766 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -95,11 +95,11 @@ in
       nixosVersionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
       # Note: code names must only increase in alphabetical order.
-      nixosCodeName = "Flounder";
+      nixosCodeName = "Gorilla";
     };
 
     # Generate /etc/os-release.  See
-    # http://0pointer.de/public/systemd-man/os-release.html for the
+    # https://www.freedesktop.org/software/systemd/man/os-release.html for the
     # format.
     environment.etc."os-release".text =
       ''
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6e69cebf763..7607af2ae92 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -38,8 +38,10 @@
   ./hardware/opengl.nix
   ./hardware/pcmcia.nix
   ./hardware/video/amdgpu.nix
+  ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
   ./hardware/video/bumblebee.nix
+  ./hardware/video/displaylink.nix
   ./hardware/video/nvidia.nix
   ./hardware/video/webcam/facetimehd.nix
   ./i18n/input-method/default.nix
@@ -59,6 +61,7 @@
   ./misc/nixpkgs.nix
   ./misc/passthru.nix
   ./misc/version.nix
+  ./programs/adb.nix
   ./programs/atop.nix
   ./programs/bash/bash.nix
   ./programs/blcr.nix
@@ -66,13 +69,17 @@
   ./programs/command-not-found/command-not-found.nix
   ./programs/dconf.nix
   ./programs/environment.nix
-  ./programs/freetds.nix
   ./programs/fish.nix
+  ./programs/freetds.nix
+  ./programs/gphoto2.nix
+  ./programs/info.nix
+  ./programs/java.nix
   ./programs/kbdlight.nix
   ./programs/light.nix
   ./programs/man.nix
   ./programs/mosh.nix
   ./programs/nano.nix
+  ./programs/oblogout.nix
   ./programs/screen.nix
   ./programs/shadow.nix
   ./programs/shell.nix
@@ -81,6 +88,7 @@
   ./programs/ssmtp.nix
   ./programs/tmux.nix
   ./programs/venus.nix
+  ./programs/vim.nix
   ./programs/wvdial.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
@@ -113,6 +121,7 @@
   ./services/audio/mpd.nix
   ./services/audio/mopidy.nix
   ./services/audio/squeezelite.nix
+  ./services/audio/ympd.nix
   ./services/backup/almir.nix
   ./services/backup/bacula.nix
   ./services/backup/crashplan.nix
@@ -125,11 +134,14 @@
   ./services/cluster/fleet.nix
   ./services/cluster/kubernetes.nix
   ./services/cluster/panamax.nix
+  ./services/computing/boinc/client.nix
   ./services/computing/torque/server.nix
   ./services/computing/torque/mom.nix
   ./services/computing/slurm/slurm.nix
+  ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildkite-agent.nix
   ./services/continuous-integration/hydra/default.nix
+  ./services/continuous-integration/gitlab-runner.nix
   ./services/continuous-integration/gocd-agent/default.nix
   ./services/continuous-integration/gocd-server/default.nix
   ./services/continuous-integration/jenkins/default.nix
@@ -142,7 +154,6 @@
   ./services/databases/hbase.nix
   ./services/databases/influxdb.nix
   ./services/databases/memcached.nix
-  ./services/databases/monetdb.nix
   ./services/databases/mongodb.nix
   ./services/databases/mysql.nix
   ./services/databases/neo4j.nix
@@ -151,6 +162,8 @@
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
   ./services/databases/riak.nix
+  ./services/databases/riak-cs.nix
+  ./services/databases/stanchion.nix
   ./services/databases/virtuoso.nix
   ./services/desktops/accountsservice.nix
   ./services/desktops/geoclue2.nix
@@ -160,6 +173,7 @@
   ./services/desktops/gnome3/gnome-keyring.nix
   ./services/desktops/gnome3/gnome-online-accounts.nix
   ./services/desktops/gnome3/gnome-online-miners.nix
+  ./services/desktops/gnome3/gnome-terminal-server.nix
   ./services/desktops/gnome3/gnome-user-share.nix
   ./services/desktops/gnome3/gvfs.nix
   ./services/desktops/gnome3/seahorse.nix
@@ -169,6 +183,7 @@
   ./services/desktops/telepathy.nix
   ./services/development/hoogle.nix
   ./services/editors/emacs.nix
+  ./services/editors/infinoted.nix
   ./services/games/factorio.nix
   ./services/games/ghost-one.nix
   ./services/games/minecraft-server.nix
@@ -208,10 +223,12 @@
   ./services/mail/freepops.nix
   ./services/mail/mail.nix
   ./services/mail/mlmmj.nix
+  ./services/mail/offlineimap.nix
   ./services/mail/opendkim.nix
   ./services/mail/opensmtpd.nix
   ./services/mail/postfix.nix
   ./services/mail/postsrsd.nix
+  ./services/mail/postgrey.nix
   ./services/mail/spamassassin.nix
   ./services/mail/rspamd.nix
   ./services/mail/rmilter.nix
@@ -230,6 +247,7 @@
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
   ./services/misc/emby.nix
+  ./services/misc/errbot.nix
   ./services/misc/etcd.nix
   ./services/misc/felix.nix
   ./services/misc/folding-at-home.nix
@@ -239,6 +257,7 @@
   ./services/misc/gitolite.nix
   ./services/misc/gpsd.nix
   ./services/misc/ihaskell.nix
+  ./services/misc/leaps.nix
   ./services/misc/mantisbt.nix
   ./services/misc/mathics.nix
   ./services/misc/matrix-synapse.nix
@@ -249,6 +268,7 @@
   ./services/misc/mwlib.nix
   ./services/misc/nix-daemon.nix
   ./services/misc/nix-gc.nix
+  ./services/misc/nix-optimise.nix
   ./services/misc/nixos-manual.nix
   ./services/misc/nix-ssh-serve.nix
   ./services/misc/nzbget.nix
@@ -265,6 +285,7 @@
   ./services/misc/siproxd.nix
   ./services/misc/sonarr.nix
   ./services/misc/spice-vdagentd.nix
+  ./services/misc/sssd.nix
   ./services/misc/subsonic.nix
   ./services/misc/sundtek.nix
   ./services/misc/svnserve.nix
@@ -286,6 +307,14 @@
   ./services/monitoring/monit.nix
   ./services/monitoring/munin.nix
   ./services/monitoring/nagios.nix
+  ./services/monitoring/prometheus/default.nix
+  ./services/monitoring/prometheus/alertmanager.nix
+  ./services/monitoring/prometheus/blackbox-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/varnish-exporter.nix
   ./services/monitoring/riemann.nix
   ./services/monitoring/riemann-dash.nix
   ./services/monitoring/riemann-tools.nix
@@ -294,11 +323,14 @@
   ./services/monitoring/statsd.nix
   ./services/monitoring/systemhealth.nix
   ./services/monitoring/teamviewer.nix
+  ./services/monitoring/telegraf.nix
   ./services/monitoring/ups.nix
   ./services/monitoring/uptime.nix
   ./services/monitoring/zabbix-agent.nix
   ./services/monitoring/zabbix-server.nix
+  ./services/network-filesystems/cachefilesd.nix
   ./services/network-filesystems/drbd.nix
+  ./services/network-filesystems/ipfs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
   ./services/network-filesystems/openafs-client/default.nix
@@ -326,18 +358,20 @@
   ./services/networking/connman.nix
   ./services/networking/consul.nix
   ./services/networking/coturn.nix
+  ./services/networking/dante.nix
   ./services/networking/ddclient.nix
   ./services/networking/dhcpcd.nix
   ./services/networking/dhcpd.nix
   ./services/networking/dnschain.nix
   ./services/networking/dnscrypt-proxy.nix
   ./services/networking/dnsmasq.nix
-  ./services/networking/docker-registry-server.nix
   ./services/networking/ejabberd.nix
   ./services/networking/fan.nix
+  ./services/networking/fakeroute.nix
   ./services/networking/ferm.nix
   ./services/networking/firefox/sync-server.nix
   ./services/networking/firewall.nix
+  ./services/networking/flannel.nix
   ./services/networking/flashpolicyd.nix
   ./services/networking/freenet.nix
   ./services/networking/gale.nix
@@ -350,6 +384,7 @@
   ./services/networking/haproxy.nix
   ./services/networking/heyefi.nix
   ./services/networking/hostapd.nix
+  ./services/networking/htpdate.nix
   ./services/networking/i2pd.nix
   ./services/networking/i2p.nix
   ./services/networking/iodine.nix
@@ -364,6 +399,7 @@
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/mosquitto.nix
+  ./services/networking/miredo.nix
   ./services/networking/mstpd.nix
   ./services/networking/murmur.nix
   ./services/networking/namecoind.nix
@@ -376,7 +412,6 @@
   ./services/networking/ntopng.nix
   ./services/networking/ntpd.nix
   ./services/networking/nylon.nix
-  ./services/networking/offlineimap.nix
   ./services/networking/oidentd.nix
   ./services/networking/openfire.nix
   ./services/networking/openntpd.nix
@@ -384,10 +419,12 @@
   ./services/networking/ostinato.nix
   ./services/networking/pdnsd.nix
   ./services/networking/polipo.nix
+  ./services/networking/powerdns.nix
   ./services/networking/pptpd.nix
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
+  ./services/networking/quagga.nix
   ./services/networking/quassel.nix
   ./services/networking/racoon.nix
   ./services/networking/radicale.nix
@@ -425,6 +462,7 @@
   ./services/networking/wakeonlan.nix
   ./services/networking/websockify.nix
   ./services/networking/wicd.nix
+  ./services/networking/wireguard.nix
   ./services/networking/wpa_supplicant.nix
   ./services/networking/xinetd.nix
   ./services/networking/xl2tpd.nix
@@ -438,6 +476,7 @@
   ./services/scheduling/fcron.nix
   ./services/scheduling/marathon.nix
   ./services/search/elasticsearch.nix
+  ./services/search/hound.nix
   ./services/search/kibana.nix
   ./services/search/solr.nix
   ./services/security/clamav.nix
@@ -454,6 +493,7 @@
   ./services/security/torify.nix
   ./services/security/tor.nix
   ./services/security/torsocks.nix
+  ./services/system/cgmanager.nix
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
   ./services/system/kerberos.nix
@@ -461,14 +501,21 @@
   ./services/system/uptimed.nix
   ./services/torrent/deluge.nix
   ./services/torrent/flexget.nix
+  ./services/torrent/opentracker.nix
   ./services/torrent/peerflix.nix
   ./services/torrent/transmission.nix
   ./services/ttys/agetty.nix
   ./services/ttys/gpm.nix
   ./services/ttys/kmscon.nix
+  ./services/web-apps/atlassian/confluence.nix
+  ./services/web-apps/atlassian/crowd.nix
+  ./services/web-apps/atlassian/jira.nix
   ./services/web-apps/mattermost.nix
+  ./services/web-apps/nixbot.nix
   ./services/web-apps/pump.io.nix
   ./services/web-apps/tt-rss.nix
+  ./services/web-apps/selfoss.nix
+  ./services/web-apps/quassel-webserver.nix
   ./services/web-servers/apache-httpd/default.nix
   ./services/web-servers/caddy.nix
   ./services/web-servers/fcgiwrap.nix
@@ -488,6 +535,7 @@
   ./services/x11/colord.nix
   ./services/x11/compton.nix
   ./services/x11/unclutter.nix
+  ./services/x11/unclutter-xfixes.nix
   ./services/x11/desktop-managers/default.nix
   ./services/x11/display-managers/auto.nix
   ./services/x11/display-managers/default.nix
@@ -501,6 +549,7 @@
   ./services/x11/hardware/synaptics.nix
   ./services/x11/hardware/wacom.nix
   ./services/x11/redshift.nix
+  ./services/x11/urxvtd.nix
   ./services/x11/window-managers/awesome.nix
   #./services/x11/window-managers/compiz.nix
   ./services/x11/window-managers/default.nix
@@ -543,6 +592,7 @@
   ./system/boot/stage-1.nix
   ./system/boot/stage-2.nix
   ./system/boot/systemd.nix
+  ./system/boot/systemd-nspawn.nix
   ./system/boot/timesyncd.nix
   ./system/boot/tmp.nix
   ./system/etc/etc.nix
@@ -578,6 +628,7 @@
   ./virtualisation/docker.nix
   ./virtualisation/libvirtd.nix
   ./virtualisation/lxc.nix
+  ./virtualisation/lxcfs.nix
   ./virtualisation/lxd.nix
   ./virtualisation/amazon-options.nix
   ./virtualisation/openvswitch.nix
@@ -588,4 +639,6 @@
   ./virtualisation/vmware-guest.nix
   ./virtualisation/xen-dom0.nix
   ./virtualisation/xe-guest-utilities.nix
+  ./virtualisation/openstack/keystone.nix
+  ./virtualisation/openstack/glance.nix
 ]
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index 20a1f7f1ed8..687cd9d80d3 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -7,7 +7,7 @@
   # Include some utilities that are useful for installing or repairing
   # the system.
   environment.systemPackages = [
-    pkgs.w3m # needed for the manual anyway
+    pkgs.w3m-nox # needed for the manual anyway
     pkgs.testdisk # useful for repairing boot problems
     pkgs.mssys # for writing Microsoft boot sectors / MBRs
     pkgs.efibootmgr
@@ -20,7 +20,7 @@
 
     # Some networking tools.
     pkgs.fuse
-    pkgs.sshfsFuse
+    pkgs.sshfs-fuse
     pkgs.socat
     pkgs.screen
 
@@ -42,12 +42,10 @@
     # Some compression/archiver tools.
     pkgs.unzip
     pkgs.zip
-    pkgs.dar # disk archiver
-    pkgs.cabextract
   ];
 
   # Include support for various filesystems.
-  boot.supportedFilesystems = [ "btrfs" "reiserfs" "vfat" "f2fs" "xfs" "zfs" "ntfs" "cifs" ];
+  boot.supportedFilesystems = [ "btrfs" "reiserfs" "vfat" "f2fs" "xfs" "ntfs" "cifs" ];
 
   # Configure host id for ZFS to work
   networking.hostId = lib.mkDefault "8425e349";
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 93736ba256b..7949e600f86 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -70,5 +70,11 @@ with lib;
     # the initrd builder.
     system.extraDependencies = [ pkgs.stdenv pkgs.busybox pkgs.perlPackages.ArchiveCpio ];
 
+    # Show all debug messages from the kernel but don't log refused packets
+    # because we have the firewall enabled. This makes installs from the
+    # console less cumbersome if the machine has a public IP.
+    boot.consoleLogLevel = mkDefault 7;
+    networking.firewall.logRefusedConnections = mkDefault false;
+
   };
 }
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index c353da227ae..e2497d04252 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -11,4 +11,9 @@ with lib;
   # This isn't perfect, but let's expect the user specifies an UTF-8 defaultLocale
   i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
   services.nixosManual.enable = mkDefault false;
+
+  programs.man.enable = mkDefault false;
+  programs.info.enable = mkDefault false;
+
+  sound.enable = mkDefault false;
 }
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
new file mode 100644
index 00000000000..9ba81899e58
--- /dev/null
+++ b/nixos/modules/programs/adb.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.mic92 ];
+
+  ###### interface
+  options = {
+    programs.adb = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Whether to configure system to use Android Debug Bridge (adb).
+          To grant access to a user, it must be part of adbusers group:
+          <code>users.extraUsers.alice.extraGroups = ["adbusers"];</code>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.adb.enable {
+    services.udev.packages = [ pkgs.android-udev-rules ];
+    environment.systemPackages = [ pkgs.androidenv.platformTools ];
+    users.extraGroups.adbusers = {};
+  };
+}
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index c09bcfb70e2..e23849d350b 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -16,7 +16,7 @@ let
     # programmable completion. If we do, enable all modules installed in
     # the system (and user profile).
     if shopt -q progcomp &>/dev/null; then
-      . "${pkgs.bashCompletion}/etc/profile.d/bash_completion.sh"
+      . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
       nullglobStatus=$(shopt -p nullglob)
       shopt -s nullglob
       for p in $NIX_PROFILES; do
diff --git a/nixos/modules/programs/gphoto2.nix b/nixos/modules/programs/gphoto2.nix
new file mode 100644
index 00000000000..47822562aee
--- /dev/null
+++ b/nixos/modules/programs/gphoto2.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.league ];
+
+  ###### interface
+  options = {
+    programs.gphoto2 = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Whether to configure system to use gphoto2.
+          To grant digital camera access to a user, the user must
+          be part of the camera group:
+          <code>users.extraUsers.alice.extraGroups = ["camera"];</code>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.gphoto2.enable {
+    services.udev.packages = [ pkgs.libgphoto2 ];
+    environment.systemPackages = [ pkgs.gphoto2 ];
+    users.extraGroups.camera = {};
+  };
+}
diff --git a/nixos/modules/programs/info.nix b/nixos/modules/programs/info.nix
new file mode 100644
index 00000000000..be6439dca5a
--- /dev/null
+++ b/nixos/modules/programs/info.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options = {
+
+    programs.info.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable info pages and the <command>info</command> command.
+      '';
+    };
+
+  };
+
+
+  config = mkIf config.programs.info.enable {
+
+    environment.systemPackages = [ pkgs.texinfoInteractive ];
+
+    environment.pathsToLink = [ "/info" "/share/info" ];
+
+    environment.extraOutputsToInstall = [ "info" ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/java.nix b/nixos/modules/programs/java.nix
new file mode 100644
index 00000000000..d31698c3b39
--- /dev/null
+++ b/nixos/modules/programs/java.nix
@@ -0,0 +1,58 @@
+# This module provides JAVA_HOME, with a different way to install java
+# system-wide.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.java;
+in
+
+{
+
+  options = {
+
+    programs.java = {
+
+      enable = mkEnableOption "java" // {
+        description = ''
+          Install and setup the Java development kit.
+          <note>
+          <para>This adds JAVA_HOME to the global environment, by sourcing the
+            jdk's setup-hook on shell init. It is equivalent to starting a shell
+            through 'nix-shell -p jdk', or roughly the following system-wide
+            configuration:
+          </para>
+          <programlisting>
+            environment.variables.JAVA_HOME = ''${pkgs.jdk.home}/lib/openjdk;
+            environment.systemPackages = [ pkgs.jdk ];
+          </programlisting>
+          </note>
+        '';
+      };
+
+      package = mkOption {
+        default = pkgs.jdk;
+        defaultText = "pkgs.jdk";
+        description = ''
+          Java package to install. Typical values are pkgs.jdk or pkgs.jre.
+        '';
+        type = types.package;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    environment.shellInit = ''
+      test -e ${cfg.package}/nix-support/setup-hook && source ${cfg.package}/nix-support/setup-hook
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/mosh.nix b/nixos/modules/programs/mosh.nix
index b478f8e180f..1c29eddf01d 100644
--- a/nixos/modules/programs/mosh.nix
+++ b/nixos/modules/programs/mosh.nix
@@ -9,14 +9,14 @@ let
 in
 {
   options.programs.mosh = {
-      enable = mkOption {
-	description = ''
-	  Whether to enable mosh. Note, this will open ports in your firewall!
-	'';
-        default = false;
-	example = true;
-	type = lib.types.bool;
-      };
+    enable = mkOption {
+      description = ''
+        Whether to enable mosh. Note, this will open ports in your firewall!
+      '';
+      default = false;
+      example = true;
+      type = lib.types.bool;
+    };
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix
new file mode 100644
index 00000000000..79a8ddb7ce3
--- /dev/null
+++ b/nixos/modules/programs/oblogout.nix
@@ -0,0 +1,160 @@
+# Global configuration for oblogout.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.oblogout;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.oblogout = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install OBLogout and create <filename>/etc/oblogout.conf</filename>.
+          See <filename>${pkgs.oblogout}/share/doc/README</filename>.
+        '';
+      };
+
+      opacity = mkOption {
+        type = types.int;
+        default = 70;
+        description = ''
+        '';
+      };
+
+      bgcolor = mkOption {
+        type = types.str;
+        default = "black";
+        description = ''
+        '';
+      };
+
+      buttontheme = mkOption {
+        type = types.str;
+        default = "simplistic";
+        description = ''
+        '';
+      };
+
+      buttons = mkOption {
+        type = types.str;
+        default =  "cancel, logout, restart, shutdown, suspend, hibernate";
+        description = ''
+        '';
+      };
+
+      cancel = mkOption {
+        type = types.str;
+        default =  "Escape";
+        description = ''
+        '';
+      };
+
+      shutdown = mkOption {
+        type = types.str;
+        default = "S";
+        description = ''
+        '';
+      };
+
+      restart = mkOption {
+        type = types.str;
+        default = "R";
+        description = ''
+        '';
+      };
+
+      suspend = mkOption {
+        type = types.str;
+        default = "U";
+        description = ''
+        '';
+      };
+
+      logout = mkOption {
+        type = types.str;
+        default = "L";
+        description = ''
+        '';
+      };
+
+      lock = mkOption {
+        type = types.str;
+        default = "K";
+        description = ''
+        '';
+      };
+
+      hibernate = mkOption {
+        type = types.str;
+        default =  "H";
+        description = ''
+        '';
+      };
+
+      clogout = mkOption {
+        type = types.str;
+        default = "openbox --exit";
+        description = ''
+        '';
+      };
+
+      clock = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+        '';
+      };
+
+      cswitchuser = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.oblogout ];
+
+    environment.etc."oblogout.conf".text = ''
+      [settings]
+      usehal = false
+
+      [looks]
+      opacity = ${toString cfg.opacity}
+      bgcolor = ${cfg.bgcolor}
+      buttontheme = ${cfg.buttontheme}
+      buttons = ${cfg.buttons}
+
+      [shortcuts]
+      cancel = ${cfg.cancel}
+      shutdown = ${cfg.shutdown}
+      restart = ${cfg.restart}
+      suspend = ${cfg.suspend}
+      logout = ${cfg.logout}
+      lock = ${cfg.lock}
+      hibernate = ${cfg.hibernate}
+
+      [commands]
+      shutdown = systemctl poweroff
+      restart = systemctl reboot
+      suspend = systemctl suspend
+      hibernate = systemctl hibernate
+      logout = ${cfg.clogout}
+      lock = ${cfg.clock}
+      switchuser = ${cfg.cswitchuser}
+    '';
+  };
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index f6f66924f32..08d96cbcf4b 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -99,7 +99,6 @@ in
         groupdel = { rootOK = true; };
         login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
         chpasswd = { rootOK = true; };
-        chgpasswd = { rootOK = true; };
       };
 
 
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index cc835081c9f..5f4d4dc9475 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -56,7 +56,6 @@ in
 
       setXAuthLocation = mkOption {
         type = types.bool;
-        default = config.services.xserver.enable;
         description = ''
           Whether to set the path to <command>xauth</command> for X11-forwarded connections.
           This causes a dependency on X11 packages.
@@ -165,6 +164,9 @@ in
 
   config = {
 
+    programs.ssh.setXAuthLocation =
+      mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 || config.services.openssh.forwardX11);
+
     assertions =
       [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation;
           message = "cannot enable X11 forwarding without setting XAuth location";
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
new file mode 100644
index 00000000000..8476c1accd3
--- /dev/null
+++ b/nixos/modules/programs/vim.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.vim;
+in {
+  options.programs.vim = {
+    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.
+      '';
+    };
+  };
+
+  config = mkIf cfg.defaultEditor {
+        environment.systemPackages = [ pkgs.vim ];
+        environment.variables = { EDITOR = mkOverride 900 "vim"; };
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 1b8b7a79593..b4d941a7770 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -25,7 +25,10 @@ in
       enable = mkOption {
         default = false;
         description = ''
-          Whether to configure zsh as an interactive shell.
+          Whether to configure zsh as an interactive shell. To enable zsh for
+          a particular user, use the <option>users.users.&lt;name?&gt;.shell</option>
+          option for that user. To enable zsh system-wide use the
+          <option>users.defaultUserShell</option> option.
         '';
         type = types.bool;
       };
@@ -81,6 +84,21 @@ in
         type = types.bool;
       };
 
+      enableSyntaxHighlighting = mkOption {
+        default = false;
+        description = ''
+          Enable zsh-syntax-highlighting
+        '';
+        type = types.bool;
+      };
+      
+      enableAutosuggestions = mkOption {
+        default = false;
+        description = ''
+          Enable zsh-autosuggestions
+        '';
+      };
+
     };
 
   };
@@ -99,9 +117,9 @@ in
 
       interactiveShellInit = ''
         # history defaults
-        export SAVEHIST=2000
-        export HISTSIZE=2000
-        export HISTFILE=$HOME/.zsh_history
+        SAVEHIST=2000
+        HISTSIZE=2000
+        HISTFILE=$HOME/.zsh_history
 
         setopt HIST_IGNORE_DUPS SHARE_HISTORY HIST_FCNTL_LOCK
 
@@ -117,6 +135,14 @@ 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"
+        }
+
         HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
       '';
 
@@ -179,7 +205,8 @@ in
     environment.etc."zinputrc".source = ./zinputrc;
 
     environment.systemPackages = [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions
+      ++ optional cfg.enableSyntaxHighlighting pkgs.zsh-syntax-highlighting;
 
     environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index e4584146d6f..876e54a162c 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -14,7 +14,6 @@ with lib;
     (mkRenamedOptionModule [ "networking" "enableRT73Firmware" ] [ "networking" "enableRalinkFirmware" ])
 
     (mkRenamedOptionModule [ "services" "cadvisor" "host" ] [ "services" "cadvisor" "listenAddress" ])
-    (mkRenamedOptionModule [ "services" "dockerRegistry" "host" ] [ "services" "dockerRegistry" "listenAddress" ])
     (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" ])
@@ -30,6 +29,8 @@ with lib;
     (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
     (mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ] "")
 
+    (mkRenamedOptionModule [ "services" "clamav" "updater" "config" ] [ "services" "clamav" "updater" "extraConfig" ])
+
     # Old Grub-related options.
     (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ])
     (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ])
@@ -139,6 +140,29 @@ with lib;
     # fontconfig-ultimate
     (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "rendering" ] [ "fonts" "fontconfig" "ultimate" "preset" ])
 
+    # murmur
+    (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ])
+
+    # parsoid
+    (mkRemovedOptionModule [ "services" "parsoid" "interwikis" ] [ "services" "parsoid" "wikis" ])
+
+    # tarsnap
+    (mkRemovedOptionModule [ "services" "tarsnap" "cachedir" ] "Use services.tarsnap.archives.<name>.cachedir")
+
+    # alsa
+    (mkRenamedOptionModule [ "sound" "enableMediaKeys" ] [ "sound" "mediaKeys" "enable" ])
+
+    # postgrey
+    (mkMergedOptionModule [ [ "services" "postgrey" "inetAddr" ] [ "services" "postgrey" "inetPort" ] ] [ "services" "postgrey" "socket" ] (config: let
+        value = p: getAttrFromPath p config;
+        inetAddr = [ "services" "postgrey" "inetAddr" ];
+        inetPort = [ "services" "postgrey" "inetPort" ];
+      in
+        if value inetAddr == null
+        then { path = "/var/run/postgrey.sock"; }
+        else { addr = value inetAddr; port = value inetPort; }
+    ))
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
     (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
@@ -150,7 +174,7 @@ with lib;
     (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ] "")
     (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ] "")
     (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
-      "See the 16.03 release notes for more information.")
+      "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
     (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
   ];
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 3dac558b953..726e5471141 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -129,11 +129,10 @@ in
 
       certs = mkOption {
         default = { };
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule certOpts);
         description = ''
           Attribute set of certificates to get signed and renewed.
         '';
-        options = [ certOpts ];
         example = {
           "example.com" = {
             webroot = "/var/www/challenges/";
@@ -166,7 +165,8 @@ in
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains);
                 acmeService = {
                   description = "Renew ACME Certificate for ${cert}";
-                  after = [ "network.target" ];
+                  after = [ "network.target" "network-online.target" ];
+                  wants = [ "network-online.target" ];
                   serviceConfig = {
                     Type = "oneshot";
                     SuccessExitStatus = [ "0" "1" ];
@@ -178,6 +178,7 @@ in
                   path = [ pkgs.simp_le ];
                   preStart = ''
                     mkdir -p '${cfg.directory}'
+                    chown '${data.user}:${data.group}' '${cfg.directory}'
                     if [ ! -d '${cpath}' ]; then
                       mkdir '${cpath}'
                     fi
@@ -282,6 +283,7 @@ in
           timerConfig = {
             OnCalendar = cfg.renewInterval;
             Unit = "acme-${cert}.service";
+            Persistent = "yes";
           };
         })
       );
diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml
index 15ed4c04a23..823806f4641 100644
--- a/nixos/modules/security/acme.xml
+++ b/nixos/modules/security/acme.xml
@@ -67,31 +67,30 @@ options for the <literal>security.acme</literal> module.</para>
 </section>
 
 <section><title>Using ACME certificates in Nginx</title>
-<para>In practice ACME is mostly used for retrieval and renewal of
-  certificates that will be used in a webserver like Nginx. A configuration for
-  Nginx that uses the certificates from ACME for
-  <literal>foo.example.com</literal> will look similar to:
+<para>NixOS supports fetching ACME certificates for you by setting
+<literal>enableACME = true;</literal> in a virtualHost config. We
+first create self-signed placeholder certificates in place of the
+real ACME certs. The placeholder certs are overwritten when the ACME
+certs arrive. For <literal>foo.example.com</literal> the config would
+look like.
 </para>
 
 <programlisting>
-services.nginx.httpConfig = ''
-  server {
-    server_name foo.example.com;
-    listen 443 ssl;
-    ssl_certificate     ${config.security.acme.directory}/foo.example.com/fullchain.pem;
-    ssl_certificate_key ${config.security.acme.directory}/foo.example.com/key.pem;
-    root /var/www/foo.example.com/;
-  }
-'';
+services.nginx = {
+  enable = true;
+  virtualHosts = {
+    "foo.example.com" = {
+      forceSSL = true;
+      enableACME = true;
+      locations."/" = {
+        root = "/var/www";
+      };
+    };
+  };
+}
 </programlisting>
 
-<para>Now Nginx will try to use the certificates that will be retrieved by ACME.
-  ACME needs Nginx (or any other webserver) to function and Nginx needs
-  the certificates to actually start. For this reason the ACME module
-  automatically generates self-signed certificates that will be used by Nginx to
-  start. After that Nginx is used by ACME to retrieve the actual ACME
-  certificates. <literal>security.acme.preliminarySelfsigned</literal> can be
-  used to control whether to generate the self-signed certificates.
-</para>
+<para>At the moment you still have to restart Nginx after the ACME
+certs arrive.</para>
 </section>
 </chapter>
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
index 202639f9870..d323a158a4d 100644
--- a/nixos/modules/security/apparmor.nix
+++ b/nixos/modules/security/apparmor.nix
@@ -18,22 +18,30 @@ in
          default = [];
          description = "List of files containing AppArmor profiles.";
        };
+       packages = mkOption {
+         type = types.listOf types.package;
+         default = [];
+         description = "List of packages to be added to apparmor's include path";
+       };
      };
    };
 
    config = mkIf cfg.enable {
      environment.systemPackages = [ pkgs.apparmor-utils ];
 
-     systemd.services.apparmor = {
+     systemd.services.apparmor = let
+       paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
+         ([ pkgs.apparmor-profiles ] ++ cfg.packages);
+     in {
        wantedBy = [ "local-fs.target" ];
        serviceConfig = {
          Type = "oneshot";
          RemainAfterExit = "yes";
-         ExecStart = concatMapStrings (p:
-           ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv -I ${pkgs.apparmor-profiles}/etc/apparmor.d "${p}" ; ''
+         ExecStart = map (p:
+           ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
          ) cfg.profiles;
-         ExecStop = concatMapStrings (p:
-           ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}" ; ''
+         ExecStop = map (p:
+           ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
          ) cfg.profiles;
        };
      };
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix
index ebfe594d0c7..7ac21fd9650 100644
--- a/nixos/modules/security/audit.nix
+++ b/nixos/modules/security/audit.nix
@@ -104,7 +104,11 @@ in {
       description = "Kernel Auditing";
       wantedBy = [ "basic.target" ];
 
-      unitConfig.ConditionVirtualization = "!container";
+      unitConfig = {
+        ConditionVirtualization = "!container";
+        ConditionSecurity = [ "audit" ];
+      };
+
 
       path = [ pkgs.audit ];
 
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index 849530238e7..67469be18b4 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -4,10 +4,16 @@ with lib;
 
 let
 
+  cfg = config.security.pki;
+
+  cacertPackage = pkgs.cacert.override {
+    blacklist = cfg.caCertificateBlacklist;
+  };
+
   caCertificates = pkgs.runCommand "ca-certificates.crt"
     { files =
-        config.security.pki.certificateFiles ++
-        [ (builtins.toFile "extra.crt" (concatStringsSep "\n" config.security.pki.certificates)) ];
+        cfg.certificateFiles ++
+        [ (builtins.toFile "extra.crt" (concatStringsSep "\n" cfg.certificates)) ];
      }
     ''
       cat $files > $out
@@ -52,11 +58,27 @@ in
       '';
     };
 
+    security.pki.caCertificateBlacklist = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [
+        "WoSign" "WoSign China"
+        "CA WoSign ECC Root"
+        "Certification Authority of WoSign G2"
+      ];
+      description = ''
+        A list of blacklisted CA certificate names that won't be imported from
+        the Mozilla Trust Store into
+        <filename>/etc/ssl/certs/ca-certificates.crt</filename>. Use the
+        names from that file.
+      '';
+    };
+
   };
 
   config = {
 
-    security.pki.certificateFiles = [ "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
+    security.pki.certificateFiles = [ "${cacertPackage}/etc/ssl/certs/ca-bundle.crt" ];
 
     # NixOS canonical location + Debian/Ubuntu/Arch/Gentoo compatibility.
     environment.etc."ssl/certs/ca-certificates.crt".source = caCertificates;
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
index b5e1417fc89..e5b35427015 100644
--- a/nixos/modules/security/duosec.nix
+++ b/nixos/modules/security/duosec.nix
@@ -73,7 +73,7 @@ in
       };
 
       failmode = mkOption {
-        type = types.str;
+        type = types.enum [ "safe" "enum" ];
         default = "safe";
         description = ''
           On service or configuration errors that prevent Duo
@@ -115,7 +115,7 @@ in
       };
 
       prompts = mkOption {
-        type = types.int;
+        type = types.enum [ 1 2 3 ];
         default = 3;
         description = ''
           If a user fails to authenticate with a second factor, Duo
@@ -181,13 +181,7 @@ in
 
   config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
     assertions =
-      [ { assertion = cfg.failmode == "safe" || cfg.failmode == "secure";
-          message   = "Invalid value for failmode (must be safe or secure).";
-        }
-        { assertion = cfg.prompts == 1 || cfg.prompts == 2 || cfg.prompts == 3;
-          message   = "Invalid value for prompts (must be 1, 2, or 3).";
-        }
-        { assertion = !cfg.pam.enable;
+      [ { assertion = !cfg.pam.enable;
           message   = "PAM support is currently not implemented.";
         }
       ];
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
index ea1064c2d42..3726b6c7818 100644
--- a/nixos/modules/security/grsecurity.nix
+++ b/nixos/modules/security/grsecurity.nix
@@ -6,14 +6,6 @@ let
   cfg = config.security.grsecurity;
   grsecLockPath = "/proc/sys/kernel/grsecurity/grsec_lock";
 
-  # Ascertain whether ZFS is required for booting the system; grsecurity is
-  # currently incompatible with ZFS, rendering the system unbootable.
-  zfsNeededForBoot = filter
-    (fs: (fs.neededForBoot
-          || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
-          && fs.fsType == "zfs")
-    config.system.build.fileSystems != [];
-
   # Ascertain whether NixOS container support is required
   containerSupportRequired =
     config.boot.enableContainers && config.containers != {};
@@ -27,7 +19,14 @@ in
 
   options.security.grsecurity = {
 
-    enable = mkEnableOption "grsecurity/PaX";
+    enable = mkOption {
+      type = types.bool;
+      example = true;
+      default = false;
+      description = ''
+        Enable grsecurity/PaX.
+      '';
+    };
 
     lockTunables = mkOption {
       type = types.bool;
@@ -58,19 +57,12 @@ in
 
   config = mkIf cfg.enable {
 
-    # Allow the user to select a different package set, subject to the stated
-    # required kernel config
-    boot.kernelPackages = mkDefault pkgs.linuxPackages_grsec_nixos;
+    boot.kernelPackages = mkForce pkgs.linuxPackages_grsec_nixos;
 
-    boot.kernelParams = optional cfg.disableEfiRuntimeServices "noefi";
+    boot.kernelParams = [ "grsec_sysfs_restrict=0" ]
+      ++ optional cfg.disableEfiRuntimeServices "noefi";
 
-    system.requiredKernelConfig = with config.lib.kernelConfig;
-      [ (isEnabled "GRKERNSEC")
-        (isEnabled "PAX")
-        (isYES "GRKERNSEC_SYSCTL")
-        (isYES "GRKERNSEC_SYSCTL_DISTRO")
-        (isNO "GRKERNSEC_NO_RBAC")
-      ];
+    nixpkgs.config.grsecurity = true;
 
     # Install PaX related utillities into the system profile.
     environment.systemPackages = with pkgs; [ gradm paxctl pax-utils ];
@@ -118,26 +110,63 @@ in
     boot.kernel.sysctl = {
       # Read-only under grsecurity
       "kernel.kptr_restrict" = mkForce null;
+
+      # All grsec tunables default to off, those not enabled below are
+      # *disabled*.  We use mkDefault to allow expert users to override
+      # our choices, but use mkForce where tunables would outright
+      # conflict with other settings.
+
+      # Enable all chroot restrictions by default (overwritten as
+      # necessary below)
+      "kernel.grsecurity.chroot_caps" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_bad_rename" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_chmod" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_chroot" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_fchdir" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_mknod" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_mount" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_pivot" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_shmat" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_sysctl" = mkDefault 1;
+      "kernel.grsecurity.chroot_deny_unix" = mkDefault 1;
+      "kernel.grsecurity.chroot_enforce_chdir" = mkDefault 1;
+      "kernel.grsecurity.chroot_findtask" = mkDefault 1;
+      "kernel.grsecurity.chroot_restrict_nice" = mkDefault 1;
+
+      # Enable various grsec protections
+      "kernel.grsecurity.consistent_setxid" = mkDefault 1;
+      "kernel.grsecurity.deter_bruteforce" = mkDefault 1;
+      "kernel.grsecurity.fifo_restrictions" = mkDefault 1;
+      "kernel.grsecurity.harden_ipc" = mkDefault 1;
+      "kernel.grsecurity.harden_ptrace" = mkDefault 1;
+      "kernel.grsecurity.harden_tty" = mkDefault 1;
+      "kernel.grsecurity.ip_blackhole" = mkDefault 1;
+      "kernel.grsecurity.linking_restrictions" = mkDefault 1;
+      "kernel.grsecurity.ptrace_readexec" = mkDefault 1;
+
+      # Enable auditing
+      "kernel.grsecurity.audit_ptrace" = mkDefault 1;
+      "kernel.grsecurity.forkfail_logging" = mkDefault 1;
+      "kernel.grsecurity.rwxmap_logging" = mkDefault 1;
+      "kernel.grsecurity.signal_logging" = mkDefault 1;
+      "kernel.grsecurity.timechange_logging" = mkDefault 1;
     } // optionalAttrs config.nix.useSandbox {
       # chroot(2) restrictions that conflict with sandboxed Nix builds
       "kernel.grsecurity.chroot_caps" = mkForce 0;
+      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
       "kernel.grsecurity.chroot_deny_chroot" = mkForce 0;
       "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
       "kernel.grsecurity.chroot_deny_pivot" = mkForce 0;
-      "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
     } // optionalAttrs containerSupportRequired {
       # chroot(2) restrictions that conflict with NixOS lightweight containers
+      "kernel.grsecurity.chroot_caps" = mkForce 0;
       "kernel.grsecurity.chroot_deny_chmod" = mkForce 0;
       "kernel.grsecurity.chroot_deny_mount" = mkForce 0;
       "kernel.grsecurity.chroot_restrict_nice" = mkForce 0;
-      "kernel.grsecurity.chroot_caps" = mkForce 0;
+      # Disable privileged IO by default, unless X is enabled
+    } // optionalAttrs (!config.services.xserver.enable) {
+      "kernel.grsecurity.disable_priv_io" = mkDefault 1;
     };
 
-    assertions = [
-      { assertion = !zfsNeededForBoot;
-        message = "grsecurity is currently incompatible with ZFS";
-      }
-    ];
-
   };
 }
diff --git a/nixos/modules/security/grsecurity.xml b/nixos/modules/security/grsecurity.xml
index 28415e89bfa..a7bcf4924f0 100644
--- a/nixos/modules/security/grsecurity.xml
+++ b/nixos/modules/security/grsecurity.xml
@@ -51,6 +51,13 @@
       # nixos-rebuild boot
       # reboot
     </programlisting>
+    <note><para>
+      Enabling the grsecurity module overrides
+      <option>boot.kernelPackages</option>, to reduce the risk of
+      misconfiguration.  <xref linkend="sec-grsec-custom-kernel" />
+      describes how to use a custom kernel package set.
+    </para></note>
+
     For most users, further configuration should be unnecessary.  All users
     are encouraged to look over <xref linkend="sec-grsec-security" /> before
     using the system, however.  If you experience problems, please refer to
@@ -144,9 +151,6 @@
         a TCP simultaneous OPEN on that port before the connection is actually
         established.</para></listitem>
 
-        <listitem><para><filename class="directory">/sys</filename> hardening:
-        breaks systemd.</para></listitem>
-
         <listitem><para>Trusted path execution: a desirable feature, but
         requires some more work to operate smoothly on NixOS.</para></listitem>
       </itemizedlist>
@@ -201,33 +205,42 @@
   </para>
 
   <para>
-    To use a custom kernel with upstream's recommended settings for server
-    deployments:
+    To build a custom kernel using upstream's recommended settings for server
+    deployments, while still using the NixOS module:
     <programlisting>
-      boot.kernelPackages =
-        let
-          kernel = pkgs.linux_grsec_nixos.override {
-            extraConfig = ''
-              GRKERNSEC y
-              PAX y
-              GRKERNSEC_CONFIG_AUTO y
-              GRKERNSEC_CONFIG_SERVER y
-              GRKERNSEC_CONFIG_SECURITY y
-            '';
+      nixpkgs.config.packageOverrides = super: {
+        linux_grsec_nixos = super.linux_grsec_nixos.override {
+          extraConfig = ''
+            GRKERNSEC_CONFIG_AUTO y
+            GRKERNSEC_CONFIG_SERVER y
+            GRKERNSEC_CONFIG_SECURITY y
+          '';
           };
-          self = pkgs.linuxPackagesFor kernel self;
-        in self;
+      }
     </programlisting>
+  </para>
+
+  <para>
     The 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.  Most of these assumptions are
-    encoded as assertions &#x2014; mismatches should ideally result in a build
-    failure.  Currently, the only way to work around incompatibilities is to
-    eschew the NixOS module and do all configuration yourself.
+    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.
+
+    If not using the NixOS module, a custom grsecurity package set can
+    be specified inline instead, as in
+    <programlisting>
+      boot.kernelPackages =
+        let
+          kernel = pkgs.linux_grsec_nixos.override {
+            extraConfig = /* as above */;
+          };
+          self = pkgs.linuxPackagesFor kernel self;
+        in self;
+    </programlisting>
   </para>
 
   </sect1>
@@ -275,6 +288,10 @@
     <option>security.grsecurity.disableEfiRuntimeServices</option> to override
     this behavior.</para></listitem>
 
+    <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>
+
     <listitem><para>Virtualization: KVM is the preferred virtualization
     solution. Xen, Virtualbox, and VMWare are
     <emphasis>unsupported</emphasis> and most likely require a custom kernel.
@@ -308,6 +325,19 @@
       </programlisting>
     </para></listitem>
 
+    <listitem><para>
+      The gitlab service (<xref linkend="module-services-gitlab" />)
+      requires a variant of the <literal>ruby</literal> interpreter
+      built without `mprotect()` hardening, as in
+      <programlisting>
+        services.gitlab.packages.gitlab = pkgs.gitlab.override {
+          ruby = pkgs.ruby.overrideAttrs (attrs: {
+            postFixup = "paxmark m $out/bin/ruby";
+          });
+        };
+      </programlisting>
+    </para></listitem>
+
   </itemizedlist>
 
   </sect1>
@@ -330,13 +360,19 @@
 
       <listitem><para>
         <literal>pax_sanitize_slab={off|fast|full}</literal>: control kernel
-        slab object sanitization
+        slab object sanitization. Defaults to <literal>fast</literal>
       </para></listitem>
 
       <listitem><para>
         <literal>pax_size_overflow_report_only</literal>: log size overflow
         violations but leave the violating task running
       </para></listitem>
+
+      <listitem><para>
+        <literal>grsec_sysfs_restrict=[0|1]</literal>: toggle sysfs
+        restrictions. The NixOS module sets this to <literal>0</literal>
+        for systemd compatibility
+      </para></listitem>
     </itemizedlist>
   </para>
 
diff --git a/nixos/modules/security/hidepid.nix b/nixos/modules/security/hidepid.nix
index 8f2df380cfe..96443fda758 100644
--- a/nixos/modules/security/hidepid.nix
+++ b/nixos/modules/security/hidepid.nix
@@ -2,24 +2,26 @@
 with lib;
 
 {
-  options = {
-    security.hideProcessInformation = mkEnableOption "" // { description = ''
-      Restrict access to process information to the owning user.  Enabling
-      this option implies, among other things, that command-line arguments
-      remain private.  This option is recommended for most systems, unless
-      there's a legitimate reason for allowing unprivileged users to inspect
-      the process information of other users.
+  meta = {
+    maintainers = [ maintainers.joachifm ];
+    doc = ./hidepid.xml;
+  };
 
-      Members of the group "proc" are exempt from process information hiding.
-      To allow a service to run without process information hiding, add "proc"
-      to its supplementary groups via
-      <option>systemd.services.&lt;name?&gt;.serviceConfig.SupplementaryGroups</option>.
-    ''; };
+  options = {
+    security.hideProcessInformation = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Restrict process information to the owning user.
+      '';
+    };
   };
 
   config = mkIf config.security.hideProcessInformation {
     users.groups.proc.gid = config.ids.gids.proc;
+    users.groups.proc.members = [ "polkituser" ];
 
     boot.specialFileSystems."/proc".options = [ "hidepid=2" "gid=${toString config.ids.gids.proc}" ];
+    systemd.services.systemd-logind.serviceConfig.SupplementaryGroups = [ "proc" ];
   };
 }
diff --git a/nixos/modules/security/hidepid.xml b/nixos/modules/security/hidepid.xml
new file mode 100644
index 00000000000..5715ee7ac16
--- /dev/null
+++ b/nixos/modules/security/hidepid.xml
@@ -0,0 +1,33 @@
+<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-hidepid">
+
+  <title>Hiding process information</title>
+
+  <para>
+    Setting
+    <programlisting>
+      security.hideProcessInformation = true;
+    </programlisting>
+    ensures that access to process information is restricted to the
+    owning user.  This implies, among other things, that command-line
+    arguments remain private.  Unless your deployment relies on unprivileged
+    users being able to inspect the process information of other users, this
+    option should be safe to enable.
+  </para>
+
+  <para>
+    Members of the <literal>proc</literal> group are exempt from process
+    information hiding.
+  </para>
+
+  <para>
+    To allow a service <replaceable>foo</replaceable> to run without process information hiding, set
+    <programlisting>
+      systemd.services.<replaceable>foo</replaceable>.serviceConfig.SupplementaryGroups = [ "proc" ];
+    </programlisting>
+  </para>
+
+</chapter>
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 1c5e6862da6..c5088b64bb3 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -105,6 +105,16 @@ let
         '';
       };
 
+      setEnvironment = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether the service should set the environment variables
+          listed in <option>environment.sessionVariables</option>
+          using <literal>pam_env.so</literal>.
+        '';
+      };
+
       setLoginUid = mkOption {
         type = types.bool;
         description = ''
@@ -223,6 +233,8 @@ let
           account sufficient pam_unix.so
           ${optionalString use_ldap
               "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.services.sssd.enable
+              "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
           ${optionalString config.krb5.enable
               "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
 
@@ -263,6 +275,8 @@ let
               "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
+              "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"}
           ${optionalString config.krb5.enable ''
             auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
             auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
@@ -278,26 +292,32 @@ let
               "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
           ${optionalString use_ldap
               "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.services.sssd.enable
+              "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"}
           ${optionalString config.krb5.enable
               "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
           ${optionalString config.services.samba.syncPasswordsByPam
               "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"}
 
           # Session management.
-          session required pam_env.so envfile=${config.system.build.pamEnvironment}
+          ${optionalString cfg.setEnvironment ''
+            session required pam_env.so envfile=${config.system.build.pamEnvironment}
+          ''}
           session required pam_unix.so
           ${optionalString cfg.setLoginUid
               "session ${
                 if config.boot.isContainer then "optional" else "required"
               } pam_loginuid.so"}
           ${optionalString cfg.makeHomeDir
-              "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=/etc/skel umask=0022"}
+              "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0022"}
           ${optionalString cfg.updateWtmp
               "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
           ${optionalString config.security.pam.enableEcryptfs
               "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
           ${optionalString use_ldap
               "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.services.sssd.enable
+              "session optional ${pkgs.sssd}/lib/security/pam_sss.so"}
           ${optionalString config.krb5.enable
               "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
           ${optionalString cfg.otpwAuth
@@ -374,8 +394,7 @@ in
 
     security.pam.services = mkOption {
       default = [];
-      type = types.loaOf types.optionSet;
-      options = [ pamOpts ];
+      type = with types; loaOf (submodule pamOpts);
       description =
         ''
           This option defines the PAM services.  A service typically
@@ -386,6 +405,16 @@ in
         '';
     };
 
+    security.pam.makeHomeDir.skelDirectory = mkOption {
+      type = types.str;
+      default = "/var/empty";
+      example =  "/etc/skel";
+      description = ''
+        Path to skeleton directory whose contents are copied to home
+        directories newly created by <literal>pam_mkhomedir</literal>.
+      '';
+    };
+
     security.pam.enableSSHAgentAuth = mkOption {
       default = false;
       description =
@@ -436,6 +465,7 @@ in
       # Include the PAM modules in the system path mostly for the manpages.
       [ pkgs.pam ]
       ++ optional config.users.ldap.enable pam_ldap
+      ++ optional config.services.sssd.enable pkgs.sssd
       ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
       ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
       ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ]
@@ -495,6 +525,13 @@ in
         vlock = {};
         xlock = {};
         xscreensaver = {};
+
+        runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
+
+        /* FIXME: should runuser -l start a systemd session? Currently
+           it complains "Cannot create session: Already running in a
+           session". */
+        runuser-l = { rootOK = true; unixAuth = false; };
       };
 
   };
diff --git a/nixos/modules/security/permissions-wrappers/setuid-wrappers.nix b/nixos/modules/security/permissions-wrappers/setuid-wrappers.nix
index e1dca477d70..fe220c94313 100644
--- a/nixos/modules/security/permissions-wrappers/setuid-wrappers.nix
+++ b/nixos/modules/security/permissions-wrappers/setuid-wrappers.nix
@@ -117,6 +117,7 @@ in
 
           mkdir -p /run/setuid-wrapper-dirs
           wrapperDir=$(mktemp --directory --tmpdir=/run/setuid-wrapper-dirs setuid-wrappers.XXXXXXXXXX)
+          chmod a+rx $wrapperDir
 
           ${concatMapStrings makeSetuidWrapper setuidPrograms}
 
@@ -131,7 +132,7 @@ in
             # Compatibility with old state, just remove the folder and symlink
             rm -f ${wrapperDir}/*
             # if it happens to be a tmpfs
-            umount ${wrapperDir} || true
+            ${pkgs.utillinux}/bin/umount ${wrapperDir} || true
             rm -d ${wrapperDir}
             ln -d --symbolic $wrapperDir ${wrapperDir}
           else
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
index b14ea7a5f27..3a1ffc55e5f 100644
--- a/nixos/modules/security/rngd.nix
+++ b/nixos/modules/security/rngd.nix
@@ -18,7 +18,7 @@ with lib;
   config = mkIf config.security.rngd.enable {
     services.udev.extraRules = ''
       KERNEL=="random", TAG+="systemd"
-      SUBSYSTEM=="cpu", ENV{MODALIAS}=="x86cpu:*feature:*009E*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+      SUBSYSTEM=="cpu", ENV{MODALIAS}=="cpu:type:x86,*feature:*009E*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
       KERNEL=="hw_random", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
       ${if config.services.tcsd.enable then "" else ''KERNEL=="tpm0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"''}
     '';
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index 8a8f1525df4..652f23c2938 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -74,7 +74,7 @@ in
         Defaults env_keep+=SSH_AUTH_SOCK
 
         # "root" is allowed to do anything.
-        root        ALL=(ALL) SETENV: ALL
+        root        ALL=(ALL:ALL) SETENV: ALL
 
         # Users in the "wheel" group can do anything.
         %wheel      ALL=(ALL:ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index c63f4dc8d7f..53786dbc627 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -33,16 +33,6 @@ in
         '';
       };
 
-      enableMediaKeys = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether to enable volume and capture control with keyboard media keys.
-
-          Enabling this will turn on <option>services.actkbd</option>.
-        '';
-      };
-
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -54,6 +44,31 @@ in
         '';
       };
 
+      mediaKeys = {
+
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable volume and capture control with keyboard media keys.
+
+            Enabling this will turn on <option>services.actkbd</option>.
+          '';
+        };
+
+        volumeStep = mkOption {
+          type = types.string;
+          default = "1";
+          example = "1%";
+          description = ''
+            The value by which to increment/decrement volume on media keys.
+
+            See amixer(1) for allowed values.
+          '';
+        };
+
+      };
+
     };
 
   };
@@ -90,17 +105,17 @@ in
         };
       };
 
-    services.actkbd = mkIf config.sound.enableMediaKeys {
+    services.actkbd = mkIf config.sound.mediaKeys.enable {
       enable = true;
       bindings = [
         # "Mute" media key
         { keys = [ 113 ]; events = [ "key" ];       command = "${alsaUtils}/bin/amixer -q set Master toggle"; }
 
         # "Lower Volume" media key
-        { keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1- unmute"; }
+        { keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}- unmute"; }
 
         # "Raise Volume" media key
-        { keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1+ unmute"; }
+        { keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master ${config.sound.mediaKeys.volumeStep}+ unmute"; }
 
         # "Mic Mute" media key
         { keys = [ 190 ]; events = [ "key" ];       command = "${alsaUtils}/bin/amixer -q set Capture toggle"; }
diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix
index 029b14ab472..c0a0f037429 100644
--- a/nixos/modules/services/audio/mopidy.nix
+++ b/nixos/modules/services/audio/mopidy.nix
@@ -21,13 +21,7 @@ in {
 
     services.mopidy = {
 
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Whether to enable Mopidy, a music player daemon.
-        '';
-      };
+      enable = mkEnableOption "Mopidy, a music player daemon";
 
       dataDir = mkOption {
         default = "/var/lib/mopidy";
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index 5d5fef66794..a89215d7382 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -33,6 +33,7 @@ in {
     services.mpd = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = ''
           Whether to enable MPD, the music player daemon.
@@ -40,6 +41,7 @@ in {
       };
 
       musicDirectory = mkOption {
+        type = types.path;
         default = "${cfg.dataDir}/music";
         description = ''
           The directory where mpd reads music from.
@@ -47,6 +49,7 @@ in {
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "";
         description = ''
           Extra directives added to to the end of MPD's configuration file,
@@ -56,6 +59,7 @@ in {
       };
 
       dataDir = mkOption {
+        type = types.path;
         default = "/var/lib/mpd";
         description = ''
           The directory where MPD stores its state, tag cache,
@@ -64,11 +68,13 @@ in {
       };
 
       user = mkOption {
+        type = types.str;
         default = "mpd";
         description = "User account under which MPD runs.";
       };
 
       group = mkOption {
+        type = types.str;
         default = "mpd";
         description = "Group account under which MPD runs.";
       };
@@ -76,15 +82,17 @@ in {
       network = {
 
         listenAddress = mkOption {
-          default = "any";
+          type = types.str;
+          default = "127.0.0.1";
+          example = "any";
           description = ''
-            This setting sets the address for the daemon to listen on. Careful attention
-            should be paid if this is assigned to anything other then the default, any.
-            This setting can deny access to control of the daemon.
+            The address for the daemon to listen on.
+            Use <literal>any</literal> to listen on all addresses.
           '';
         };
 
         port = mkOption {
+          type = types.int;
           default = 6600;
           description = ''
             This setting is the TCP port that is desired for the daemon to get assigned
@@ -114,12 +122,12 @@ in {
       after = [ "network.target" "sound.target" ];
       description = "Music Player Daemon";
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.mpd ];
+
       preStart = "mkdir -p ${cfg.dataDir} && chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}";
-      script = "exec mpd --no-daemon ${mpdConf}";
       serviceConfig = {
         User = "${cfg.user}";
         PermissionsStartOnly = true;
+        ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon ${mpdConf}";
       };
     };
 
diff --git a/nixos/modules/services/audio/ympd.nix b/nixos/modules/services/audio/ympd.nix
new file mode 100644
index 00000000000..d34c1c9d83c
--- /dev/null
+++ b/nixos/modules/services/audio/ympd.nix
@@ -0,0 +1,57 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ympd;
+in {
+
+  ###### interface
+
+  options = {
+
+    services.ympd = {
+
+      enable = mkEnableOption "ympd, the MPD Web GUI";
+
+      webPort = mkOption {
+        type = types.string;
+        default = "8080";
+        description = "The port where ympd's web interface will be available.";
+        example = "ssl://8080:/path/to/ssl-private-key.pem";
+      };
+
+      mpd = {
+        host = mkOption {
+          type = types.string;
+          default = "localhost";
+          description = "The host where MPD is listening.";
+          example = "localhost";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = config.services.mpd.network.port;
+          description = "The port where MPD is listening.";
+          example = 6600;
+        };
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.ympd = {
+      description = "Standalone MPD Web GUI written in C";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.ympd}/bin/ympd --host ${cfg.mpd.host} --port ${toString cfg.mpd.port} --webport ${cfg.webPort} --user nobody";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/backup/bacula.nix b/nixos/modules/services/backup/bacula.nix
index 8a26aae75fe..340b0cf0723 100644
--- a/nixos/modules/services/backup/bacula.nix
+++ b/nixos/modules/services/backup/bacula.nix
@@ -198,8 +198,7 @@ in {
         description = ''
           This option defines director resources in Bacula File Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ directorOptions ];
+        type = with types; attrsOf (submodule directorOptions);
       };
 
       extraClientConfig = mkOption {
@@ -253,8 +252,7 @@ in {
         description = ''
           This option defines Director resources in Bacula Storage Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ directorOptions ];
+        type = with types; attrsOf (submodule directorOptions);
       };
 
       device = mkOption {
@@ -262,8 +260,7 @@ in {
         description = ''
           This option defines Device resources in Bacula Storage Daemon.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ deviceOptions ];
+        type = with types; attrsOf (submodule deviceOptions);
       };
  
       extraStorageConfig = mkOption {
@@ -343,6 +340,7 @@ in {
 
       extraConfig = mkOption {
         default = "";
+        type = types.lines;
         description = ''
           Extra configuration for Bacula Director Daemon.
         '';
diff --git a/nixos/modules/services/backup/crashplan.nix b/nixos/modules/services/backup/crashplan.nix
index 38cf8eb72fb..d0af2e416b6 100644
--- a/nixos/modules/services/backup/crashplan.nix
+++ b/nixos/modules/services/backup/crashplan.nix
@@ -49,7 +49,7 @@ with lib;
         ensureDir ${crashplan.vardir}/backupArchives 700
         ensureDir ${crashplan.vardir}/log 777
         cp -avn ${crashplan}/conf.template/* ${crashplan.vardir}/conf
-        for x in app.asar bin EULA.txt install.vars lang lib libjniwrap64.so libjniwrap.so libjtux64.so libjtux.so libmd564.so libmd5.so share skin upgrade; do
+        for x in app.asar bin install.vars lang lib libc42archive64.so libc52archive.so libjniwrap64.so libjniwrap.so libjtux64.so libjtux.so libleveldb64.so libleveldb.so libmd564.so libmd5.so share skin upgrade; do
           rm -f ${crashplan.vardir}/$x;
           ln -sf ${crashplan}/$x ${crashplan.vardir}/$x;
         done
diff --git a/nixos/modules/services/backup/rsnapshot.nix b/nixos/modules/services/backup/rsnapshot.nix
index ce628a72036..16815bcc860 100644
--- a/nixos/modules/services/backup/rsnapshot.nix
+++ b/nixos/modules/services/backup/rsnapshot.nix
@@ -7,11 +7,14 @@ let
   cfgfile = pkgs.writeText "rsnapshot.conf" ''
     config_version	1.2
     cmd_cp	${pkgs.coreutils}/bin/cp
+    cmd_rm	${pkgs.coreutils}/bin/rm
     cmd_rsync	${pkgs.rsync}/bin/rsync
     cmd_ssh	${pkgs.openssh}/bin/ssh
     cmd_logger	${pkgs.inetutils}/bin/logger
     cmd_du	${pkgs.coreutils}/bin/du
+    cmd_rsnapshot_diff	${pkgs.rsnapshot}/bin/rsnapshot-diff
     lockfile	/run/rsnapshot.pid
+    link_dest	1
 
     ${cfg.extraConfig}
   '';
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 24892a2a59a..67112343c33 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -1,25 +1,25 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 with lib;
 
 let
-  cfg = config.services.tarsnap;
+  gcfg = config.services.tarsnap;
 
   configFile = name: cfg: ''
-    cachedir ${config.services.tarsnap.cachedir}/${name}
-    keyfile  ${cfg.keyfile}
+    keyfile ${cfg.keyfile}
+    ${optionalString (cfg.cachedir != null) "cachedir ${cfg.cachedir}"}
     ${optionalString cfg.nodump "nodump"}
     ${optionalString cfg.printStats "print-stats"}
     ${optionalString cfg.printStats "humanize-numbers"}
     ${optionalString (cfg.checkpointBytes != null) ("checkpoint-bytes "+cfg.checkpointBytes)}
     ${optionalString cfg.aggressiveNetworking "aggressive-networking"}
-    ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
-    ${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)}
+    ${concatStringsSep "\n" (map (v: "exclude ${v}") cfg.excludes)}
+    ${concatStringsSep "\n" (map (v: "include ${v}") cfg.includes)}
     ${optionalString cfg.lowmem "lowmem"}
     ${optionalString cfg.verylowmem "verylowmem"}
-    ${optionalString (cfg.maxbw != null) ("maxbw "+toString cfg.maxbw)}
-    ${optionalString (cfg.maxbwRateUp != null) ("maxbw-rate-up "+toString cfg.maxbwRateUp)}
-    ${optionalString (cfg.maxbwRateDown != null) ("maxbw-rate-down "+toString cfg.maxbwRateDown)}
+    ${optionalString (cfg.maxbw != null) "maxbw ${toString cfg.maxbw}"}
+    ${optionalString (cfg.maxbwRateUp != null) "maxbw-rate-up ${toString cfg.maxbwRateUp}"}
+    ${optionalString (cfg.maxbwRateDown != null) "maxbw-rate-down ${toString cfg.maxbwRateDown}"}
   '';
 in
 {
@@ -60,34 +60,13 @@ in
         '';
       };
 
-      cachedir = mkOption {
-        type    = types.nullOr types.path;
-        default = "/var/cache/tarsnap";
-        description = ''
-          The cache allows tarsnap to identify previously stored data
-          blocks, reducing archival time and bandwidth usage.
-
-          Should the cache become desynchronized or corrupted, tarsnap
-          will refuse to run until you manually rebuild the cache with
-          <command>tarsnap --fsck</command>.
-
-          Note that each individual archive (specified below) has its own cache
-          directory specified under <literal>cachedir</literal>; this is because
-          tarsnap locks the cache during backups, meaning multiple services
-          archives cannot be backed up concurrently or overlap with a shared
-          cache.
-
-          Set to <literal>null</literal> to disable caching.
-        '';
-      };
-
       archives = mkOption {
-        type = types.attrsOf (types.submodule (
+        type = types.attrsOf (types.submodule ({ config, ... }:
           {
             options = {
               keyfile = mkOption {
                 type = types.str;
-                default = config.services.tarsnap.keyfile;
+                default = gcfg.keyfile;
                 description = ''
                   Set a specific keyfile for this archive. This defaults to
                   <literal>"/root/tarsnap.key"</literal> if left unspecified.
@@ -107,6 +86,21 @@ in
                 '';
               };
 
+              cachedir = mkOption {
+                type = types.nullOr types.path;
+                default = "/var/cache/tarsnap/${utils.escapeSystemdPath config.keyfile}";
+                description = ''
+                  The cache allows tarsnap to identify previously stored data
+                  blocks, reducing archival time and bandwidth usage.
+
+                  Should the cache become desynchronized or corrupted, tarsnap
+                  will refuse to run until you manually rebuild the cache with
+                  <command>tarsnap --fsck</command>.
+
+                  Set to <literal>null</literal> to disable caching.
+                '';
+              };
+
               nodump = mkOption {
                 type = types.bool;
                 default = true;
@@ -249,7 +243,7 @@ in
               };
 
             gamedata =
-              { directories = [ "/var/lib/minecraft "];
+              { directories = [ "/var/lib/minecraft" ];
                 period      = "*:30";
               };
           }
@@ -262,8 +256,8 @@ in
           archive names are suffixed by a 1 second resolution timestamp.
 
           For each member of the set is created a timer which triggers the
-          instanced <literal>tarsnap@</literal> service unit. You may use
-          <command>systemctl start tarsnap@archive-name</command> to
+          instanced <literal>tarsnap-archive-name</literal> service unit. You may use
+          <command>systemctl start tarsnap-archive-name</command> to
           manually trigger creation of <literal>archive-name</literal> at
           any time.
         '';
@@ -271,63 +265,73 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
+  config = mkIf gcfg.enable {
     assertions =
       (mapAttrsToList (name: cfg:
         { assertion = cfg.directories != [];
           message = "Must specify paths for tarsnap to back up";
-        }) cfg.archives) ++
+        }) gcfg.archives) ++
       (mapAttrsToList (name: cfg:
         { assertion = !(cfg.lowmem && cfg.verylowmem);
           message = "You cannot set both lowmem and verylowmem";
-        }) cfg.archives);
-
-    systemd.services."tarsnap@" = {
-      description = "Tarsnap archive '%i'";
-      requires    = [ "network-online.target" ];
-      after       = [ "network-online.target" ];
-
-      path = [ pkgs.iputils pkgs.tarsnap pkgs.coreutils ];
-
-      # In order for the persistent tarsnap timer to work reliably, we have to
-      # make sure that the tarsnap server is reachable after systemd starts up
-      # the service - therefore we sleep in a loop until we can ping the
-      # endpoint.
-      preStart = "while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done";
-      scriptArgs = "%i";
-      script = ''
-        mkdir -p -m 0755 ${dirOf cfg.cachedir}
-        mkdir -p -m 0700 ${cfg.cachedir}
-        chown root:root ${cfg.cachedir}
-        chmod 0700 ${cfg.cachedir}
-        mkdir -p -m 0700 ${cfg.cachedir}/$1
-        DIRS=`cat /etc/tarsnap/$1.dirs`
-        exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS
-      '';
-
-      serviceConfig = {
-        IOSchedulingClass = "idle";
-        NoNewPrivileges = "true";
-        CapabilityBoundingSet = "CAP_DAC_READ_SEARCH";
-        PermissionsStartOnly = "true";
-      };
-    };
+        }) gcfg.archives);
+
+    systemd.services =
+      mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
+        description = "Tarsnap archive '${name}'";
+        requires    = [ "network-online.target" ];
+        after       = [ "network-online.target" ];
+
+        path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ];
+
+        # In order for the persistent tarsnap timer to work reliably, we have to
+        # make sure that the tarsnap server is reachable after systemd starts up
+        # the service - therefore we sleep in a loop until we can ping the
+        # endpoint.
+        preStart = ''
+          while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done
+        '';
+
+        script =
+          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" -c -f "${name}-$(date +"%Y%m%d%H%M%S")" ${concatStringsSep " " cfg.directories}'';
+          in if (cfg.cachedir != null) then ''
+            mkdir -p ${cfg.cachedir}
+            chmod 0700 ${cfg.cachedir}
+
+            ( flock 9
+              if [ ! -e ${cfg.cachedir}/firstrun ]; then
+                ( flock 10
+                  flock -u 9
+                  tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck
+                  flock 9
+                ) 10>${cfg.cachedir}/firstrun
+              fi
+            ) 9>${cfg.cachedir}/lockf
+
+             exec flock ${cfg.cachedir}/firstrun ${run}
+          '' else "exec ${run}";
+
+        serviceConfig = {
+          Type = "oneshot";
+          IOSchedulingClass = "idle";
+          NoNewPrivileges = "true";
+          CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
+          PermissionsStartOnly = "true";
+        };
+      }) gcfg.archives;
 
     # Note: the timer must be Persistent=true, so that systemd will start it even
     # if e.g. your laptop was asleep while the latest interval occurred.
-    systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap@${name}"
+    systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}"
       { timerConfig.OnCalendar = cfg.period;
         timerConfig.Persistent = "true";
         wantedBy = [ "timers.target" ];
-      }) cfg.archives;
+      }) gcfg.archives;
 
     environment.etc =
-      (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf"
+      mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf"
         { text = configFile name cfg;
-        }) cfg.archives) //
-      (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs"
-        { text = concatStringsSep " " cfg.directories;
-        }) cfg.archives);
+        }) gcfg.archives;
 
     environment.systemPackages = [ pkgs.tarsnap ];
   };
diff --git a/nixos/modules/services/cluster/fleet.nix b/nixos/modules/services/cluster/fleet.nix
index 78d4ea93c49..ec03be39594 100644
--- a/nixos/modules/services/cluster/fleet.nix
+++ b/nixos/modules/services/cluster/fleet.nix
@@ -28,7 +28,7 @@ in {
 
     etcdServers = mkOption {
       type = types.listOf types.str;
-      default = [ "http://127.0.0.1:4001" ];
+      default = [ "http://127.0.0.1:2379" ];
       description = ''
         Fleet list of etcd endpoints to use.
       '';
diff --git a/nixos/modules/services/cluster/kubernetes.nix b/nixos/modules/services/cluster/kubernetes.nix
index 42efde36678..fbf7412a6cd 100644
--- a/nixos/modules/services/cluster/kubernetes.nix
+++ b/nixos/modules/services/cluster/kubernetes.nix
@@ -5,14 +5,77 @@ with lib;
 let
   cfg = config.services.kubernetes;
 
+  skipAttrs = attrs: map (filterAttrs (k: v: k != "enable"))
+    (filter (v: !(hasAttr "enable" v) || v.enable) attrs);
+
+  infraContainer = pkgs.dockerTools.buildImage {
+    name = "pause";
+    tag = "latest";
+    contents = cfg.package.pause;
+    config.Cmd = "/bin/pause";
+  };
+
+  kubeconfig = pkgs.writeText "kubeconfig" (builtins.toJSON {
+    apiVersion = "v1";
+    kind = "Config";
+    clusters = [{
+      name = "local";
+      cluster.certificate-authority = cfg.kubeconfig.caFile;
+      cluster.server = cfg.kubeconfig.server;
+    }];
+    users = [{
+      name = "kubelet";
+      user = {
+        client-certificate = cfg.kubeconfig.certFile;
+        client-key = cfg.kubeconfig.keyFile;
+      };
+    }];
+    contexts = [{
+      context = {
+        cluster = "local";
+        user = "kubelet";
+      };
+      current-context = "kubelet-context";
+    }];
+  });
+
+  policyFile = pkgs.writeText "kube-policy"
+    concatStringsSep "\n" (map (builtins.toJSON cfg.apiserver.authorizationPolicy));
+
+  cniConfig = pkgs.buildEnv {
+    name = "kubernetes-cni-config";
+    paths = imap (i: entry:
+      pkgs.writeTextDir "${10+i}-${entry.type}.conf" (builtins.toJSON entry)
+    ) cfg.kubelet.cni.config;
+  };
+
+  manifests = pkgs.buildEnv {
+    name = "kubernetes-manifests";
+    paths = mapAttrsToList (name: manifest:
+      pkgs.writeTextDir "${name}.json" (builtins.toJSON manifest)
+    ) cfg.kubelet.manifests;
+  };
+
 in {
 
   ###### interface
 
   options.services.kubernetes = {
+    roles = mkOption {
+      description = ''
+        Kubernetes role that this machine should take.
+
+        Master role will enable etcd, apiserver, scheduler and controller manager
+        services. Node role will enable etcd, docker, kubelet and proxy services.
+      '';
+      default = [];
+      type = types.listOf (types.enum ["master" "node"]);
+    };
+
     package = mkOption {
       description = "Kubernetes package to use.";
       type = types.package;
+      default = pkgs.kubernetes;
     };
 
     verbose = mkOption {
@@ -21,21 +84,56 @@ in {
       type = types.bool;
     };
 
-    etcdServers = mkOption {
-      description = "Kubernetes list of etcd servers to watch.";
-      default = [ "127.0.0.1:4001" ];
-      type = types.listOf types.str;
+    etcd = {
+      servers = mkOption {
+        description = "List of etcd servers. By default etcd is started, except if this option is changed.";
+        default = ["http://127.0.0.1:2379"];
+        type = types.listOf types.str;
+      };
+
+      keyFile = mkOption {
+        description = "Etcd key file";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      certFile = mkOption {
+        description = "Etcd cert file";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      caFile = mkOption {
+        description = "Etcd ca file";
+        default = null;
+        type = types.nullOr types.path;
+      };
     };
 
-    roles = mkOption {
-      description = ''
-        Kubernetes role that this machine should take.
+    kubeconfig = {
+      server = mkOption {
+        description = "Kubernetes apiserver server address";
+        default = "http://${cfg.apiserver.address}:${toString cfg.apiserver.port}";
+        type = types.str;
+      };
 
-        Master role will enable etcd, apiserver, scheduler and controller manager
-        services. Node role will enable etcd, docker, kubelet and proxy services.
-      '';
-      default = [];
-      type = types.listOf (types.enum ["master" "node"]);
+      caFile = mkOption {
+        description = "Certificate authrority file to use to connect to kuberentes apiserver";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      certFile = mkOption {
+        description = "Client certificate file to use to connect to kubernetes";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      keyFile = mkOption {
+        description = "Client key file to use to connect to kubernetes";
+        type = types.nullOr types.path;
+        default = null;
+      };
     };
 
     dataDir = mkOption {
@@ -44,12 +142,6 @@ in {
       type = types.path;
     };
 
-    dockerCfg = mkOption {
-      description = "Kubernetes contents of dockercfg file.";
-      default = "";
-      type = types.lines;
-    };
-
     apiserver = {
       enable = mkOption {
         description = "Whether to enable kubernetes apiserver.";
@@ -72,6 +164,16 @@ in {
         type = types.str;
       };
 
+      advertiseAddress = mkOption {
+        description = ''
+          Kubernetes apiserver IP address on which to advertise the apiserver
+          to members of the cluster. This address must be reachable by the rest
+          of the cluster.
+        '';
+        default = null;
+        type = types.nullOr types.str;
+      };
+
       port = mkOption {
         description = "Kubernetes apiserver listening port.";
         default = 8080;
@@ -80,41 +182,36 @@ in {
 
       securePort = mkOption {
         description = "Kubernetes apiserver secure port.";
-        default = 6443;
+        default = 443;
         type = types.int;
       };
 
       tlsCertFile = mkOption {
         description = "Kubernetes apiserver certificate file.";
-        default = "";
-        type = types.str;
+        default = null;
+        type = types.nullOr types.path;
       };
 
-      tlsPrivateKeyFile = mkOption {
+      tlsKeyFile = mkOption {
         description = "Kubernetes apiserver private key file.";
-        default = "";
-        type = types.str;
+        default = null;
+        type = types.nullOr types.path;
       };
 
       clientCaFile = mkOption {
         description = "Kubernetes apiserver CA file for client auth.";
-        default = "";
-        type = types.str;
+        default = null;
+        type = types.nullOr types.path;
       };
 
       tokenAuth = mkOption {
         description = ''
           Kubernetes apiserver token authentication file. See
-          <link xlink:href="http://kubernetes.io/v1.0/docs/admin/authentication.html"/>
+          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
         '';
-        default = {};
-        example = literalExample ''
-          {
-            alice = "abc123";
-            bob = "xyz987";
-          }
-        '';
-        type = types.attrsOf types.str;
+        default = null;
+        example = ''token,user,uid,"group1,group2,group3"'';
+        type = types.nullOr types.lines;
       };
 
       authorizationMode = mkOption {
@@ -148,13 +245,13 @@ in {
 
       allowPrivileged = mkOption {
         description = "Whether to allow privileged containers on kubernetes.";
-        default = false;
+        default = true;
         type = types.bool;
       };
 
       portalNet = mkOption {
         description = "Kubernetes CIDR notation IP range from which to assign portal IPs";
-        default = "10.10.10.10/16";
+        default = "10.10.10.10/24";
         type = types.str;
       };
 
@@ -171,9 +268,9 @@ in {
       admissionControl = mkOption {
         description = ''
           Kubernetes admission control plugins to use. See
-          <link xlink:href="http://kubernetes.io/v1.0/docs/admin/admission-controllers.html"/>
+          <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/>
         '';
-        default = ["AlwaysAdmit"];
+        default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota"];
         example = [
           "NamespaceLifecycle" "NamespaceExists" "LimitRanger"
           "SecurityContextDeny" "ServiceAccount" "ResourceQuota"
@@ -181,15 +278,40 @@ in {
         type = types.listOf types.str;
       };
 
-      serviceAccountKey = mkOption {
+      serviceAccountKeyFile = mkOption {
         description = ''
           Kubernetes apiserver PEM-encoded x509 RSA private or public key file,
-          used to verify ServiceAccount tokens.
+          used to verify ServiceAccount tokens. By default tls private key file
+          is used.
         '';
         default = null;
         type = types.nullOr types.path;
       };
 
+      kubeletClientCaFile = mkOption {
+        description = "Path to a cert file for connecting to kubelet";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      kubeletClientCertFile = mkOption {
+        description = "Client certificate to use for connections to kubelet";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      kubeletClientKeyFile = mkOption {
+        description = "Key to use for connections to kubelet";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      kubeletHttps = mkOption {
+        description = "Whether to use https for connections to kubelet";
+        default = true;
+        type = types.bool;
+      };
+
       extraOpts = mkOption {
         description = "Kubernetes apiserver extra command line options.";
         default = "";
@@ -216,10 +338,10 @@ in {
         type = types.int;
       };
 
-      master = mkOption {
-        description = "Kubernetes apiserver address";
-        default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
-        type = types.str;
+      leaderElect = mkOption {
+        description = "Whether to start leader election before executing main loop";
+        type = types.bool;
+        default = false;
       };
 
       extraOpts = mkOption {
@@ -248,13 +370,13 @@ in {
         type = types.int;
       };
 
-      master = mkOption {
-        description = "Kubernetes apiserver address";
-        default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
-        type = types.str;
+      leaderElect = mkOption {
+        description = "Whether to start leader election before executing main loop";
+        type = types.bool;
+        default = false;
       };
 
-      serviceAccountPrivateKey = mkOption {
+      serviceAccountKeyFile = mkOption {
         description = ''
           Kubernetes controller manager PEM-encoded private RSA key file used to
           sign service account tokens
@@ -272,6 +394,12 @@ in {
         type = types.nullOr types.path;
       };
 
+      clusterCidr = mkOption {
+        description = "Kubernetes controller manager CIDR Range for Pods in cluster";
+        default = "10.10.0.0/16";
+        type = types.str;
+      };
+
       extraOpts = mkOption {
         description = "Kubernetes controller manager extra command line options.";
         default = "";
@@ -292,6 +420,12 @@ in {
         type = types.bool;
       };
 
+      registerSchedulable = mkOption {
+        description = "Register the node as schedulable. No-op if register-node is false.";
+        default = true;
+        type = types.bool;
+      };
+
       address = mkOption {
         description = "Kubernetes kubelet info server listening address.";
         default = "0.0.0.0";
@@ -304,6 +438,18 @@ in {
         type = types.int;
       };
 
+      tlsCertFile = mkOption {
+        description = "File containing x509 Certificate for HTTPS.";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
+      tlsKeyFile = mkOption {
+        description = "File containing x509 private key matching tlsCertFile.";
+        default = null;
+        type = types.nullOr types.path;
+      };
+
       healthz = {
         bind = mkOption {
           description = "Kubernetes kubelet healthz listening address.";
@@ -326,19 +472,10 @@ in {
 
       allowPrivileged = mkOption {
         description = "Whether to allow kubernetes containers to request privileged mode.";
-        default = false;
+        default = true;
         type = types.bool;
       };
 
-      apiServers = mkOption {
-        description = ''
-          Kubernetes kubelet list of Kubernetes API servers for publishing events,
-          and reading pods and services.
-        '';
-        default = ["${cfg.apiserver.address}:${toString cfg.apiserver.port}"];
-        type = types.listOf types.str;
-      };
-
       cadvisorPort = mkOption {
         description = "Kubernetes kubelet local cadvisor port.";
         default = 4194;
@@ -347,16 +484,62 @@ in {
 
       clusterDns = mkOption {
         description = "Use alternative dns.";
-        default = "";
+        default = "10.10.0.1";
         type = types.str;
       };
 
       clusterDomain = mkOption {
         description = "Use alternative domain.";
-        default = "kubernetes.io";
+        default = "cluster.local";
         type = types.str;
       };
 
+      networkPlugin = mkOption {
+        description = "Network plugin to use by kubernetes";
+        type = types.nullOr (types.enum ["cni" "kubenet"]);
+        default = "kubenet";
+      };
+
+      cni = {
+        packages = mkOption {
+          description = "List of network plugin packages to install";
+          type = types.listOf types.package;
+          default = [];
+        };
+
+        config = mkOption {
+          description = "Kubernetes CNI configuration";
+          type = types.listOf types.attrs;
+          default = [];
+          example = literalExample ''
+            [{
+              "cniVersion": "0.2.0",
+              "name": "mynet",
+              "type": "bridge",
+              "bridge": "cni0",
+              "isGateway": true,
+              "ipMasq": true,
+              "ipam": {
+                  "type": "host-local",
+                  "subnet": "10.22.0.0/16",
+                  "routes": [
+                      { "dst": "0.0.0.0/0" }
+                  ]
+              }
+            } {
+              "cniVersion": "0.2.0",
+              "type": "loopback"
+            }]
+          '';
+        };
+      };
+
+      manifests = mkOption {
+        description = "List of manifests to bootstrap with kubelet";
+        type = types.attrsOf types.attrs;
+        default = {};
+      };
+
       extraOpts = mkOption {
         description = "Kubernetes kubelet extra command line options.";
         default = "";
@@ -377,12 +560,6 @@ in {
         type = types.str;
       };
 
-      master = mkOption {
-        description = "Kubernetes apiserver address";
-        default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
-        type = types.str;
-      };
-
       extraOpts = mkOption {
         description = "Kubernetes proxy extra command line options.";
         default = "";
@@ -390,23 +567,23 @@ in {
       };
     };
 
-    kube2sky = {
-      enable = mkEnableOption "Whether to enable kube2sky dns service.";
+    dns = {
+      enable = mkEnableOption "kubernetes dns service.";
 
-      domain = mkOption  {
-        description = "Kuberntes kube2sky domain under which all DNS names will be hosted.";
-        default = cfg.kubelet.clusterDomain;
-        type = types.str;
+      port = mkOption {
+        description = "Kubernetes dns listening port";
+        default = 53;
+        type = types.int;
       };
 
-      master = mkOption {
-        description = "Kubernetes apiserver address";
-        default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
+      domain = mkOption  {
+        description = "Kuberntes dns domain under which to create names.";
+        default = cfg.kubelet.clusterDomain;
         type = types.str;
       };
 
       extraOpts = mkOption {
-        description = "Kubernetes kube2sky extra command line options.";
+        description = "Kubernetes dns extra command line options.";
         default = "";
         type = types.str;
       };
@@ -416,50 +593,118 @@ in {
   ###### implementation
 
   config = mkMerge [
+    (mkIf cfg.kubelet.enable {
+      systemd.services.kubelet = {
+        description = "Kubernetes Kubelet Service";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" "docker.service" "kube-apiserver.service" ];
+        path = with pkgs; [ gitMinimal openssh docker utillinux iproute ethtool thin-provisioning-tools iptables ];
+        preStart = ''
+          docker load < ${infraContainer}
+          rm /opt/cni/bin/* || true
+          ${concatMapStringsSep "\n" (p: "ln -fs ${p.plugins}/* /opt/cni/bin") cfg.kubelet.cni.packages}
+        '';
+        serviceConfig = {
+          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"} \
+            ${optionalString (cfg.kubelet.tlsCertFile != null)
+              "--tls-cert-file=${cfg.kubelet.tlsCertFile}"} \
+            ${optionalString (cfg.kubelet.tlsKeyFile != null)
+              "--tls-private-key-file=${cfg.kubelet.tlsKeyFile}"} \
+            --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"} \
+            --root-dir=${cfg.dataDir} \
+            --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \
+            ${optionalString (cfg.kubelet.clusterDns != "")
+              "--cluster-dns=${cfg.kubelet.clusterDns}"} \
+            ${optionalString (cfg.kubelet.clusterDomain != "")
+              "--cluster-domain=${cfg.kubelet.clusterDomain}"} \
+            --pod-infra-container-image=pause \
+            ${optionalString (cfg.kubelet.networkPlugin != null)
+              "--network-plugin=${cfg.kubelet.networkPlugin}"} \
+            --cni-conf-dir=${cniConfig} \
+            --reconcile-cidr \
+            --hairpin-mode=hairpin-veth \
+            ${optionalString cfg.verbose "--v=6 --log_flush_frequency=1s"} \
+            ${cfg.kubelet.extraOpts}
+          '';
+          WorkingDirectory = cfg.dataDir;
+        };
+      };
+
+      environment.etc = mapAttrs' (name: manifest:
+        nameValuePair "kubernetes/manifests/${name}.json" {
+          text = builtins.toJSON manifest;
+          mode = "0755";
+        }
+      ) cfg.kubelet.manifests;
+
+      # Allways include cni plugins
+      services.kubernetes.kubelet.cni.packages = [pkgs.cni];
+    })
+
     (mkIf cfg.apiserver.enable {
       systemd.services.kube-apiserver = {
-        description = "Kubernetes Api Server";
+        description = "Kubernetes Kubelet Service";
         wantedBy = [ "multi-user.target" ];
-        requires = ["kubernetes-setup.service"];
-        after = [ "network-interfaces.target" "etcd.service" ];
+        after = [ "network.target" "docker.service" ];
         serviceConfig = {
-          ExecStart = let
-            authorizationPolicyFile =
-              pkgs.writeText "kubernetes-policy"
-                (builtins.toJSON cfg.apiserver.authorizationPolicy);
-            tokenAuthFile =
-              pkgs.writeText "kubernetes-auth"
-                (concatImapStringsSep "\n" (i: v: v + "," + (toString i))
-                    (mapAttrsToList (name: token: token + "," + name) cfg.apiserver.tokenAuth));
-          in ''${cfg.package}/bin/kube-apiserver \
-            --etcd-servers=${concatMapStringsSep "," (f: "http://${f}") cfg.etcdServers} \
-            --insecure-bind-address=${cfg.apiserver.address} \
+          ExecStart = ''${cfg.package}/bin/kube-apiserver \
+            --etcd-servers=${concatStringsSep "," cfg.etcd.servers} \
+            ${optionalString (cfg.etcd.caFile != null)
+              "--etcd-cafile=${cfg.etcd.caFile}"} \
+            ${optionalString (cfg.etcd.certFile != null)
+              "--etcd-certfile=${cfg.etcd.certFile}"} \
+            ${optionalString (cfg.etcd.keyFile != null)
+              "--etcd-keyfile=${cfg.etcd.keyFile}"} \
             --insecure-port=${toString cfg.apiserver.port} \
-            --bind-address=${cfg.apiserver.publicAddress} \
+            --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"} \
-            ${optionalString (cfg.apiserver.tlsCertFile!="")
+            ${optionalString (cfg.apiserver.tlsCertFile != null)
               "--tls-cert-file=${cfg.apiserver.tlsCertFile}"} \
-            ${optionalString (cfg.apiserver.tlsPrivateKeyFile!="")
-              "--tls-private-key-file=${cfg.apiserver.tlsPrivateKeyFile}"} \
-            ${optionalString (cfg.apiserver.tokenAuth!=[])
-              "--token-auth-file=${tokenAuthFile}"} \
-            ${optionalString (cfg.apiserver.clientCaFile!="")
+            ${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"} \
+            ${optionalString (cfg.apiserver.kubeletClientCaFile != null)
+              "--kubelet-certificate-authority=${cfg.apiserver.kubeletClientCaFile}"} \
+            ${optionalString (cfg.apiserver.kubeletClientCertFile != null)
+              "--kubelet-client-certificate=${cfg.apiserver.kubeletClientCertFile}"} \
+            ${optionalString (cfg.apiserver.kubeletClientKeyFile != null)
+              "--kubelet-client-key=${cfg.apiserver.kubeletClientKeyFile}"} \
+            ${optionalString (cfg.apiserver.clientCaFile != null)
               "--client-ca-file=${cfg.apiserver.clientCaFile}"} \
             --authorization-mode=${cfg.apiserver.authorizationMode} \
             ${optionalString (cfg.apiserver.authorizationMode == "ABAC")
-              "--authorization-policy-file=${authorizationPolicyFile}"} \
+              "--authorization-policy-file=${policyFile}"} \
             --secure-port=${toString cfg.apiserver.securePort} \
             --service-cluster-ip-range=${cfg.apiserver.portalNet} \
-            ${optionalString (cfg.apiserver.runtimeConfig!="")
+            ${optionalString (cfg.apiserver.runtimeConfig != "")
               "--runtime-config=${cfg.apiserver.runtimeConfig}"} \
             --admission_control=${concatStringsSep "," cfg.apiserver.admissionControl} \
-            ${optionalString (cfg.apiserver.serviceAccountKey!=null)
-              "--service-account-key-file=${cfg.apiserver.serviceAccountKey}"} \
-            --logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \
+            ${optionalString (cfg.apiserver.serviceAccountKeyFile!=null)
+              "--service-account-key-file=${cfg.apiserver.serviceAccountKeyFile}"} \
+            ${optionalString cfg.verbose "--v=6"} \
+            ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
             ${cfg.apiserver.extraOpts}
           '';
+          WorkingDirectory = cfg.dataDir;
           User = "kubernetes";
+          Group = "kubernetes";
+          AmbientCapabilities = "cap_net_bind_service";
+          Restart = "on-failure";
+          RestartSec = 5;
         };
       };
     })
@@ -468,17 +713,20 @@ in {
       systemd.services.kube-scheduler = {
         description = "Kubernetes Scheduler Service";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" "kubernetes-apiserver.service" ];
+        after = [ "kube-apiserver.service" ];
         serviceConfig = {
           ExecStart = ''${cfg.package}/bin/kube-scheduler \
             --address=${cfg.scheduler.address} \
             --port=${toString cfg.scheduler.port} \
-            --master=${cfg.scheduler.master} \
-            --logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \
+            --leader-elect=${if cfg.scheduler.leaderElect then "true" else "false"} \
+            --kubeconfig=${kubeconfig} \
+            ${optionalString cfg.verbose "--v=6"} \
+            ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
             ${cfg.scheduler.extraOpts}
           '';
+          WorkingDirectory = cfg.dataDir;
           User = "kubernetes";
+          Group = "kubernetes";
         };
       };
     })
@@ -487,113 +735,94 @@ in {
       systemd.services.kube-controller-manager = {
         description = "Kubernetes Controller Manager Service";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" "kubernetes-apiserver.service" ];
+        after = [ "kube-apiserver.service" ];
         serviceConfig = {
           ExecStart = ''${cfg.package}/bin/kube-controller-manager \
             --address=${cfg.controllerManager.address} \
             --port=${toString cfg.controllerManager.port} \
-            --master=${cfg.controllerManager.master} \
-            ${optionalString (cfg.controllerManager.serviceAccountPrivateKey!=null)
-              "--service-account-private-key-file=${cfg.controllerManager.serviceAccountPrivateKey}"} \
+            --kubeconfig=${kubeconfig} \
+            --leader-elect=${if cfg.controllerManager.leaderElect then "true" else "false"} \
+            ${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"} \
             ${optionalString (cfg.controllerManager.rootCaFile!=null)
               "--root-ca-file=${cfg.controllerManager.rootCaFile}"} \
-            --logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \
+            ${optionalString (cfg.controllerManager.clusterCidr!=null)
+              "--cluster-cidr=${cfg.controllerManager.clusterCidr}"} \
+            --allocate-node-cidrs=true \
+            ${optionalString cfg.verbose "--v=6"} \
+            ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
             ${cfg.controllerManager.extraOpts}
           '';
+          WorkingDirectory = cfg.dataDir;
           User = "kubernetes";
+          Group = "kubernetes";
         };
       };
     })
 
-    (mkIf cfg.kubelet.enable {
-      systemd.services.kubelet = {
-        description = "Kubernetes Kubelet Service";
-        wantedBy = [ "multi-user.target" ];
-        requires = ["kubernetes-setup.service"];
-        after = [ "network-interfaces.target" "etcd.service" "docker.service" ];
-        path = [ pkgs.gitMinimal pkgs.openssh ];
-        script = ''
-          export PATH="/bin:/sbin:/usr/bin:/usr/sbin:$PATH"
-          exec ${cfg.package}/bin/kubelet \
-            --api-servers=${concatMapStringsSep "," (f: "http://${f}") cfg.kubelet.apiServers}  \
-            --register-node=${if cfg.kubelet.registerNode then "true" else "false"} \
-            --address=${cfg.kubelet.address} \
-            --port=${toString cfg.kubelet.port} \
-            --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"} \
-            --root-dir=${cfg.dataDir} \
-            --cadvisor_port=${toString cfg.kubelet.cadvisorPort} \
-            ${optionalString (cfg.kubelet.clusterDns != "")
-                ''--cluster-dns=${cfg.kubelet.clusterDns}''} \
-            ${optionalString (cfg.kubelet.clusterDomain != "")
-                ''--cluster-domain=${cfg.kubelet.clusterDomain}''} \
-            --logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log_flush_frequency=1s"} \
-            ${cfg.kubelet.extraOpts}
-          '';
-        serviceConfig.WorkingDirectory = cfg.dataDir;
-      };
-    })
-
     (mkIf cfg.proxy.enable {
       systemd.services.kube-proxy = {
         description = "Kubernetes Proxy Service";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" "etcd.service" ];
+        after = [ "kube-apiserver.service" ];
+        path = [pkgs.iptables];
         serviceConfig = {
           ExecStart = ''${cfg.package}/bin/kube-proxy \
-            --master=${cfg.proxy.master} \
+            --kubeconfig=${kubeconfig} \
             --bind-address=${cfg.proxy.address} \
-            --logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \
-            ${cfg.proxy.extraOpts}
+            ${optionalString cfg.verbose "--v=6"} \
+            ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
+            ${cfg.controllerManager.extraOpts}
           '';
-          Restart = "always"; # Retry connection
-          RestartSec = "5s";
+          WorkingDirectory = cfg.dataDir;
         };
       };
     })
 
-    (mkIf cfg.kube2sky.enable {
-      systemd.services.kube2sky = {
-        description = "Kubernetes Dns Bridge Service";
+    (mkIf cfg.dns.enable {
+      systemd.services.kube-dns = {
+        description = "Kubernetes Dns Service";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" "skydns.service" "etcd.service" "kubernetes-apiserver.service" ];
+        after = [ "kube-apiserver.service" ];
         serviceConfig = {
-          ExecStart = ''${cfg.package}/bin/kube2sky \
-            -etcd-server=http://${head cfg.etcdServers} \
-            -domain=${cfg.kube2sky.domain} \
-            -kube_master_url=http://${cfg.kube2sky.master} \
-            -logtostderr=true \
-            ${optionalString cfg.verbose "--v=6 --log-flush-frequency=1s"} \
-            ${cfg.kube2sky.extraOpts}
+          ExecStart = ''${cfg.package}/bin/kube-dns \
+            --kubecfg-file=${kubeconfig} \
+            --dns-port=${toString cfg.dns.port} \
+            --domain=${cfg.dns.domain} \
+            ${optionalString cfg.verbose "--v=6"} \
+            ${optionalString cfg.verbose "--log-flush-frequency=1s"} \
+            ${cfg.dns.extraOpts}
           '';
+          WorkingDirectory = cfg.dataDir;
           User = "kubernetes";
+          Group = "kubernetes";
+          AmbientCapabilities = "cap_net_bind_service";
+          SendSIGHUP = true;
         };
       };
     })
 
+    (mkIf cfg.kubelet.enable {
+      boot.kernelModules = ["br_netfilter"];
+    })
+
     (mkIf (any (el: el == "master") cfg.roles) {
+      virtualisation.docker.enable = mkDefault true;
+      services.kubernetes.kubelet.enable = mkDefault true;
+      services.kubernetes.kubelet.allowPrivileged = mkDefault true;
       services.kubernetes.apiserver.enable = mkDefault true;
       services.kubernetes.scheduler.enable = mkDefault true;
       services.kubernetes.controllerManager.enable = mkDefault true;
-      services.kubernetes.kube2sky.enable = mkDefault true;
+      services.etcd.enable = mkDefault (cfg.etcd.servers == ["http://127.0.0.1:2379"]);
     })
 
     (mkIf (any (el: el == "node") cfg.roles) {
       virtualisation.docker.enable = mkDefault true;
+      virtualisation.docker.logDriver = mkDefault "json-file";
       services.kubernetes.kubelet.enable = mkDefault true;
       services.kubernetes.proxy.enable = mkDefault true;
-    })
-
-    (mkIf (any (el: el == "node" || el == "master") cfg.roles) {
-      services.etcd.enable = mkDefault true;
-
-      services.skydns.enable = mkDefault true;
-      services.skydns.domain = mkDefault cfg.kubelet.clusterDomain;
+      services.kubernetes.dns.enable = mkDefault true;
     })
 
     (mkIf (
@@ -601,24 +830,16 @@ in {
         cfg.scheduler.enable ||
         cfg.controllerManager.enable ||
         cfg.kubelet.enable ||
-        cfg.proxy.enable
+        cfg.proxy.enable ||
+        cfg.dns.enable
     ) {
-      systemd.services.kubernetes-setup = {
-        description = "Kubernetes setup.";
-        serviceConfig.Type = "oneshot";
-        script = ''
-          mkdir -p /var/run/kubernetes
-          chown kubernetes /var/lib/kubernetes
-
-          rm ${cfg.dataDir}/.dockercfg || true
-          ln -fs ${pkgs.writeText "kubernetes-dockercfg" cfg.dockerCfg} ${cfg.dataDir}/.dockercfg
-        '';
-      };
-
-      services.kubernetes.package = mkDefault pkgs.kubernetes;
+      systemd.tmpfiles.rules = [
+        "d /opt/cni/bin 0755 root root -"
+        "d /var/run/kubernetes 0755 kubernetes kubernetes -"
+        "d /var/lib/kubernetes 0755 kubernetes kubernetes -"
+      ];
 
       environment.systemPackages = [ cfg.package ];
-
       users.extraUsers = singleton {
         name = "kubernetes";
         uid = config.ids.uids.kubernetes;
@@ -630,6 +851,5 @@ in {
       };
       users.extraGroups.kubernetes.gid = config.ids.gids.kubernetes;
     })
-
   ];
 }
diff --git a/nixos/modules/services/cluster/panamax.nix b/nixos/modules/services/cluster/panamax.nix
index b47ff744fc2..4475e8d8c24 100644
--- a/nixos/modules/services/cluster/panamax.nix
+++ b/nixos/modules/services/cluster/panamax.nix
@@ -46,7 +46,7 @@ in {
 
     fleetctlEndpoint = mkOption {
       type = types.str;
-      default = "http://127.0.0.1:4001";
+      default = "http://127.0.0.1:2379";
       description = ''
         Panamax fleetctl endpoint.
       '';
diff --git a/nixos/modules/services/computing/boinc/client.nix b/nixos/modules/services/computing/boinc/client.nix
new file mode 100644
index 00000000000..91bd463732d
--- /dev/null
+++ b/nixos/modules/services/computing/boinc/client.nix
@@ -0,0 +1,88 @@
+{config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.boinc;
+  allowRemoteGuiRpcFlag = optionalString cfg.allowRemoteGuiRpc "--allow_remote_gui_rpc";
+
+in
+  {
+    options.services.boinc = {
+      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
+          background service. The boinccmd command can be used to control the
+          daemon.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.boinc;
+        defaultText = "pkgs.boinc";
+        description = ''
+          Which BOINC package to use.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/boinc";
+        description = ''
+          The directory in which to store BOINC's configuration and data files.
+        '';
+      };
+
+      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,
+          only the hosts listed in <varname>dataDir</varname>/remote_hosts.cfg will be allowed to
+          connect.
+
+          See also: <link xlink:href="http://boinc.berkeley.edu/wiki/Controlling_BOINC_remotely#Remote_access"/>
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = [cfg.package];
+
+      users.users.boinc = {
+        createHome = false;
+        description = "BOINC Client";
+        home = cfg.dataDir;
+        isSystemUser = true;
+      };
+
+      systemd.services.boinc = {
+        description = "BOINC Client";
+        after = ["network.target" "local-fs.target"];
+        wantedBy = ["multi-user.target"];
+        preStart = ''
+          mkdir -p ${cfg.dataDir}
+          chown boinc ${cfg.dataDir}
+        '';
+        script = ''
+          ${cfg.package}/bin/boinc_client --dir ${cfg.dataDir} --redirectio ${allowRemoteGuiRpcFlag}
+        '';
+        serviceConfig = {
+          PermissionsStartOnly = true; # preStart must be run as root
+          User = "boinc";
+          Nice = 10;
+        };
+      };
+    };
+
+    meta = {
+      maintainers = with lib.maintainers; [kierdavis];
+    };
+  }
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
new file mode 100644
index 00000000000..a40be4f546e
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -0,0 +1,250 @@
+# NixOS module for Buildbot continous integration server.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.buildbot-master;
+  escapeStr = s: escape ["'"] s;
+  masterCfg = pkgs.writeText "master.cfg" ''
+    from buildbot.plugins import *
+    factory = util.BuildFactory()
+    c = BuildmasterConfig = dict(
+     workers       = [${concatStringsSep "," cfg.workers}],
+     protocols     = { 'pb': {'port': ${cfg.bpPort} } },
+     title         = '${escapeStr cfg.title}',
+     titleURL      = '${escapeStr cfg.titleUrl}',
+     buildbotURL   = '${escapeStr cfg.buildbotUrl}',
+     db            = dict(db_url='${escapeStr cfg.dbUrl}'),
+     www           = dict(port=${toString cfg.port}),
+     change_source = [ ${concatStringsSep "," cfg.changeSource} ],
+     schedulers    = [ ${concatStringsSep "," cfg.schedulers} ],
+     builders      = [ ${concatStringsSep "," cfg.builders} ],
+     status        = [ ${concatStringsSep "," cfg.status} ],
+    )
+    for step in [ ${concatStringsSep "," cfg.factorySteps} ]:
+      factory.addStep(step)
+
+    ${cfg.extraConfig}
+  '';
+
+  configFile = if cfg.masterCfg == null then masterCfg else cfg.masterCfg;
+
+in {
+  options = {
+    services.buildbot-master = {
+
+      factorySteps = mkOption {
+        type = types.listOf types.str;
+        description = "Factory Steps";
+        default = [];
+        example = [
+          "steps.Git(repourl='git://github.com/buildbot/pyflakes.git', mode='incremental')"
+          "steps.ShellCommand(command=['trial', 'pyflakes'])"
+        ];
+      };
+
+      changeSource = mkOption {
+        type = types.listOf types.str;
+        description = "List of Change Sources.";
+        default = [];
+        example = [
+          "changes.GitPoller('git://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)"
+        ];
+      };
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Buildbot continuous integration server.";
+      };
+
+      extraConfig = mkOption {
+        type = types.str;
+        description = "Extra configuration to append to master.cfg";
+        default = "";
+      };
+
+      masterCfg = mkOption {
+        type = with types; nullOr path;
+        description = ''
+          Optionally pass path to raw master.cfg file.
+          Other options in this configuration will be ignored.
+        '';
+        default = null;
+        example = literalExample ''
+          pkgs.writeText "master.cfg" "BuildmasterConfig = c = {}"
+        '';
+      };
+
+      schedulers = mkOption {
+        type = types.listOf types.str;
+        description = "List of Schedulers.";
+        default = [
+          "schedulers.SingleBranchScheduler(name='all', change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=['runtests'])"
+          "schedulers.ForceScheduler(name='force',builderNames=['runtests'])"
+        ];
+      };
+
+      builders = mkOption {
+        type = types.listOf types.str;
+        description = "List of Builders.";
+        default = [
+          "util.BuilderConfig(name='runtests',workernames=['default-worker'],factory=factory)"
+        ];
+      };
+
+      workers = mkOption {
+        type = types.listOf types.str;
+        description = "List of Workers.";
+        default = [
+          "worker.Worker('default-worker', 'password')"
+        ];
+        example = [ "worker.LocalWorker('default-worker')" ];
+      };
+
+      status = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        description = "List of status notification endpoints.";
+      };
+
+      user = mkOption {
+        default = "buildbot";
+        type = types.str;
+        description = "User the buildbot server should execute under.";
+      };
+
+      group = mkOption {
+        default = "buildbot";
+        type = types.str;
+        description = "Primary group of buildbot user.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [ "nixbld" ];
+        description = "List of extra groups that the buildbot user should be a part of.";
+      };
+
+      home = mkOption {
+        default = "/home/buildbot";
+        type = types.path;
+        description = "Buildbot home directory.";
+      };
+
+      buildbotDir = mkOption {
+        default = "${cfg.home}/master";
+        type = types.path;
+        description = "Specifies the Buildbot directory.";
+      };
+
+      bpPort = mkOption {
+        default = "9989";
+        type = types.string;
+        example = "tcp:10000:interface=127.0.0.1";
+        description = "Port where the master will listen to Buildbot Worker.";
+      };
+
+      listenAddress = mkOption {
+        default = "0.0.0.0";
+        type = types.str;
+        description = "Specifies the bind address on which the buildbot HTTP interface listens.";
+      };
+
+      buildbotUrl = mkOption {
+        default = "http://localhost:8010/";
+        type = types.str;
+        description = "Specifies the Buildbot URL.";
+      };
+
+      title = mkOption {
+        default = "Buildbot";
+        type = types.str;
+        description = "Specifies the Buildbot Title.";
+      };
+
+      titleUrl = mkOption {
+        default = "Buildbot";
+        type = types.str;
+        description = "Specifies the Buildbot TitleURL.";
+      };
+
+      dbUrl = mkOption {
+        default = "sqlite:///state.sqlite";
+        type = types.str;
+        description = "Specifies the database connection string.";
+      };
+
+      port = mkOption {
+        default = 8010;
+        type = types.int;
+        description = "Specifies port number on which the buildbot HTTP interface listens.";
+      };
+
+      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;
+      };
+
+      packages = mkOption {
+        default = [ ];
+        example = [ 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 == "buildbot") {
+      name = "buildbot";
+    };
+
+    users.extraUsers = optional (cfg.user == "buildbot") {
+      name = "buildbot";
+      description = "buildbot user";
+      isNormalUser = true;
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      extraGroups = cfg.extraGroups;
+      useDefaultShell = true;
+    };
+
+    systemd.services.buildbot-master = {
+      description = "Buildbot Continuous Integration Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = cfg.packages;
+
+      serviceConfig = {
+        Type = "forking";
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = cfg.home;
+        ExecStart = "${cfg.package}/bin/buildbot start ${cfg.buildbotDir}";
+      };
+
+      preStart = ''
+        mkdir -vp ${cfg.buildbotDir}
+        chown -c ${cfg.user}:${cfg.group} ${cfg.buildbotDir}
+        ln -sf ${configFile} ${cfg.buildbotDir}/master.cfg
+        ${cfg.package}/bin/buildbot create-master ${cfg.buildbotDir}
+      '';
+
+      postStart = ''
+        until [[ $(${pkgs.curl}/bin/curl -s --head -w '\n%{http_code}' http://localhost:${toString cfg.port} | tail -n1) =~ ^(200|403)$ ]]; do
+          sleep 1
+        done
+      '';
+    };
+  };
+
+}
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
new file mode 100644
index 00000000000..1fe4d28f9f3
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gitlab-runner;
+  configFile = pkgs.writeText "config.toml" cfg.configText;
+in
+{
+  options.services.gitlab-runner = {
+    enable = mkEnableOption "Gitlab Runner";
+
+    configText = mkOption {
+      description = "Verbatim config.toml to use";
+    };
+
+    workDir = mkOption {
+      default = "/var/lib/gitlab-runner";
+      type = types.path;
+      description = "The working directory used";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.gitlab-runner = {
+      description = "Gitlab Runner";
+      after = [ "network.target" "docker.service" ];
+      requires = [ "docker.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''${pkgs.gitlab-runner.bin}/bin/gitlab-runner run \
+          --working-directory ${cfg.workDir} \
+          --config ${configFile} \
+          --service gitlab-runner \
+          --user gitlab-runner \
+        '';
+      };
+    };
+
+    users.extraUsers.gitlab-runner = {
+      group = "gitlab-runner";
+      extraGroups = [ "docker" ];
+      uid = config.ids.uids.gitlab-runner;
+      home = cfg.workDir;
+      createHome = true;
+    };
+
+    users.extraGroups.gitlab-runner.gid = config.ids.gids.gitlab-runner;
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/gocd-agent/default.nix b/nixos/modules/services/continuous-integration/gocd-agent/default.nix
index 21f319f7fcf..05adb18fbe9 100644
--- a/nixos/modules/services/continuous-integration/gocd-agent/default.nix
+++ b/nixos/modules/services/continuous-integration/gocd-agent/default.nix
@@ -37,6 +37,7 @@ in {
 
       packages = mkOption {
         default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ];
+        defaultText = "[ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]";
         type = types.listOf types.package;
         description = ''
           Packages to add to PATH for the Go.CD agent process.
@@ -98,7 +99,7 @@ in {
         ];
         description = ''
           Specifies startup command line arguments to pass to Go.CD agent
-          java process.  Example contains debug and gcLog arguments.
+          java process.
         '';
       };
 
diff --git a/nixos/modules/services/continuous-integration/gocd-server/default.nix b/nixos/modules/services/continuous-integration/gocd-server/default.nix
index 2d198630121..07e00f17f1e 100644
--- a/nixos/modules/services/continuous-integration/gocd-server/default.nix
+++ b/nixos/modules/services/continuous-integration/gocd-server/default.nix
@@ -68,6 +68,7 @@ in {
 
       packages = mkOption {
         default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ];
+        defaultText = "[ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ]";
         type = types.listOf types.package;
         description = ''
           Packages to add to PATH for the Go.CD server's process.
@@ -90,7 +91,7 @@ in {
         '';
       };
 
-      extraOptions = mkOption {
+      startupOptions = mkOption {
         default = [
           "-Xms${cfg.initialJavaHeapSize}"
           "-Xmx${cfg.maxJavaHeapMemory}"
@@ -103,6 +104,15 @@ in {
           "-Dcruise.server.port=${toString cfg.port}"
           "-Dcruise.server.ssl.port=${toString cfg.sslPort}"
         ];
+
+        description = ''
+          Specifies startup command line arguments to pass to Go.CD server
+          java process.
+        '';
+      };
+
+      extraOptions = mkOption {
+        default = [ ];
         example = [ 
           "-X debug" 
           "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
@@ -169,7 +179,8 @@ in {
 
       script = ''
         ${pkgs.git}/bin/git config --global --add http.sslCAinfo /etc/ssl/certs/ca-certificates.crt
-        ${pkgs.jre}/bin/java -server ${concatStringsSep " " cfg.extraOptions} \
+        ${pkgs.jre}/bin/java -server ${concatStringsSep " " cfg.startupOptions} \
+                               ${concatStringsSep " " cfg.extraOptions}  \
                               -jar ${pkgs.gocd-server}/go-server/go.jar
       '';
 
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index b1b3404add0..fa550f68b33 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -343,7 +343,7 @@ in
       { wantedBy = [ "multi-user.target" ];
         requires = [ "hydra-init.service" ];
         after = [ "hydra-init.service" "network.target" ];
-        path = [ pkgs.nettools ];
+        path = [ cfg.package pkgs.nettools ];
         environment = env;
         serviceConfig =
           { ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator";
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
index 5c55ef406d5..906cb320df9 100644
--- a/nixos/modules/services/databases/4store-endpoint.nix
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -61,7 +61,9 @@ with lib;
     services.avahi.enable = true;
 
     systemd.services."4store-endpoint" = {
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
       script = ''
         ${run} '${pkgs.rdf4store}/bin/4s-httpd -D ${cfg.options} ${if cfg.listenAddress!=null then "-H ${cfg.listenAddress}" else "" } -p ${toString cfg.port} ${cfg.database}'
       '';
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
index 33e731e9681..62856822f90 100644
--- a/nixos/modules/services/databases/4store.nix
+++ b/nixos/modules/services/databases/4store.nix
@@ -53,7 +53,8 @@ with lib;
     services.avahi.enable = true;
 
     systemd.services."4store" = {
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
 
       preStart = ''
         mkdir -p ${stateDir}/
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index c98af617587..b43b448ed7e 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -377,7 +377,7 @@ in {
     systemd.services.cassandra = {
       description = "Cassandra Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       environment = cassandraEnvironment;
       restartTriggers = [ cassandraConfFile cassandraLogFile cassandraRackFile ];
       serviceConfig = {
diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix
index ae0589b399e..d4d231456c5 100644
--- a/nixos/modules/services/databases/couchdb.nix
+++ b/nixos/modules/services/databases/couchdb.nix
@@ -162,7 +162,7 @@ in {
 
         if [ "$(id -u)" = 0 ]; then
           chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`;
-          (-f ${cfg.uriFile} && chown ${cfg.user}:${cfg.group} ${cfg.uriFile}) || true
+          (test -f ${cfg.uriFile} && chown ${cfg.user}:${cfg.group} ${cfg.uriFile}) || true
           chown ${cfg.user}:${cfg.group} ${cfg.databaseDir}
           chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir}
           chown ${cfg.user}:${cfg.group} ${cfg.configFile}
diff --git a/nixos/modules/services/databases/influxdb.nix b/nixos/modules/services/databases/influxdb.nix
index e2268bd556e..dd88624f406 100644
--- a/nixos/modules/services/databases/influxdb.nix
+++ b/nixos/modules/services/databases/influxdb.nix
@@ -66,16 +66,16 @@ let
       enabled = false;
     }];
 
-    collectd = {
+    collectd = [{
       enabled = false;
       typesdb = "${pkgs.collectd}/share/collectd/types.db";
       database = "collectd_db";
       port = 25826;
-    };
+    }];
 
-    opentsdb = {
+    opentsdb = [{
       enabled = false;
-    };
+    }];
 
     continuous_queries = {
       enabled = true;
@@ -160,7 +160,7 @@ in
     systemd.services.influxdb = {
       description = "InfluxDB Server";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       serviceConfig = {
         ExecStart = ''${cfg.package}/bin/influxd -config "${configFile}"'';
         User = "${cfg.user}";
@@ -171,6 +171,11 @@ in
         mkdir -m 0770 -p ${cfg.dataDir}
         if [ "$(id -u)" = 0 ]; then chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}; fi
       '';
+      postStart = mkBefore ''
+        until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://127.0.0.1${toString configOptions.http.bind-address}'/ping; do
+          sleep 1;
+        done
+      '';
     };
 
     users.extraUsers = optional (cfg.user == "influxdb") {
diff --git a/nixos/modules/services/databases/monetdb.nix b/nixos/modules/services/databases/monetdb.nix
deleted file mode 100644
index 9f09c71e005..00000000000
--- a/nixos/modules/services/databases/monetdb.nix
+++ /dev/null
@@ -1,88 +0,0 @@
-{ config, lib, pkgs, ... }:
-let
-  cfg = config.services.monetdb;
-  monetdbUser = "monetdb";
-in
-with lib;
-{
-
-  ###### interface
-
-  options = {
-
-    services.monetdb = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable MonetDB database server.";
-      };
-
-      package = mkOption {
-        type = types.path;
-        description = "MonetDB package to use.";
-      };
-
-      dbfarmDir = mkOption {
-        type = types.path;
-        default = "/var/lib/monetdb";
-        description = ''
-          Specifies location of Monetdb dbfarm (keeps database and auxiliary files).
-        '';
-      };
-
-      port = mkOption {
-        default = "50000";
-        example = "50000";
-        description = "Port to listen on.";
-      };
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    users.extraUsers.monetdb = 
-      { name = monetdbUser;
-        uid = config.ids.uids.monetdb;
-        description = "monetdb user";
-        home = cfg.dbfarmDir;
-      };
-
-    users.extraGroups.monetdb.gid = config.ids.gids.monetdb;
-
-    environment.systemPackages = [ cfg.package ];
-
-    systemd.services.monetdb =
-      { description = "MonetDB Server";
-
-        wantedBy = [ "multi-user.target" ];
-
-        after = [ "network.target" ];
-
-        path = [ cfg.package ];
-
-        preStart =
-          ''
-            # Initialise the database.
-            if ! test -e ${cfg.dbfarmDir}/.merovingian_properties; then
-                mkdir -m 0700 -p ${cfg.dbfarmDir}
-                chown -R ${monetdbUser} ${cfg.dbfarmDir}
-                ${cfg.package}/bin/monetdbd create ${cfg.dbfarmDir}
-                ${cfg.package}/bin/monetdbd set port=${cfg.port} ${cfg.dbfarmDir}
-            fi
-          '';
-
-        serviceConfig.ExecStart = "${cfg.package}/bin/monetdbd start -n ${cfg.dbfarmDir}";
-
-        serviceConfig.ExecStop = "${cfg.package}/bin/monetdbd stop ${cfg.dbfarmDir}";
-
-        unitConfig.RequiresMountsFor = "${cfg.dbfarmDir}";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
index ef9bc46e4a0..38e46a0c6ef 100644
--- a/nixos/modules/services/databases/mongodb.nix
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -12,13 +12,11 @@ let
 
   mongoCnf = pkgs.writeText "mongodb.conf"
   ''
-    bind_ip = ${cfg.bind_ip}
-    ${optionalString cfg.quiet "quiet = true"}
-    dbpath = ${cfg.dbpath}
-    syslog = true
-    fork = true
-    pidfilepath = ${cfg.pidFile}
-    ${optionalString (cfg.replSetName != "") "replSet = ${cfg.replSetName}"}
+    net.bindIp: ${cfg.bind_ip}
+    ${optionalString cfg.quiet "systemLog.quiet: true"}
+    systemLog.destination: syslog
+    storage.dbPath: ${cfg.dbpath}
+    ${optionalString (cfg.replSetName != "") "replication.replSetName: ${cfg.replSetName}"}
     ${cfg.extraConfig}
   '';
 
@@ -84,9 +82,9 @@ in
       extraConfig = mkOption {
         default = "";
         example = ''
-          nojournal = true
+          storage.journal.enabled: false
         '';
-        description = "MongoDB extra configuration";
+        description = "MongoDB extra configuration in YAML format";
       };
     };
 
@@ -112,7 +110,7 @@ in
         after = [ "network.target" ];
 
         serviceConfig = {
-          ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf}";
+          ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf} --fork --pidfilepath ${cfg.pidFile}";
           User = cfg.user;
           PIDFile = cfg.pidFile;
           Type = "forking";
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 0b2f99f8fff..fcf1f123cfb 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -43,6 +43,7 @@ in
     services.mysql = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = "
           Whether to enable the MySQL server.
@@ -58,16 +59,19 @@ in
       };
 
       port = mkOption {
-        default = "3306";
+        type = types.int;
+        default = 3306;
         description = "Port of MySQL";
       };
 
       user = mkOption {
+        type = types.str;
         default = "mysql";
         description = "User account under which MySQL runs";
       };
 
       dataDir = mkOption {
+        type = types.path;
         default = "/var/mysql"; # !!! should be /var/db/mysql
         description = "Location where MySQL stores its table files";
       };
@@ -78,6 +82,7 @@ in
       };
 
       extraOptions = mkOption {
+        type = types.lines;
         default = "";
         example = ''
           key_buffer_size = 6G
@@ -115,32 +120,39 @@ in
 
       replication = {
         role = mkOption {
+          type = types.enum [ "master" "slave" "none" ];
           default = "none";
-          description = "Role of the MySQL server instance. Can be either: master, slave or none";
+          description = "Role of the MySQL server instance.";
         };
 
         serverId = mkOption {
+          type = types.int;
           default = 1;
           description = "Id of the MySQL server instance. This number must be unique for each instance";
         };
 
         masterHost = mkOption {
+          type = types.str;
           description = "Hostname of the MySQL master server";
         };
 
         slaveHost = mkOption {
+          type = types.str;
           description = "Hostname of the MySQL slave server";
         };
 
         masterUser = mkOption {
+          type = types.str;
           description = "Username of the MySQL replication user";
         };
 
         masterPassword = mkOption {
+          type = types.str;
           description = "Password of the MySQL replication user";
         };
 
         masterPort = mkOption {
+          type = types.int;
           default = 3306;
           description = "Port number on which the MySQL master server runs";
         };
@@ -167,6 +179,7 @@ in
     systemd.services.mysql =
       { description = "MySQL Server";
 
+        after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
 
         unitConfig.RequiresMountsFor = "${cfg.dataDir}";
diff --git a/nixos/modules/services/databases/neo4j.nix b/nixos/modules/services/databases/neo4j.nix
index 41b96068590..7b51f1af689 100644
--- a/nixos/modules/services/databases/neo4j.nix
+++ b/nixos/modules/services/databases/neo4j.nix
@@ -5,34 +5,34 @@ with lib;
 let
   cfg = config.services.neo4j;
 
-  serverConfig = pkgs.writeText "neo4j-server.properties" ''
-    org.neo4j.server.database.location=${cfg.dataDir}/data/graph.db
-    org.neo4j.server.webserver.address=${cfg.listenAddress}
-    org.neo4j.server.webserver.port=${toString cfg.port}
+  serverConfig = pkgs.writeText "neo4j.conf" ''
+    dbms.directories.data=${cfg.dataDir}/data
+    dbms.directories.certificates=${cfg.certDir}
+    dbms.directories.logs=${cfg.dataDir}/logs
+    dbms.directories.plugins=${cfg.dataDir}/plugins
+    dbms.connector.http.type=HTTP
+    dbms.connector.http.enabled=true
+    dbms.connector.http.address=${cfg.listenAddress}:${toString cfg.port}
+    ${optionalString cfg.enableBolt ''
+      dbms.connector.bolt.type=BOLT
+      dbms.connector.bolt.enabled=true
+      dbms.connector.bolt.tls_level=OPTIONAL
+      dbms.connector.bolt.address=${cfg.listenAddress}:${toString cfg.boltPort}
+    ''}
     ${optionalString cfg.enableHttps ''
-      org.neo4j.server.webserver.https.enabled=true
-      org.neo4j.server.webserver.https.port=${toString cfg.httpsPort}
-      org.neo4j.server.webserver.https.cert.location=${cfg.cert}
-      org.neo4j.server.webserver.https.key.location=${cfg.key}
-      org.neo4j.server.webserver.https.keystore.location=${cfg.dataDir}/data/keystore
+      dbms.connector.https.type=HTTP
+      dbms.connector.https.enabled=true
+      dbms.connector.https.encryption=TLS
+      dbms.connector.https.address=${cfg.listenAddress}:${toString cfg.httpsPort}
     ''}
-    org.neo4j.server.webadmin.rrdb.location=${cfg.dataDir}/data/rrd
-    org.neo4j.server.webadmin.data.uri=/db/data/
-    org.neo4j.server.webadmin.management.uri=/db/manage/
-    org.neo4j.server.db.tuning.properties=${cfg.package}/share/neo4j/conf/neo4j.properties
-    org.neo4j.server.manage.console_engines=shell
+    dbms.shell.enabled=true
     ${cfg.extraServerConfig}
   '';
 
-  loggingConfig = pkgs.writeText "logging.properties" cfg.loggingConfig;
-
   wrapperConfig = pkgs.writeText "neo4j-wrapper.conf" ''
-    wrapper.java.additional=-Dorg.neo4j.server.properties=${serverConfig}
-    wrapper.java.additional=-Djava.util.logging.config.file=${loggingConfig}
-    wrapper.java.additional=-XX:+UseConcMarkSweepGC
-    wrapper.java.additional=-XX:+CMSClassUnloadingEnabled
-    wrapper.pidfile=${cfg.dataDir}/neo4j-server.pid
-    wrapper.name=neo4j
+    dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
+    dbms.jvm.additional=-XX:+UseConcMarkSweepGC
+    dbms.jvm.additional=-XX:+CMSClassUnloadingEnabled
   '';
 
 in {
@@ -65,6 +65,18 @@ in {
       type = types.int;
     };
 
+    enableBolt = mkOption {
+      description = "Enable bolt for Neo4j.";
+      default = true;
+      type = types.bool;
+    };
+
+    boltPort = mkOption {
+      description = "Neo4j port to listen for BOLT traffic.";
+      default = 7687;
+      type = types.int;
+    };
+
     enableHttps = mkOption {
       description = "Enable https for Neo4j.";
       default = false;
@@ -77,15 +89,9 @@ in {
       type = types.int;
     };
 
-    cert = mkOption {
-      description = "Neo4j https certificate.";
-      default = "${cfg.dataDir}/conf/ssl/neo4j.cert";
-      type = types.path;
-    };
-
-    key = mkOption {
-      description = "Neo4j https certificate key.";
-      default = "${cfg.dataDir}/conf/ssl/neo4j.key";
+    certDir = mkOption {
+      description = "Neo4j TLS certificates directory.";
+      default = "${cfg.dataDir}/certificates";
       type = types.path;
     };
 
@@ -95,26 +101,11 @@ in {
       type = types.path;
     };
 
-    loggingConfig = mkOption {
-      description = "Neo4j logging configuration.";
-      default = ''
-        handlers=java.util.logging.ConsoleHandler
-        .level=INFO
-        org.neo4j.server.level=INFO
-
-        java.util.logging.ConsoleHandler.level=INFO
-        java.util.logging.ConsoleHandler.formatter=org.neo4j.server.logging.SimpleConsoleFormatter
-        java.util.logging.ConsoleHandler.filter=org.neo4j.server.logging.NeoLogFilter
-      '';
-      type = types.lines;
-    };
-
     extraServerConfig = mkOption {
       description = "Extra configuration for neo4j server.";
       default = "";
       type = types.lines;
     };
-
   };
 
   ###### implementation
@@ -123,15 +114,19 @@ in {
     systemd.services.neo4j = {
       description = "Neo4j Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
-      environment = { NEO4J_INSTANCE = cfg.dataDir; };
+      after = [ "network.target" ];
+      environment = {
+        NEO4J_HOME = "${cfg.package}/share/neo4j";
+        NEO4J_CONF = "${cfg.dataDir}/conf";
+      };
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/neo4j console";
         User = "neo4j";
         PermissionsStartOnly = true;
       };
       preStart = ''
-        mkdir -m 0700 -p ${cfg.dataDir}/{data/graph.db,conf}
+        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
       '';
@@ -146,5 +141,4 @@ in {
       home = cfg.dataDir;
     };
   };
-
 }
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index 9f22aa7c92b..b8e6c0cec3d 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -53,6 +53,13 @@ in
         description = "The database directory.";
       };
 
+      configDir = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = "Use this optional config directory instead of using slapd.conf";
+        example = "/var/db/slapd.d";
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -96,7 +103,7 @@ in
         mkdir -p ${cfg.dataDir}
         chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
       '';
-      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" -f ${configFile}";
+      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" ${if cfg.configDir == null then "-f "+configFile else "-F "+cfg.configDir}";
     };
 
     users.extraUsers.openldap =
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 9988fc6e63b..24ef4637ec9 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -11,12 +11,14 @@ let
     if cfg.extraPlugins == [] then pg
     else pkgs.buildEnv {
       name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
-      paths = [ pg ] ++ cfg.extraPlugins;
+      paths = [ pg pg.lib ] ++ cfg.extraPlugins;
+      buildInputs = [ pkgs.makeWrapper ];
       postBuild =
         ''
           mkdir -p $out/bin
           rm $out/bin/{pg_config,postgres,pg_ctl}
           cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
+          wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
         '';
     };
 
diff --git a/nixos/modules/services/databases/riak-cs.nix b/nixos/modules/services/databases/riak-cs.nix
new file mode 100644
index 00000000000..198efc29222
--- /dev/null
+++ b/nixos/modules/services/databases/riak-cs.nix
@@ -0,0 +1,202 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.riak-cs;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.riak-cs = {
+
+      enable = mkEnableOption "riak-cs";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.riak-cs;
+        defaultText = "pkgs.riak-cs";
+        example = literalExample "pkgs.riak-cs";
+        description = ''
+          Riak package to use.
+        '';
+      };
+
+      nodeName = mkOption {
+        type = types.str;
+        default = "riak-cs@127.0.0.1";
+        description = ''
+          Name of the Erlang node.
+        '';
+      };
+      
+      anonymousUserCreation = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Anonymous user creation.
+        '';
+      };
+
+      riakHost = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8087";
+        description = ''
+          Name of riak hosting service.
+        '';
+      };
+
+      listener = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8080";
+        description = ''
+          Name of Riak CS listening service.
+        '';
+      };
+
+      stanchionHost = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8085";
+        description = ''
+          Name of stanchion hosting service.
+        '';
+      };
+
+      stanchionSsl = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Tell stanchion to use SSL.
+        '';
+      };
+
+      distributedCookie = mkOption {
+        type = types.str;
+        default = "riak";
+        description = ''
+          Cookie for distributed node communication.  All nodes in the
+          same cluster should use the same cookie or they will not be able to
+          communicate.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/db/riak-cs";
+        description = ''
+          Data directory for Riak CS.
+        '';
+      };
+
+      logDir = mkOption {
+        type = types.path;
+        default = "/var/log/riak-cs";
+        description = ''
+          Log directory for Riak CS.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be appended to <filename>riak-cs.conf</filename>.
+        '';
+      };
+
+      extraAdvancedConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be appended to <filename>advanced.config</filename>.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+    environment.etc."riak-cs/riak-cs.conf".text = ''
+      nodename = ${cfg.nodeName}
+      distributed_cookie = ${cfg.distributedCookie}
+
+      platform_log_dir = ${cfg.logDir}
+
+      riak_host = ${cfg.riakHost}
+      listener = ${cfg.listener}
+      stanchion_host = ${cfg.stanchionHost}
+
+      anonymous_user_creation = ${if cfg.anonymousUserCreation then "on" else "off"}
+
+      ${cfg.extraConfig}
+    '';
+
+    environment.etc."riak-cs/advanced.config".text = ''
+      ${cfg.extraAdvancedConfig}
+    '';
+
+    users.extraUsers.riak-cs = {
+      name = "riak-cs";
+      uid = config.ids.uids.riak-cs;
+      group = "riak";
+      description = "Riak CS server user";
+    };
+
+  systemd.services.riak-cs = {
+      description = "Riak CS Server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      path = [
+        pkgs.utillinux # for `logger`
+        pkgs.bash
+      ];
+
+      environment.HOME = "${cfg.dataDir}";
+      environment.RIAK_CS_DATA_DIR = "${cfg.dataDir}";
+      environment.RIAK_CS_LOG_DIR = "${cfg.logDir}";
+      environment.RIAK_CS_ETC_DIR = "/etc/riak";
+
+      preStart = ''
+        if ! test -e ${cfg.logDir}; then
+          mkdir -m 0755 -p ${cfg.logDir}
+          chown -R riak-cs ${cfg.logDir}
+        fi
+
+        if ! test -e ${cfg.dataDir}; then
+          mkdir -m 0700 -p ${cfg.dataDir}
+          chown -R riak-cs ${cfg.dataDir}
+        fi
+      '';
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/riak-cs console";
+        ExecStop = "${cfg.package}/bin/riak-cs stop";
+        StandardInput = "tty";
+        User = "riak-cs";
+        Group = "riak-cs";
+        PermissionsStartOnly = true;
+        # Give Riak a decent amount of time to clean up.
+        TimeoutStopSec = 120;
+        LimitNOFILE = 65536;
+      };
+
+      unitConfig.RequiresMountsFor = [
+        "${cfg.dataDir}"
+        "${cfg.logDir}"
+        "/etc/riak"
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/riak.nix b/nixos/modules/services/databases/riak.nix
index bee768fa42a..e0ebf164aef 100644
--- a/nixos/modules/services/databases/riak.nix
+++ b/nixos/modules/services/databases/riak.nix
@@ -20,7 +20,9 @@ in
 
       package = mkOption {
         type = types.package;
-        example = literalExample "pkgs.riak2";
+        default = pkgs.riak;
+        defaultText = "pkgs.riak";
+        example = literalExample "pkgs.riak";
         description = ''
           Riak package to use.
         '';
@@ -68,6 +70,14 @@ in
         '';
       };
 
+      extraAdvancedConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be appended to <filename>advanced.config</filename>.
+        '';
+      };
+
     };
 
   };
@@ -88,6 +98,10 @@ in
       ${cfg.extraConfig}
     '';
 
+    environment.etc."riak/advanced.config".text = ''
+      ${cfg.extraAdvancedConfig}
+    '';
+
     users.extraUsers.riak = {
       name = "riak";
       uid = config.ids.uids.riak;
@@ -108,6 +122,7 @@ in
         pkgs.bash
       ];
 
+      environment.HOME = "${cfg.dataDir}";
       environment.RIAK_DATA_DIR = "${cfg.dataDir}";
       environment.RIAK_LOG_DIR = "${cfg.logDir}";
       environment.RIAK_ETC_DIR = "/etc/riak";
diff --git a/nixos/modules/services/databases/stanchion.nix b/nixos/modules/services/databases/stanchion.nix
new file mode 100644
index 00000000000..f2dbb78b5c4
--- /dev/null
+++ b/nixos/modules/services/databases/stanchion.nix
@@ -0,0 +1,212 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.stanchion;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.stanchion = {
+
+      enable = mkEnableOption "stanchion";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.stanchion;
+        defaultText = "pkgs.stanchion";
+        example = literalExample "pkgs.stanchion";
+        description = ''
+          Stanchion package to use.
+        '';
+      };
+
+      nodeName = mkOption {
+        type = types.str;
+        default = "stanchion@127.0.0.1";
+        description = ''
+          Name of the Erlang node.
+        '';
+      };
+
+      adminKey = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Name of admin user.
+        '';
+      };
+
+      adminSecret = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Name of admin secret
+        '';
+      };
+
+      riakHost = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8087";
+        description = ''
+          Name of riak hosting service.
+        '';
+      };
+
+      listener = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8085";
+        description = ''
+          Name of Riak CS listening service.
+        '';
+      };
+
+      stanchionHost = mkOption {
+        type = types.str;
+        default = "127.0.0.1:8085";
+        description = ''
+          Name of stanchion hosting service.
+        '';
+      };
+
+      stanchionSsl = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Tell stanchion to use SSL.
+        '';
+      };
+
+      distributedCookie = mkOption {
+        type = types.str;
+        default = "riak";
+        description = ''
+          Cookie for distributed node communication.  All nodes in the
+          same cluster should use the same cookie or they will not be able to
+          communicate.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/db/stanchion";
+        description = ''
+          Data directory for Stanchion.
+        '';
+      };
+
+      logDir = mkOption {
+        type = types.path;
+        default = "/var/log/stanchion";
+        description = ''
+          Log directory for Stanchino.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Additional text to be appended to <filename>stanchion.conf</filename>.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    environment.etc."stanchion/advanced.config".text = ''
+      [{stanchion, []}].
+    '';
+
+    environment.etc."stanchion/stanchion.conf".text = ''
+      listener = ${cfg.listener}
+
+      riak_host = ${cfg.riakHost}
+
+      ${optionalString (cfg.adminKey == "") "#"} admin.key=${optionalString (cfg.adminKey != "") cfg.adminKey}
+      ${optionalString (cfg.adminSecret == "") "#"} admin.secret=${optionalString (cfg.adminSecret != "") cfg.adminSecret}
+
+      platform_bin_dir = ${pkgs.stanchion}/bin
+      platform_data_dir = ${cfg.dataDir}
+      platform_etc_dir = /etc/stanchion
+      platform_lib_dir = ${pkgs.stanchion}/lib
+      platform_log_dir = ${cfg.logDir}
+
+      nodename = ${cfg.nodeName}
+
+      distributed_cookie = ${cfg.distributedCookie}
+
+      stanchion_ssl=${if cfg.stanchionSsl then "on" else "off"}
+
+      ${cfg.extraConfig}
+    '';
+
+    users.extraUsers.stanchion = {
+      name = "stanchion";
+      uid = config.ids.uids.stanchion;
+      group = "stanchion";
+      description = "Stanchion server user";
+    };
+
+    users.extraGroups.stanchion.gid = config.ids.gids.stanchion;
+
+    systemd.services.stanchion = {
+      description = "Stanchion Server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      path = [
+        pkgs.utillinux # for `logger`
+        pkgs.bash
+      ];
+
+      environment.HOME = "${cfg.dataDir}";
+      environment.STANCHION_DATA_DIR = "${cfg.dataDir}";
+      environment.STANCHION_LOG_DIR = "${cfg.logDir}";
+      environment.STANCHION_ETC_DIR = "/etc/stanchion";
+
+      preStart = ''
+        if ! test -e ${cfg.logDir}; then
+          mkdir -m 0755 -p ${cfg.logDir}
+          chown -R stanchion:stanchion ${cfg.logDir}
+        fi
+
+        if ! test -e ${cfg.dataDir}; then
+          mkdir -m 0700 -p ${cfg.dataDir}
+          chown -R stanchion:stanchion ${cfg.dataDir}
+        fi
+      '';
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/stanchion console";
+        ExecStop = "${cfg.package}/bin/stanchion stop";
+        StandardInput = "tty";
+        User = "stanchion";
+        Group = "stanchion";
+        PermissionsStartOnly = true;
+        # Give Stanchion a decent amount of time to clean up.
+        TimeoutStopSec = 120;
+        LimitNOFILE = 65536;
+      };
+
+      unitConfig.RequiresMountsFor = [
+        "${cfg.dataDir}"
+        "${cfg.logDir}"
+        "/etc/stanchion"
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix
index bdd210a2550..3231fede08f 100644
--- a/nixos/modules/services/databases/virtuoso.nix
+++ b/nixos/modules/services/databases/virtuoso.nix
@@ -62,7 +62,8 @@ with lib;
       };
 
     systemd.services.virtuoso = {
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
 
       preStart = ''
         mkdir -p ${stateDir}
diff --git a/nixos/modules/services/desktops/accountsservice.nix b/nixos/modules/services/desktops/accountsservice.nix
index c28c2729576..2a7450669ea 100644
--- a/nixos/modules/services/desktops/accountsservice.nix
+++ b/nixos/modules/services/desktops/accountsservice.nix
@@ -35,6 +35,14 @@ with lib;
     services.dbus.packages = [ pkgs.accountsservice ];
 
     systemd.packages = [ pkgs.accountsservice ];
+
+    systemd.services.accounts-daemon= {
+
+      wantedBy = [ "graphical.target" ];
+
+    } // (mkIf (!config.users.mutableUsers) {
+      environment.NIXOS_USERS_PURE = "true";
+    });
   };
 
 }
diff --git a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
index a8f8da0eed5..2db2e2fe1c3 100644
--- a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
+++ b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
@@ -37,6 +37,8 @@ in
 
     services.dbus.packages = [ gnome3.evolution_data_server ];
 
+    systemd.packages = [ gnome3.evolution_data_server ];
+
   };
 
 }
diff --git a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
new file mode 100644
index 00000000000..384cede679c
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
@@ -0,0 +1,44 @@
+# GNOME Documents daemon.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  gnome3 = config.environment.gnome3.packageSet;
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.gnome-terminal-server = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME Terminal server service,
+          needed for gnome-terminal.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.gnome-terminal-server.enable {
+
+    environment.systemPackages = [ gnome3.gnome_terminal ];
+
+    services.dbus.packages = [ gnome3.gnome_terminal ];
+
+    systemd.packages = [ gnome3.gnome_terminal ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/gvfs.nix b/nixos/modules/services/desktops/gnome3/gvfs.nix
index a07cdadbb12..6bbabe8d3c5 100644
--- a/nixos/modules/services/desktops/gnome3/gvfs.nix
+++ b/nixos/modules/services/desktops/gnome3/gvfs.nix
@@ -37,6 +37,8 @@ in
 
     services.dbus.packages = [ gnome3.gvfs ];
 
+    systemd.packages = [ gnome3.gvfs ];
+
     services.udev.packages = [ pkgs.libmtp.bin ];
 
   };
diff --git a/nixos/modules/services/desktops/gnome3/tracker.nix b/nixos/modules/services/desktops/gnome3/tracker.nix
index 8c5935a5ee3..dcaa60103a3 100644
--- a/nixos/modules/services/desktops/gnome3/tracker.nix
+++ b/nixos/modules/services/desktops/gnome3/tracker.nix
@@ -37,6 +37,8 @@ in
 
     services.dbus.packages = [ gnome3.tracker ];
 
+    systemd.packages = [ gnome3.tracker ];
+
   };
 
 }
diff --git a/nixos/modules/services/desktops/profile-sync-daemon.nix b/nixos/modules/services/desktops/profile-sync-daemon.nix
index d66ecef2385..e3f74df3e57 100644
--- a/nixos/modules/services/desktops/profile-sync-daemon.nix
+++ b/nixos/modules/services/desktops/profile-sync-daemon.nix
@@ -86,6 +86,12 @@ in {
   };
 
   config = mkIf cfg.enable {
+    assertions = [
+      { assertion = cfg.users != [];
+        message = "services.psd.users must contain at least one user";
+      }
+    ];
+
     systemd = {
       services = {
         psd = {
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 6795ec52fe4..08fa6de6374 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -79,9 +79,13 @@ in {
 
     environment.systemPackages = [ cfg.package editorScript ];
 
-    environment.variables = if cfg.defaultEditor then {
-      EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor";
-    } else {};
+    environment.variables = {
+      # This is required so that GTK applications launched from Emacs
+      # get properly themed:
+      GTK_DATA_PREFIX = "${config.system.path}";
+    } // (if cfg.defaultEditor then {
+        EDITOR = mkOverride 900 "${editorScript}/bin/emacseditor";
+      } else {});
   };
 
   meta.doc = ./emacs.xml;
diff --git a/nixos/modules/services/editors/emacs.xml b/nixos/modules/services/editors/emacs.xml
index ee8ef512bc7..e03f6046de8 100644
--- a/nixos/modules/services/editors/emacs.xml
+++ b/nixos/modules/services/editors/emacs.xml
@@ -43,9 +43,10 @@
     <title>Installing <application>Emacs</application></title>
 
     <para>
-      Emacs can installed in the normal way for Nix (see <xref
-      linkend="sec-package-management" />). In addition, a NixOS
-      <emphasis>service</emphasis> can be enabled.
+      Emacs can be installed in the normal way for Nix (see
+      <xref linkend="sec-package-management" />).
+      In addition, a NixOS <emphasis>service</emphasis>
+      can be enabled.
     </para>
 
     <section>
@@ -59,20 +60,20 @@
         <variablelist>
           <varlistentry>
             <term><varname>emacs</varname></term>
-            <term><varname>emacs24</varname></term>
+            <term><varname>emacs25</varname></term>
             <listitem>
               <para>
-                The latest stable version of Emacs 24 using the <link
+                The latest stable version of Emacs 25 using the <link
                 xlink:href="http://www.gtk.org">GTK+ 2</link> widget
                 toolkit.
               </para>
             </listitem>
           </varlistentry>
           <varlistentry>
-            <term><varname>emacs24-nox</varname></term>
+            <term><varname>emacs25-nox</varname></term>
             <listitem>
               <para>
-                Emacs 24 built without any dependency on X11
+                Emacs 25 built without any dependency on X11
                 libraries.
               </para>
             </listitem>
@@ -86,15 +87,6 @@
               </para>
             </listitem>
           </varlistentry>
-          <varlistentry>
-            <term><varname>emacs25pre</varname></term>
-            <listitem>
-              <para>
-                A pretest version of what will become the first
-                version of Emacs 25.
-              </para>
-            </listitem>
-          </varlistentry>
         </variablelist>
       </para>
 
@@ -364,14 +356,14 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
         <programlisting><![CDATA[
 { pkgs ? import <nixpkgs> {} }:
 let
-  myEmacs = pkgs.lib.overrideDerivation (pkgs.emacs.override {
+  myEmacs = (pkgs.emacs.override {
     # Use gtk3 instead of the default gtk2
     withGTK3 = true;
     withGTK2 = false;
-  }) (attrs: {
+  }).overrideAttrs (attrs: {
     # I don't want emacs.desktop file because I only use
     # emacsclient.
-    postInstall = attrs.postInstall + ''
+    postInstall = (attrs.postInstall or "") + ''
       rm $out/share/applications/emacs.desktop
     '';
   });
@@ -573,6 +565,55 @@ services.emacs.install = true;
       &lt;RET&gt; nixos-rebuild &lt;RET&gt;.</literal>
     </para>
   </section>
+
+  <section xml:id="sec-emacs-docbook-xml">
+    <title>Editing DocBook 5 XML Documents</title>
+    <para>
+      Emacs includes <link
+      xlink:href="https://www.gnu.org/software/emacs/manual/html_node/nxml-mode/Introduction.html">nXML</link>,
+      a major-mode for validating and editing XML documents.
+      When editing DocBook 5.0 documents, such as
+      <link linkend="book-nixos-manual">this one</link>,
+      nXML needs to be configured with the relevant schema, which is
+      not included.
+    </para>
+
+    <para>
+      To install the DocBook 5.0 schemas, either add
+      <varname>pkgs.docbook5</varname> to
+      <varname>environment.systemPackages</varname> (<link
+      linkend="sec-declarative-package-mgmt">NixOS</link>), or run
+      <literal>nix-env -i pkgs.docbook5</literal>
+      (<link linkend="sec-ad-hoc-packages">Nix</link>).
+    </para>
+
+    <para>
+      Then customize the variable <varname>rng-schema-locating-files</varname> to include <filename>~/.emacs.d/schemas.xml</filename> and put the following text into that file:
+      <example xml:id="ex-emacs-docbook-xml">
+        <title>nXML Schema Configuration (<filename>~/.emacs.d/schemas.xml</filename>)</title>
+        <programlisting language="xml"><![CDATA[
+<?xml version="1.0"?>
+<!--
+  To let emacs find this file, evaluate:
+  (add-to-list 'rng-schema-locating-files "~/.emacs.d/schemas.xml")
+-->
+<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
+  <!--
+    Use this variation if pkgs.docbook5 is added to environment.systemPackages
+  -->
+  <namespace ns="http://docbook.org/ns/docbook"
+             uri="/run/current-system/sw/share/xml/docbook-5.0/rng/docbookxi.rnc"/>
+  <!--
+    Use this variation if installing schema with "nix-env -iA pkgs.docbook5".
+  <namespace ns="http://docbook.org/ns/docbook"
+             uri="../.nix-profile/share/xml/docbook-5.0/rng/docbookxi.rnc"/>
+  -->
+</locatingRules>
+]]></programlisting>
+    </example>
+  </para>
+
+  </section>
 </section>
 
 </chapter>
diff --git a/nixos/modules/services/editors/infinoted.nix b/nixos/modules/services/editors/infinoted.nix
new file mode 100644
index 00000000000..963147b18a0
--- /dev/null
+++ b/nixos/modules/services/editors/infinoted.nix
@@ -0,0 +1,158 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.infinoted;
+in {
+  options.services.infinoted = {
+    enable = mkEnableOption "infinoted";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.libinfinity.override { daemon = true; };
+      defaultText = "pkgs.libinfinity.override { daemon = true; }";
+      description = ''
+        Package providing infinoted
+      '';
+    };
+
+    keyFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Private key to use for TLS
+      '';
+    };
+
+    certificateFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Server certificate to use for TLS
+      '';
+    };
+
+    certificateChain = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Chain of CA-certificates to which our `certificateFile` is relative.
+        Optional for TLS.
+      '';
+    };
+
+    securityPolicy = mkOption {
+      type = types.enum ["no-tls" "allow-tls" "require-tls"];
+      default = "require-tls";
+      description = ''
+        How strictly to enforce clients connection with TLS.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 6523;
+      description = ''
+        Port to listen on
+      '';
+    };
+
+    rootDirectory = mkOption {
+      type = types.path;
+      default = "/var/lib/infinoted/documents/";
+      description = ''
+        Root of the directory structure to serve
+      '';
+    };
+
+    plugins = mkOption {
+      type = types.listOf types.str;
+      default = [ "note-text" "note-chat" "logging" "autosave" ];
+      description = ''
+        Plugins to enable
+      '';
+    };
+
+    passwordFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        File to read server-wide password from
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = ''
+        [autosave]
+        interval=10
+      '';
+      description = ''
+        Additional configuration to append to infinoted.conf
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "infinoted";
+      description = ''
+        What to call the dedicated user under which infinoted is run
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "infinoted";
+      description = ''
+        What to call the primary group of the dedicated user under which infinoted is run
+      '';
+    };
+  };
+
+  config = mkIf (cfg.enable) {
+    users.extraUsers = optional (cfg.user == "infinoted")
+      { name = "infinoted";
+        description = "Infinoted user";
+        group = cfg.group;
+      };
+    users.extraGroups = optional (cfg.group == "infinoted")
+      { name = "infinoted";
+      };
+  
+    systemd.services.infinoted =
+      { description = "Gobby Dedicated Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          Type = "simple";
+          Restart = "always";
+          ExecStart = "${cfg.package}/bin/infinoted-0.6 --config-file=/var/lib/infinoted/infinoted.conf";
+          User = cfg.user;
+          Group = cfg.group;
+          PermissionsStartOnly = true;
+        };
+        preStart = ''
+          mkdir -p /var/lib/infinoted
+          install -o ${cfg.user} -g ${cfg.group} -m 0600 /dev/null /var/lib/infinoted/infinoted.conf
+          cat >>/var/lib/infinoted/infinoted.conf <<EOF
+          [infinoted]
+          ${optionalString (cfg.keyFile != null) ''key-file=${cfg.keyFile}''}
+          ${optionalString (cfg.certificateFile != null) ''certificate-file=${cfg.certificateFile}''}
+          ${optionalString (cfg.certificateChain != null) ''certificate-chain=${cfg.certificateChain}''}
+          port=${toString cfg.port}
+          security-policy=${cfg.securityPolicy}
+          root-directory=${cfg.rootDirectory}
+          plugins=${concatStringsSep ";" cfg.plugins}
+          ${optionalString (cfg.passwordFile != null) ''password=$(head -n 1 ${cfg.passwordFile})''}
+
+          ${cfg.extraConfig}
+          EOF
+
+          install -o ${cfg.user} -g ${cfg.group} -m 0750 -d ${cfg.rootDirectory}
+        '';
+      };
+  };
+}
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
index 5762148df2b..71ff6bb2f3f 100644
--- a/nixos/modules/services/games/ghost-one.nix
+++ b/nixos/modules/services/games/ghost-one.nix
@@ -21,8 +21,7 @@ in
 
       language = mkOption {
         default = "English";
-        type = types.addCheck types.str
-          (lang: elem lang [ "English" "Spanish" "Russian" "Serbian" "Turkish" ]);
+        type = types.enum [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
         description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
       };
 
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
index 57ac37bb1bd..21aff780b67 100644
--- a/nixos/modules/services/games/terraria.nix
+++ b/nixos/modules/services/games/terraria.nix
@@ -64,7 +64,7 @@ in
       };
 
       worldPath = mkOption {
-        type        = types.path;
+        type        = types.nullOr types.path;
         default     = null;
         description = ''
           The path to the world file (<literal>.wld</literal>) which should be loaded.
@@ -126,8 +126,8 @@ in
         User    = "terraria";
         Type = "oneshot";
         RemainAfterExit = true;
-        ExecStart = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
-        ExecStop = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter";
+        ExecStart = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
+        ExecStop = "${getBin pkgs.tmux}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter";
       };
 
       postStart = ''
diff --git a/nixos/modules/services/hardware/brltty.nix b/nixos/modules/services/hardware/brltty.nix
index 03e530b2c96..1266e8f81e5 100644
--- a/nixos/modules/services/hardware/brltty.nix
+++ b/nixos/modules/services/hardware/brltty.nix
@@ -28,7 +28,7 @@ in {
       };
       serviceConfig = {
         ExecStart = "${pkgs.brltty}/bin/brltty --no-daemon";
-        Type = "simple";        # Change to notidy after next releae
+        Type = "notify";
         TimeoutStartSec = 5;
         TimeoutStopSec = 10;
         Restart = "always";
@@ -39,6 +39,8 @@ in {
         ProtectSystem = "full";
         SystemCallArchitectures = "native";
       };
+      wants = [ "systemd-udev-settle.service" ];
+      after = [ "local-fs.target" "systemd-udev-settle.service" ];
       before = [ "sysinit.target" ];
       wantedBy = [ "sysinit.target" ];
     };
diff --git a/nixos/modules/services/hardware/pommed.nix b/nixos/modules/services/hardware/pommed.nix
index 7be4dc1e846..bf7d6a46a29 100644
--- a/nixos/modules/services/hardware/pommed.nix
+++ b/nixos/modules/services/hardware/pommed.nix
@@ -2,7 +2,9 @@
 
 with lib;
 
-{
+let cfg = config.services.hardware.pommed;
+    defaultConf = "${pkgs.pommed_light}/etc/pommed.conf.mactel";
+in {
 
   options = {
 
@@ -12,37 +14,37 @@ with lib;
         type = types.bool;
         default = false;
         description = ''
-          Whether to use the pommed tool to handle Apple laptop keyboard hotkeys.
+          Whether to use the pommed tool to handle Apple laptop
+          keyboard hotkeys.
         '';
       };
 
       configFile = mkOption {
-        type = types.path;
+        type = types.nullOr types.path;
+        default = null;
         description = ''
-          The path to the <filename>pommed.conf</filename> file.
+          The path to the <filename>pommed.conf</filename> file. Leave
+          to null to use the default config file
+          (<filename>/etc/pommed.conf.mactel</filename>). See the
+          files <filename>/etc/pommed.conf.mactel</filename> and
+          <filename>/etc/pommed.conf.pmac</filename> for examples to
+          build on.
         '';
       };
     };
 
   };
 
-  config = mkIf config.services.hardware.pommed.enable {
-    environment.systemPackages = [ pkgs.polkit ];
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.polkit pkgs.pommed_light ];
 
-    environment.etc."pommed.conf".source = config.services.hardware.pommed.configFile;
-
-    services.hardware.pommed.configFile = "${pkgs.pommed}/etc/pommed.conf";
-
-    services.dbus.packages = [ pkgs.pommed ];
+    environment.etc."pommed.conf".source =
+      if cfg.configFile == null then defaultConf else cfg.configFile;
 
     systemd.services.pommed = {
-      description = "Pommed hotkey management";
+      description = "Pommed Apple Hotkeys Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "dbus.service" ];
-      postStop = "rm -f /var/run/pommed.pid";
-      script = "${pkgs.pommed}/bin/pommed";
-      serviceConfig.Type = "forking";
-      path = [ pkgs.eject ];
+      script = "${pkgs.pommed_light}/bin/pommed -f";
     };
   };
 }
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
index a3403740312..8ddb9ef9c53 100644
--- a/nixos/modules/services/hardware/sane.nix
+++ b/nixos/modules/services/hardware/sane.nix
@@ -7,9 +7,35 @@ let
   pkg = if config.hardware.sane.snapshot
     then pkgs.sane-backends-git
     else pkgs.sane-backends;
-  backends = [ pkg ] ++ config.hardware.sane.extraBackends;
+
+  sanedConf = pkgs.writeTextFile {
+    name = "saned.conf";
+    destination = "/etc/sane.d/saned.conf";
+    text = ''
+      localhost
+      ${config.services.saned.extraConfig}
+    '';
+  };
+
+  netConf = pkgs.writeTextFile {
+    name = "net.conf";
+    destination = "/etc/sane.d/net.conf";
+    text = ''
+      ${lib.optionalString config.services.saned.enable "localhost"}
+      ${config.hardware.sane.netConf}
+    '';
+  };
+
+  env = {
+    SANE_CONFIG_DIR = config.hardware.sane.configDir;
+    LD_LIBRARY_PATH = [ "${saneConfig}/lib/sane" ];
+  };
+
+  backends = [ pkg netConf ] ++ optional config.services.saned.enable sanedConf ++ config.hardware.sane.extraBackends;
   saneConfig = pkgs.mkSaneConfig { paths = backends; };
 
+  enabled = config.hardware.sane.enable || config.services.saned.enable;
+
 in
 
 {
@@ -51,27 +77,86 @@ in
 
     hardware.sane.configDir = mkOption {
       type = types.string;
+      internal = true;
       description = "The value of SANE_CONFIG_DIR.";
     };
 
-  };
+    hardware.sane.netConf = mkOption {
+      type = types.lines;
+      default = "";
+      example = "192.168.0.16";
+      description = ''
+        Network hosts that should be probed for remote scanners.
+      '';
+    };
 
+    services.saned.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable saned network daemon for remote connection to scanners.
 
-  ###### implementation
+        saned would be runned from <literal>scanner</literal> user; to allow
+        access to hardware that doesn't have <literal>scanner</literal> group
+        you should add needed groups to this user.
+      '';
+    };
 
-  config = mkIf config.hardware.sane.enable {
+    services.saned.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = "192.168.0.0/24";
+      description = ''
+        Extra saned configuration lines.
+      '';
+    };
 
-    hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d";
+  };
 
-    environment.systemPackages = backends;
-    environment.sessionVariables = {
-      SANE_CONFIG_DIR = config.hardware.sane.configDir;
-      LD_LIBRARY_PATH = [ "${saneConfig}/lib/sane" ];
-    };
-    services.udev.packages = backends;
 
-    users.extraGroups."scanner".gid = config.ids.gids.scanner;
+  ###### implementation
 
-  };
+  config = mkMerge [
+    (mkIf enabled {
+      hardware.sane.configDir = mkDefault "${saneConfig}/etc/sane.d";
+
+      environment.systemPackages = backends;
+      environment.sessionVariables = env;
+      services.udev.packages = backends;
+
+      users.extraGroups."scanner".gid = config.ids.gids.scanner;
+    })
+
+    (mkIf config.services.saned.enable {
+      networking.firewall.connectionTrackingModules = [ "sane" ];
+
+      systemd.services."saned@" = {
+        description = "Scanner Service";
+        environment = mapAttrs (name: val: toString val) env;
+        serviceConfig = {
+          User = "scanner";
+          Group = "scanner";
+          ExecStart = "${pkg}/bin/saned";
+        };
+      };
+
+      systemd.sockets.saned = {
+        description = "saned incoming socket";
+        wantedBy = [ "sockets.target" ];
+        listenStreams = [ "0.0.0.0:6566" "[::]:6566" ];
+        socketConfig = {
+          # saned needs to distinguish between IPv4 and IPv6 to open matching data sockets.
+          BindIPv6Only = "ipv6-only";
+          Accept = true;
+          MaxConnections = 1;
+        };
+      };
+
+      users.extraUsers."scanner" = {
+        uid = config.ids.uids.scanner;
+        group = "scanner";
+      };
+    })
+  ];
 
 }
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
index 3ec74458cd2..1923addeb3a 100644
--- a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
@@ -81,12 +81,11 @@ in
         { office1 = { model = "MFC-7860DW"; ip = "192.168.1.2"; };
           office2 = { model = "MFC-7860DW"; nodename = "BRW0080927AFBCE"; };
         };
-      type = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule netDeviceOpts);
       description = ''
         The list of network devices that will be registered against the brscan4
         sane backend.
       '';
-      options = [ netDeviceOpts ];
     };
   };
 
@@ -113,4 +112,4 @@ in
     ];
 
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index 281d02a8c65..f36a9e7b459 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -40,7 +40,7 @@ in
       };
 
       extraConfig = mkOption {
-        type = types.str;
+        type = types.lines;
         default = "";
         description = "Additional configuration variables for TLP";
       };
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 7c4c93d0fcb..14d65978c32 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -32,13 +32,11 @@ let
   '';
 
   # Perform substitutions in all udev rules files.
-  udevRules = stdenv.mkDerivation {
-    name = "udev-rules";
-
-    preferLocalBuild = true;
-    allowSubstitutes = false;
-
-    buildCommand = ''
+  udevRules = pkgs.runCommand "udev-rules"
+    { preferLocalBuild = true;
+      allowSubstitutes = false;
+    }
+    ''
       mkdir -p $out
       shopt -s nullglob
       set +o pipefail
@@ -130,15 +128,12 @@ let
         ln -s /dev/null $out/80-drivers.rules
       ''}
     ''; # */
-  };
 
-  hwdbBin = stdenv.mkDerivation {
-    name = "hwdb.bin";
-
-    preferLocalBuild = true;
-    allowSubstitutes = false;
-
-    buildCommand = ''
+  hwdbBin = pkgs.runCommand "hwdb.bin"
+    { preferLocalBuild = true;
+      allowSubstitutes = false;
+    }
+    ''
       mkdir -p etc/udev/hwdb.d
       for i in ${toString ([udev] ++ cfg.packages)}; do
         echo "Adding hwdb files for package $i"
@@ -151,7 +146,6 @@ let
       ${udev}/bin/udevadm hwdb --update --root=$(pwd)
       mv etc/udev/hwdb.bin $out
     '';
-  };
 
   # Udev has a 512-character limit for ENV{PATH}, so create a symlink
   # tree to work around this.
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 755599ff621..86451ec318c 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -55,49 +55,53 @@ let
 
   levelOption = mkOption {
     default = "server";
-    type = types.str;
+    type = types.enum [ "workstation" "server" "paranoid" ];
     description = ''
-      Set the logcheck level. Either "workstation", "server", or "paranoid".
+      Set the logcheck level.
     '';
   };
 
   ignoreOptions = {
-    level = levelOption;
+    options = {
+      level = levelOption;
 
-    regex = mkOption {
-      default = "";
-      type = types.str;
-      description = ''
-        Regex specifying which log lines to ignore.
-      '';
+      regex = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Regex specifying which log lines to ignore.
+        '';
+      };
     };
   };
 
   ignoreCronOptions = {
-    user = mkOption {
-      default = "root";
-      type = types.str;
-      description = ''
-        User that runs the cronjob.
-      '';
-    };
+    options = {
+      user = mkOption {
+        default = "root";
+        type = types.str;
+        description = ''
+          User that runs the cronjob.
+        '';
+      };
 
-    cmdline = mkOption {
-      default = "";
-      type = types.str;
-      description = ''
-        Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
-      '';
-    };
+      cmdline = mkOption {
+        default = "";
+        type = types.str;
+        description = ''
+          Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
+        '';
+      };
 
-    timeArgs = mkOption {
-      default = null;
-      type = types.nullOr (types.str);
-      example = "02 06 * * *";
-      description = ''
-        "min hr dom mon dow" crontab time args, to auto-create a cronjob too.
-        Leave at null to not do this and just add a logcheck ignore rule.
-      '';
+      timeArgs = mkOption {
+        default = null;
+        type = types.nullOr (types.str);
+        example = "02 06 * * *";
+        description = ''
+          "min hr dom mon dow" crontab time args, to auto-create a cronjob too.
+          Leave at null to not do this and just add a logcheck ignore rule.
+        '';
+      };
     };
   };
 
@@ -180,8 +184,7 @@ in
         description = ''
           This option defines extra ignore rules.
         '';
-        type = types.loaOf types.optionSet;
-        options = [ ignoreOptions ];
+        type = with types; loaOf (submodule ignoreOptions);
       };
 
       ignoreCron = mkOption {
@@ -189,8 +192,7 @@ in
         description = ''
           This option defines extra ignore rules for cronjobs.
         '';
-        type = types.loaOf types.optionSet;
-        options = [ ignoreOptions ignoreCronOptions ];
+        type = with types; loaOf (submodule ignoreCronOptions);
       };
 
       extraGroups = mkOption {
diff --git a/nixos/modules/services/logging/syslogd.nix b/nixos/modules/services/logging/syslogd.nix
index a0f8e89fa69..fe0b0490811 100644
--- a/nixos/modules/services/logging/syslogd.nix
+++ b/nixos/modules/services/logging/syslogd.nix
@@ -100,6 +100,12 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions =
+      [ { assertion = !config.services.rsyslogd.enable;
+          message = "rsyslogd conflicts with syslogd";
+        }
+      ];
+
     environment.systemPackages = [ pkgs.sysklogd ];
 
     services.syslogd.extraParams = optional cfg.enableNetworkInput "-r";
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 7848288850a..1d427429b9c 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -111,7 +111,7 @@ in
     };
 
     extraConfig = mkOption {
-      type = types.str;
+      type = types.lines;
       default = "";
       example = "mail_debug = yes";
       description = "Additional entries to put verbatim into Dovecot's config file.";
@@ -271,6 +271,9 @@ in
       { assertion = cfg.showPAMFailure -> cfg.enablePAM;
         message = "dovecot is configured with showPAMFailure while enablePAM is disabled";
       }
+      { assertion = (cfg.sieveScripts != {}) -> ((cfg.mailUser != null) && (cfg.mailGroup != null));
+        message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set";
+      }
     ];
 
   };
diff --git a/nixos/modules/services/mail/freepops.nix b/nixos/modules/services/mail/freepops.nix
index e8c30a36923..5b729ca50a5 100644
--- a/nixos/modules/services/mail/freepops.nix
+++ b/nixos/modules/services/mail/freepops.nix
@@ -74,7 +74,8 @@ in
   config = mkIf cfg.enable {
     systemd.services.freepopsd = {
       description = "Freepopsd (webmail over POP3)";
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
       script = ''
         ${pkgs.freepops}/bin/freepopsd \
           -p ${toString cfg.port} \
diff --git a/nixos/modules/services/networking/offlineimap.nix b/nixos/modules/services/mail/offlineimap.nix
index daf6196d370..85ece020905 100644
--- a/nixos/modules/services/networking/offlineimap.nix
+++ b/nixos/modules/services/mail/offlineimap.nix
@@ -59,7 +59,7 @@ in {
       };
       path = cfg.path;
     };
-    environment.systemPackages = [ "${cfg.package}" ];
+    environment.systemPackages = [ cfg.package ];
     systemd.user.timers.offlineimap = {
       description = "offlineimap timer";
       timerConfig               = {
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index fb94560e10a..53acdba4245 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -1,17 +1,16 @@
 { config, lib, pkgs, ... }:
 
-with pkgs;
 with lib;
 
 let
 
   cfg = config.services.opensmtpd;
-  conf = writeText "smtpd.conf" cfg.serverConfiguration;
+  conf = pkgs.writeText "smtpd.conf" cfg.serverConfiguration;
   args = concatStringsSep " " cfg.extraServerArgs;
 
   sendmail = pkgs.runCommand "opensmtpd-sendmail" {} ''
     mkdir -p $out/bin
-    ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail
+    ln -s ${pkgs.opensmtpd}/sbin/smtpctl $out/bin/sendmail
   '';
 
 in {
@@ -48,21 +47,19 @@ in {
       };
 
       serverConfiguration = mkOption {
-        type = types.string;
-        default = "";
+        type = types.lines;
         example = ''
           listen on lo
           accept for any deliver to lmtp localhost:24
-        ''; 
+        '';
         description = ''
           The contents of the smtpd.conf configuration file. See the
-          OpenSMTPD documentation for syntax information. If this option
-          is left empty, the OpenSMTPD server will not start.
+          OpenSMTPD documentation for syntax information.
         '';
       };
 
       procPackages = mkOption {
-        type = types.listOf types.path;
+        type = types.listOf types.package;
         default = [];
         description = ''
           Packages to search for filters, tables, queues, and schedulers.
@@ -100,12 +97,11 @@ in {
     systemd.services.opensmtpd = let
       procEnv = pkgs.buildEnv {
         name = "opensmtpd-procs";
-        paths = [ opensmtpd ] ++ cfg.procPackages;
+        paths = [ pkgs.opensmtpd ] ++ cfg.procPackages;
         pathsToLink = [ "/libexec/opensmtpd" ];
       };
     in {
       wantedBy = [ "multi-user.target" ];
-      wants = [ "network.target" ];
       after = [ "network.target" ];
       preStart = ''
         mkdir -p /var/spool/smtpd
@@ -119,7 +115,7 @@ in {
         chown smtpq.root /var/spool/smtpd/purge
         chmod 700 /var/spool/smtpd/purge
       '';
-      serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
+      serviceConfig.ExecStart = "${pkgs.opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
       environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd";
     };
 
diff --git a/nixos/modules/services/mail/postgrey.nix b/nixos/modules/services/mail/postgrey.nix
new file mode 100644
index 00000000000..d4ae25c066a
--- /dev/null
+++ b/nixos/modules/services/mail/postgrey.nix
@@ -0,0 +1,194 @@
+{ config, lib, pkgs, ... }:
+
+with lib; let
+
+  cfg = config.services.postgrey;
+
+  natural = with types; addCheck int (x: x >= 0);
+  natural' = with types; addCheck int (x: x > 0);
+
+  socket = with types; addCheck (either (submodule unixSocket) (submodule inetSocket)) (x: x ? "path" || x ? "port");
+
+  inetSocket = with types; {
+    options = {
+      addr = mkOption {
+        type = nullOr string;
+        default = null;
+        example = "127.0.0.1";
+        description = "The address to bind to. Localhost if null";
+      };
+      port = mkOption {
+        type = natural';
+        default = 10030;
+        description = "Tcp port to bind to";
+      };
+    };
+  };
+
+  unixSocket = with types; {
+    options = {
+      path = mkOption {
+        type = path;
+        default = "/var/run/postgrey.sock";
+        description = "Path of the unix socket";
+      };
+
+      mode = mkOption {
+        type = string;
+        default = "0777";
+        description = "Mode of the unix socket";
+      };
+    };
+  };
+
+in {
+
+  options = {
+    services.postgrey = with types; {
+      enable = mkOption {
+        type = bool;
+        default = false;
+        description = "Whether to run the Postgrey daemon";
+      };
+      socket = mkOption {
+        type = socket;
+        default = {
+          path = "/var/run/postgrey.sock";
+          mode = "0777";
+        };
+        example = {
+          addr = "127.0.0.1";
+          port = 10030;
+        };
+        description = "Socket to bind to";
+      };
+      greylistText = mkOption {
+        type = string;
+        default = "Greylisted for %%s seconds";
+        description = "Response status text for greylisted messages; use %%s for seconds left until greylisting is over and %%r for mail domain of recipient";
+      };
+      greylistAction = mkOption {
+        type = string;
+        default = "DEFER_IF_PERMIT";
+        description = "Response status for greylisted messages (see access(5))";
+      };
+      greylistHeader = mkOption {
+        type = string;
+        default = "X-Greylist: delayed %%t seconds by postgrey-%%v at %%h; %%d";
+        description = "Prepend header to greylisted mails; use %%t for seconds delayed due to greylisting, %%v for the version of postgrey, %%d for the date, and %%h for the host";
+      };
+      delay = mkOption {
+        type = natural;
+        default = 300;
+        description = "Greylist for N seconds";
+      };
+      maxAge = mkOption {
+        type = natural;
+        default = 35;
+        description = "Delete entries from whitelist if they haven't been seen for N days";
+      };
+      retryWindow = mkOption {
+        type = either string natural;
+        default = 2;
+        example = "12h";
+        description = "Allow N days for the first retry. Use string with appended 'h' to specify time in hours";
+      };
+      lookupBySubnet = mkOption {
+        type = bool;
+        default = true;
+        description = "Strip the last N bits from IP addresses, determined by IPv4CIDR and IPv6CIDR";
+      };
+      IPv4CIDR = mkOption {
+        type = natural;
+        default = 24;
+        description = "Strip N bits from IPv4 addresses if lookupBySubnet is true";
+      };
+      IPv6CIDR = mkOption {
+        type = natural;
+        default = 64;
+        description = "Strip N bits from IPv6 addresses if lookupBySubnet is true";
+      };
+      privacy = mkOption {
+        type = bool;
+        default = true;
+        description = "Store data using one-way hash functions (SHA1)";
+      };
+      autoWhitelist = mkOption {
+        type = nullOr natural';
+        default = 5;
+        description = "Whitelist clients after successful delivery of N messages";
+      };
+      whitelistClients = mkOption {
+        type = listOf path;
+        default = [];
+        description = "Client address whitelist files (see postgrey(8))";
+      };
+      whitelistRecipients = mkOption {
+        type = listOf path;
+        default = [];
+        description = "Recipient address whitelist files (see postgrey(8))";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.postgrey ];
+
+    users = {
+      extraUsers = {
+        postgrey = {
+          description = "Postgrey Daemon";
+          uid = config.ids.uids.postgrey;
+          group = "postgrey";
+        };
+      };
+      extraGroups = {
+        postgrey = {
+          gid = config.ids.gids.postgrey;
+        };
+      };
+    };
+
+    systemd.services.postgrey = let
+      bind-flag = if cfg.socket ? "path" then
+        ''--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}''
+      else
+        ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}'';
+    in {
+      description = "Postfix Greylisting Service";
+      wantedBy = [ "multi-user.target" ];
+      before = [ "postfix.service" ];
+      preStart = ''
+        mkdir -p /var/postgrey
+        chown postgrey:postgrey /var/postgrey
+        chmod 0770 /var/postgrey
+      '';
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = ''${pkgs.postgrey}/bin/postgrey \
+          ${bind-flag} \
+          --group=postgrey --user=postgrey \
+          --dbdir=/var/postgrey \
+          --delay=${toString cfg.delay} \
+          --max-age=${toString cfg.maxAge} \
+          --retry-window=${toString cfg.retryWindow} \
+          ${if cfg.lookupBySubnet then "--lookup-by-subnet" else "--lookup-by-host"} \
+          --ipv4cidr=${toString cfg.IPv4CIDR} --ipv6cidr=${toString cfg.IPv6CIDR} \
+          ${optionalString cfg.privacy "--privacy"} \
+          --auto-whitelist-clients=${toString (if cfg.autoWhitelist == null then 0 else cfg.autoWhitelist)} \
+          --greylist-action=${cfg.greylistAction} \
+          --greylist-text="${cfg.greylistText}" \
+          --x-greylist-header="${cfg.greylistHeader}" \
+          ${concatMapStringsSep " " (x: "--whitelist-clients=" + x) cfg.whitelistClients} \
+          ${concatMapStringsSep " " (x: "--whitelist-recipients=" + x) cfg.whitelistRecipients}
+        '';
+        Restart = "always";
+        RestartSec = 5;
+        TimeoutSec = 10;
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/mail/postsrsd.nix b/nixos/modules/services/mail/postsrsd.nix
index 68a4c101206..a1af16ec9ac 100644
--- a/nixos/modules/services/mail/postsrsd.nix
+++ b/nixos/modules/services/mail/postsrsd.nix
@@ -20,17 +20,29 @@ in {
         description = "Whether to enable the postsrsd SRS server for Postfix.";
       };
 
+      secretsFile = mkOption {
+        type = types.path;
+        default = "/var/lib/postsrsd/postsrsd.secret";
+        description = "Secret keys used for signing and verification";
+      };
+
       domain = mkOption {
         type = types.str;
         description = "Domain name for rewrite";
       };
 
-      secretsFile = mkOption {
-        type = types.path;
-        default = "/var/lib/postsrsd/postsrsd.secret";
-        description = "Secret keys used for signing and verification";
+      separator = mkOption {
+        type = types.enum ["-" "=" "+"];
+        default = "=";
+        description = "First separator character in generated addresses";
       };
 
+      # bindAddress = mkOption { # uncomment once 1.5 is released
+      #   type = types.str;
+      #   default = "127.0.0.1";
+      #   description = "Socket listen address";
+      # };
+
       forwardPort = mkOption {
         type = types.int;
         default = 10001;
@@ -43,6 +55,18 @@ in {
         description = "Port for the reverse SRS lookup";
       };
 
+      timeout = mkOption {
+        type = types.int;
+        default = 1800;
+        description = "Timeout for idle client connections in seconds";
+      };
+
+      excludeDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Origin domains to exclude from rewriting in addition to primary domain";
+      };
+
       user = mkOption {
         type = types.str;
         default = "postsrsd";
@@ -86,7 +110,7 @@ in {
       path = [ pkgs.coreutils ];
 
       serviceConfig = {
-        ExecStart = ''${pkgs.postsrsd}/sbin/postsrsd "-s${cfg.secretsFile}" "-d${cfg.domain}" -f${toString cfg.forwardPort} -r${toString cfg.reversePort}'';
+        ExecStart = ''${pkgs.postsrsd}/sbin/postsrsd "-s${cfg.secretsFile}" "-d${cfg.domain}" -a${cfg.separator} -f${toString cfg.forwardPort} -r${toString cfg.reversePort} -t${toString cfg.timeout} "-X${concatStringsSep "," cfg.excludeDomains}"'';
         User = cfg.user;
         Group = cfg.group;
         PermissionsStartOnly = true;
diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix
index e27b38bc0e2..8f18b929c11 100644
--- a/nixos/modules/services/mail/rmilter.nix
+++ b/nixos/modules/services/mail/rmilter.nix
@@ -203,7 +203,7 @@ milter_default_action = accept
         PermissionsStartOnly = true;
         Restart = "always";
         RuntimeDirectory = "rmilter";
-        RuntimeDirectoryPermissions="0755";
+        RuntimeDirectoryMode = "0755";
       };
 
     };
diff --git a/nixos/modules/services/misc/apache-kafka.nix b/nixos/modules/services/misc/apache-kafka.nix
index 88ce8b5a23f..c856d3294c0 100644
--- a/nixos/modules/services/misc/apache-kafka.nix
+++ b/nixos/modules/services/misc/apache-kafka.nix
@@ -139,7 +139,7 @@ in {
     systemd.services.apache-kafka = {
       description = "Apache Kafka Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix
index 18f0c3eb83d..40b48f70f7e 100644
--- a/nixos/modules/services/misc/autofs.nix
+++ b/nixos/modules/services/misc/autofs.nix
@@ -22,7 +22,7 @@ in
         default = false;
         description = "
           Mount filesystems on demand. Unmount them automatically.
-          You may also be interested in afuese.
+          You may also be interested in afuse.
         ";
       };
 
diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix
index 5bda73ab64f..52719222db6 100644
--- a/nixos/modules/services/misc/bepasty.nix
+++ b/nixos/modules/services/misc/bepasty.nix
@@ -53,7 +53,7 @@ in
           };
 
           extraConfig = mkOption {
-            type = types.str;
+            type = types.lines;
             description = ''
               Extra configuration for bepasty server to be appended on the
               configuration.
diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix
index 72ec68dee6b..fe13013286b 100644..100755
--- a/nixos/modules/services/misc/confd.nix
+++ b/nixos/modules/services/misc/confd.nix
@@ -33,7 +33,7 @@ in {
 
     nodes = mkOption {
       description = "Confd list of nodes to connect to.";
-      default = [ "http://127.0.0.1:4001" ];
+      default = [ "http://127.0.0.1:2379" ];
       type = types.listOf types.str;
     };
 
diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix
index ef744439c3d..7e3b6431a13 100644
--- a/nixos/modules/services/misc/dictd.nix
+++ b/nixos/modules/services/misc/dictd.nix
@@ -2,6 +2,10 @@
 
 with lib;
 
+let
+  cfg = config.services.dictd;
+in
+
 {
 
   ###### interface
@@ -20,8 +24,9 @@ with lib;
 
       DBs = mkOption {
         type = types.listOf types.package;
-        default = [];
-        example = [ pkgs.dictdDBs.nld2eng ];
+        default = with pkgs.dictdDBs; [ wiktionary wordnet ];
+        defaultText = "with pkgs.dictdDBs; [ wiktionary wordnet ]";
+        example = literalExample "[ pkgs.dictdDBs.nld2eng ]";
         description = ''List of databases to make available.'';
       };
 
@@ -34,8 +39,8 @@ with lib;
 
   config = let dictdb = pkgs.dictDBCollector { dictlist = map (x: {
                name = x.name;
-               filename = x; } ) config.services.dictd.DBs; };
-  in mkIf config.services.dictd.enable {
+               filename = x; } ) cfg.DBs; };
+  in mkIf cfg.enable {
 
     # get the command line client on system path to make some use of the service
     environment.systemPackages = [ pkgs.dict ];
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index e5a125ad324..e96645c79c7 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -41,6 +41,7 @@ in
         type = types.path;
         description = "The Disnix package";
         default = pkgs.disnix;
+        defaultText = "pkgs.disnix";
       };
 
     };
diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix
index add339f9bdf..96ac2a1cf2c 100644
--- a/nixos/modules/services/misc/docker-registry.nix
+++ b/nixos/modules/services/misc/docker-registry.nix
@@ -6,14 +6,8 @@ let
   cfg = config.services.dockerRegistry;
 
 in {
-  ###### interface
-
   options.services.dockerRegistry = {
-    enable = mkOption {
-      description = "Whether to enable docker registry server.";
-      default = false;
-      type = types.bool;
-    };
+    enable = mkEnableOption "Docker Registry";
 
     listenAddress = mkOption {
       description = "Docker registry host or ip to bind to.";
@@ -35,8 +29,7 @@ in {
 
     extraConfig = mkOption {
       description = ''
-        Docker extra registry configuration. See
-        <link xlink:href="https://github.com/docker/docker-registry/blob/master/config/config_sample.yml"/>
+        Docker extra registry configuration via environment variables.
       '';
       default = {};
       type = types.attrsOf types.str;
@@ -50,32 +43,24 @@ in {
       after = [ "network.target" ];
 
       environment = {
-        REGISTRY_HOST = cfg.listenAddress;
-        REGISTRY_PORT = toString cfg.port;
-        GUNICORN_OPTS = "[--preload]"; # see https://github.com/docker/docker-registry#sqlalchemy
-        STORAGE_PATH = cfg.storagePath;
+        REGISTRY_HTTP_ADDR = "${cfg.listenAddress}:${toString cfg.port}";
+        REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY = cfg.storagePath;
       } // cfg.extraConfig;
 
+      script = ''
+        ${pkgs.docker-distribution}/bin/registry serve \
+          ${pkgs.docker-distribution.out}/share/go/src/github.com/docker/distribution/cmd/registry/config-example.yml
+      '';
+
       serviceConfig = {
-        ExecStart = "${pkgs.pythonPackages.docker_registry}/bin/docker-registry";
         User = "docker-registry";
-        Group = "docker";
-        PermissionsStartOnly = true;
         WorkingDirectory = cfg.storagePath;
       };
-
-      postStart = ''
-        until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/'; do
-          sleep 1;
-        done
-      '';
     };
 
-    users.extraGroups.docker.gid = mkDefault config.ids.gids.docker;
     users.extraUsers.docker-registry = {
       createHome = true;
       home = cfg.storagePath;
-      uid = config.ids.uids.docker-registry;
     };
   };
 }
diff --git a/nixos/modules/services/misc/emby.nix b/nixos/modules/services/misc/emby.nix
index fe872349f45..9f290ed70c9 100644
--- a/nixos/modules/services/misc/emby.nix
+++ b/nixos/modules/services/misc/emby.nix
@@ -43,7 +43,7 @@ in
         User = cfg.user;
         Group = cfg.group;
         PermissionsStartOnly = "true";
-        ExecStart = "${pkgs.mono}/bin/mono ${pkgs.emby}/bin/MediaBrowser.Server.Mono.exe";
+        ExecStart = "${pkgs.emby}/bin/MediaBrowser.Server.Mono";
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/misc/errbot.nix b/nixos/modules/services/misc/errbot.nix
new file mode 100644
index 00000000000..427cb7c546d
--- /dev/null
+++ b/nixos/modules/services/misc/errbot.nix
@@ -0,0 +1,101 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.errbot;
+  pluginEnv = plugins: pkgs.buildEnv {
+    name = "errbot-plugins";
+    paths = plugins;
+  };
+  mkConfigDir = instanceCfg: dataDir: pkgs.writeTextDir "config.py" ''
+    import logging
+    BACKEND = '${instanceCfg.backend}'
+    BOT_DATA_DIR = '${dataDir}'
+    BOT_EXTRA_PLUGIN_DIR = '${pluginEnv instanceCfg.plugins}'
+
+    BOT_LOG_LEVEL = logging.${instanceCfg.logLevel}
+    BOT_LOG_FILE = False
+
+    BOT_ADMINS = (${concatMapStringsSep "," (name: "'${name}'") instanceCfg.admins})
+
+    BOT_IDENTITY = ${builtins.toJSON instanceCfg.identity}
+
+    ${instanceCfg.extraConfig}
+  '';
+in {
+  options = {
+    services.errbot.instances = mkOption {
+      default = {};
+      description = "Errbot instance configs";
+      type = types.attrsOf (types.submodule {
+        options = {
+          dataDir = mkOption {
+            type = types.nullOr types.path;
+            default = null;
+            description = "Data directory for errbot instance.";
+          };
+
+          plugins = mkOption {
+            type = types.listOf types.package;
+            default = [];
+            description = "List of errbot plugin derivations.";
+          };
+
+          logLevel = mkOption {
+            type = types.str;
+            default = "INFO";
+            description = "Errbot log level";
+          };
+
+          admins = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            description = "List of identifiers of errbot admins.";
+          };
+
+          backend = mkOption {
+            type = types.str;
+            default = "XMPP";
+            description = "Errbot backend name.";
+          };
+
+          identity = mkOption {
+            type = types.attrs;
+            description = "Errbot identity configuration";
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = "String to be appended to the config verbatim";
+          };
+        };
+      });
+    };
+  };
+
+  config = mkIf (cfg.instances != {}) {
+    users.extraUsers.errbot.group = "errbot";
+    users.extraGroups.errbot = {};
+
+    systemd.services = mapAttrs' (name: instanceCfg: nameValuePair "errbot-${name}" (
+    let
+      dataDir = if !isNull instanceCfg.dataDir then instanceCfg.dataDir else
+        "/var/lib/errbot/${name}";
+    in {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p ${dataDir}
+        chown -R errbot:errbot ${dataDir}
+      '';
+      serviceConfig = {
+        User = "errbot";
+        Restart = "on-failure";
+        ExecStart = "${pkgs.errbot}/bin/errbot -c ${mkConfigDir instanceCfg dataDir}/config.py";
+        PermissionsStartOnly = true;
+      };
+    })) cfg.instances;
+  };
+}
diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix
index 0d6ed8eb904..1de02d76ba0 100644
--- a/nixos/modules/services/misc/etcd.nix
+++ b/nixos/modules/services/misc/etcd.nix
@@ -28,13 +28,13 @@ in {
 
     listenClientUrls = mkOption {
       description = "Etcd list of URLs to listen on for client traffic.";
-      default = ["http://localhost:4001"];
+      default = ["http://127.0.0.1:2379"];
       type = types.listOf types.str;
     };
 
     listenPeerUrls = mkOption {
       description = "Etcd list of URLs to listen on for peer traffic.";
-      default = ["http://localhost:7001"];
+      default = ["http://127.0.0.1:2380"];
       type = types.listOf types.str;
     };
 
@@ -46,7 +46,7 @@ in {
 
     initialCluster = mkOption {
       description = "Etcd initial cluster configuration for bootstrapping.";
-      default = ["${cfg.name}=http://localhost:7001"];
+      default = ["${cfg.name}=http://127.0.0.1:2380"];
       type = types.listOf types.str;
     };
 
@@ -68,6 +68,54 @@ in {
       type = types.str;
     };
 
+    clientCertAuth = mkOption {
+      description = "Whether to use certs for client authentication";
+      default = false;
+      type = types.bool;
+    };
+
+    trustedCaFile = mkOption {
+      description = "Certificate authority file to use for clients";
+      default = null;
+      type = types.nullOr types.path;
+    };
+
+    certFile = mkOption {
+      description = "Cert file to use for clients";
+      default = null;
+      type = types.nullOr types.path;
+    };
+
+    keyFile = mkOption {
+      description = "Key file to use for clients";
+      default = null;
+      type = types.nullOr types.path;
+    };
+
+    peerCertFile = mkOption {
+      description = "Cert file to use for peer to peer communication";
+      default = cfg.certFile;
+      type = types.nullOr types.path;
+    };
+
+    peerKeyFile = mkOption {
+      description = "Key file to use for peer to peer communication";
+      default = cfg.keyFile;
+      type = types.nullOr types.path;
+    };
+
+    peerTrustedCaFile = mkOption {
+      description = "Certificate authority file to use for peer to peer communication";
+      default = cfg.trustedCaFile;
+      type = types.nullOr types.path;
+    };
+
+    peerClientCertAuth = mkOption {
+      description = "Whether to check all incoming peer requests from the cluster for valid client certificates signed by the supplied CA";
+      default = false;
+      type = types.bool;
+    };
+
     extraConf = mkOption {
       description = ''
         Etcd extra configuration. See
@@ -95,11 +143,11 @@ in {
 
   config = mkIf cfg.enable {
     systemd.services.etcd = {
-      description = "Etcd Daemon";
+      description = "etcd key-value store";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
-      environment = {
+      environment = (filterAttrs (n: v: v != null) {
         ETCD_NAME = cfg.name;
         ETCD_DISCOVERY = cfg.discovery;
         ETCD_DATA_DIR = cfg.dataDir;
@@ -107,18 +155,31 @@ in {
         ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls;
         ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls;
         ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls;
-      } // (optionalAttrs (cfg.discovery == ""){
+        ETCD_PEER_TRUSTED_CA_FILE = cfg.peerTrustedCaFile;
+        ETCD_PEER_CERT_FILE = cfg.peerCertFile;
+        ETCD_PEER_KEY_FILE = cfg.peerKeyFile;
+        ETCD_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth;
+        ETCD_TRUSTED_CA_FILE = cfg.trustedCaFile;
+        ETCD_CERT_FILE = cfg.certFile;
+        ETCD_KEY_FILE = cfg.keyFile;
+      }) // (optionalAttrs (cfg.discovery == ""){
         ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster;
         ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState;
         ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken;
       }) // (mapAttrs' (n: v: nameValuePair "ETCD_${n}" v) cfg.extraConf);
 
+      unitConfig = {
+        Documentation = "https://github.com/coreos/etcd";
+      };
+
       serviceConfig = {
         Type = "notify";
         ExecStart = "${pkgs.etcd.bin}/bin/etcd";
         User = "etcd";
         PermissionsStartOnly = true;
+        LimitNOFILE = 40000;
       };
+
       preStart = ''
         mkdir -m 0700 -p ${cfg.dataDir}
         if [ "$(id -u)" = 0 ]; then chown etcd ${cfg.dataDir}; fi
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
index 4f09cbfdd79..053e7e95635 100644
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -50,7 +50,7 @@ in {
       };
 
     systemd.services.foldingathome = {
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         mkdir -m 0755 -p ${stateDir}
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index b3f09999adb..1fc3a5cc869 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -164,18 +164,21 @@ in {
       packages.gitlab = mkOption {
         type = types.package;
         default = pkgs.gitlab;
+        defaultText = "pkgs.gitlab";
         description = "Reference to the gitlab package";
       };
 
       packages.gitlab-shell = mkOption {
         type = types.package;
         default = pkgs.gitlab-shell;
+        defaultText = "pkgs.gitlab-shell";
         description = "Reference to the gitlab-shell package";
       };
 
       packages.gitlab-workhorse = mkOption {
         type = types.package;
         default = pkgs.gitlab-workhorse;
+        defaultText = "pkgs.gitlab-workhorse";
         description = "Reference to the gitlab-workhorse package";
       };
 
@@ -425,7 +428,7 @@ in {
         TimeoutSec = "300";
         Restart = "on-failure";
         WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
-        ExecStart="${cfg.packages.gitlab.env}/bin/bundle exec \"sidekiq -q post_receive -q mailers -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
+        ExecStart="${cfg.packages.gitlab.env}/bin/bundle exec \"sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
       };
     };
 
@@ -449,18 +452,21 @@ in {
         Group = cfg.group;
         TimeoutSec = "300";
         Restart = "on-failure";
+        WorkingDirectory = gitlabEnv.HOME;
         ExecStart =
           "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse "
           + "-listenUmask 0 "
           + "-listenNetwork unix "
           + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
           + "-authSocket ${gitlabSocket} "
-          + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public";
+          + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public "
+          + "-secretPath ${cfg.packages.gitlab}/share/gitlab/.gitlab_workhorse_secret";
       };
     };
 
     systemd.services.gitlab = {
       after = [ "network.target" "postgresql.service" "redis.service" ];
+      requires = [ "gitlab-sidekiq.service" ];
       wantedBy = [ "multi-user.target" ];
       environment = gitlabEnv;
       path = with pkgs; [
@@ -525,17 +531,23 @@ in {
             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
             touch "${cfg.statePath}/db-created"
-
-            # The gitlab:setup task is horribly broken somehow, these two tasks will do the same for setting up the initial database
-            ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
-            ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \
-              GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}";
           fi
         fi
 
+        # enable required pg_trgm extension for gitlab
+        psql gitlab -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
         # Always do the db migrations just to be sure the database is up-to-date
         ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
 
+        # The gitlab:setup task is horribly broken somehow, the db:migrate
+        # task above and the db:seed_fu below will do the same for setting
+        # up the initial database
+        if ! test -e "${cfg.statePath}/db-seeded"; then
+          ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \
+            GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}"
+          touch "${cfg.statePath}/db-seeded"
+        fi
+
         # Change permissions in the last step because some of the
         # intermediary scripts like to create directories as root.
         chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}
diff --git a/nixos/modules/services/misc/leaps.nix b/nixos/modules/services/misc/leaps.nix
new file mode 100644
index 00000000000..b92cf27f58d
--- /dev/null
+++ b/nixos/modules/services/misc/leaps.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, ... } @ args:
+
+with lib;
+
+let
+  cfg = config.services.leaps;
+  stateDir = "/var/lib/leaps/";
+in
+{
+  options = {
+    services.leaps = {
+      enable = mkEnableOption "leaps";
+      port = mkOption {
+        type = types.int;
+        default = 8080;
+        description = "A port where leaps listens for incoming http requests";
+      };
+      address = mkOption {
+        default = "";
+        type = types.str;
+        example = "127.0.0.1";
+        description = "Hostname or IP-address to listen to. By default it will listen on all interfaces.";
+      };
+      path = mkOption {
+        default = "/";
+        type = types.path;
+        description = "Subdirectory used for reverse proxy setups";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users = {
+      users.leaps = {
+        uid             = config.ids.uids.leaps;
+        description     = "Leaps server user";
+        group           = "leaps";
+        home            = stateDir;
+        createHome      = true;
+      };
+
+      groups.leaps = {
+        gid = config.ids.gids.leaps;
+      };
+    };
+
+    systemd.services.leaps = {
+      description   = "leaps service";
+      wantedBy      = [ "multi-user.target" ];
+      after         = [ "network.target" ];
+
+      serviceConfig = {
+        User = "leaps";
+        Group = "leaps";
+        Restart = "on-failure";
+        WorkingDirectory = stateDir;
+        PrivateTmp = true;
+        ExecStart = "${pkgs.leaps.bin}/bin/leaps -path ${toString cfg.path} -address ${cfg.address}:${toString cfg.port}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index bb8dc640f98..4a1bea50c14 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -5,15 +5,20 @@ with lib;
 let
   cfg = config.services.matrix-synapse;
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
-  mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${if r.compress then "true" else "false"}}'';
-  mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${if l.tls then "true" else "false"}, x_forwarded: ${if l.x_forwarded then "true" else "false"}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
+  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";
   configFile = pkgs.writeText "homeserver.yaml" ''
+${optionalString (cfg.tls_certificate_path != null) ''
 tls_certificate_path: "${cfg.tls_certificate_path}"
+''}
 ${optionalString (cfg.tls_private_key_path != null) ''
 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: ${if cfg.no_tls then "true" else "false"}
+''}
+no_tls: ${fromBool cfg.no_tls}
 ${optionalString (cfg.bind_port != null) ''
 bind_port: ${toString cfg.bind_port}
 ''}
@@ -25,7 +30,7 @@ bind_host: "${cfg.bind_host}"
 ''}
 server_name: "${cfg.server_name}"
 pid_file: "/var/run/matrix-synapse.pid"
-web_client: ${if cfg.web_client then "true" else "false"}
+web_client: ${fromBool cfg.web_client}
 ${optionalString (cfg.public_baseurl != null) ''
 public_baseurl: "${cfg.public_baseurl}"
 ''}
@@ -53,14 +58,19 @@ media_store_path: "/var/lib/matrix-synapse/media"
 uploads_path: "/var/lib/matrix-synapse/uploads"
 max_upload_size: "${cfg.max_upload_size}"
 max_image_pixels: "${cfg.max_image_pixels}"
-dynamic_thumbnails: ${if cfg.dynamic_thumbnails then "true" else "false"}
-url_preview_enabled: False
+dynamic_thumbnails: ${fromBool cfg.dynamic_thumbnails}
+url_preview_enabled: ${fromBool 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}
+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: ${if cfg.enable_registration_captcha then "true" else "false"}
+enable_registration_captcha: ${fromBool cfg.enable_registration_captcha}
 turn_uris: ${builtins.toJSON cfg.turn_uris}
 turn_shared_secret: "${cfg.turn_shared_secret}"
-enable_registration: ${if cfg.enable_registration then "true" else "false"}
+enable_registration: ${fromBool cfg.enable_registration}
 ${optionalString (cfg.registration_shared_secret != null) ''
 registration_shared_secret: "${cfg.registration_shared_secret}"
 ''}
@@ -68,9 +78,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: {if cfg.allow_guest_access then "true" else "false"}
-enable_metrics: ${if cfg.enable_metrics then "true" else "false"}
-report_stats: ${if cfg.report_stats then "true" else "false"}
+allow_guest_access: ${fromBool 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}
 signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
 key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
@@ -139,8 +155,9 @@ in {
         '';
       };
       tls_certificate_path = mkOption {
-        type = types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.crt";
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
         description = ''
           PEM encoded X509 certificate for TLS.
           You can replace the self-signed certificate that synapse
@@ -151,16 +168,17 @@ in {
       };
       tls_private_key_path = mkOption {
         type = types.nullOr types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.key";
-        example = null;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.key";
         description = ''
           PEM encoded private key for TLS. Specify null if synapse is not
           speaking TLS directly.
         '';
       };
       tls_dh_params_path = mkOption {
-        type = types.str;
-        default = "/var/lib/matrix-synapse/homeserver.tls.dh";
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
         description = ''
           PEM dh parameters for ephemeral keys
         '';
@@ -342,6 +360,47 @@ in {
         default = "10K";
         description = "Number of events to cache in memory.";
       };
+      url_preview_enabled = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Is the preview URL API enabled?  If enabled, you *must* specify an
+          explicit url_preview_ip_range_blacklist of IPs that the spider is
+          denied from accessing.
+        '';
+      };
+      url_preview_ip_range_blacklist = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of IP address CIDR ranges that the URL preview spider is denied
+          from accessing.
+        '';
+      };
+      url_preview_ip_range_whitelist = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of IP address CIDR ranges that the URL preview spider is allowed
+          to access even if they are specified in
+          url_preview_ip_range_blacklist.
+        '';
+      };
+      url_preview_url_blacklist = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "127.0.0.0/8"
+          "10.0.0.0/8"
+          "172.16.0.0/12"
+          "192.168.0.0/16"
+          "100.64.0.0/10"
+          "169.254.0.0/16"
+        ];
+        description = ''
+          Optional list of URL matches that the URL preview spider is
+          denied from accessing.
+        '';
+      };
       recaptcha_private_key = mkOption {
         type = types.str;
         default = "";
@@ -469,6 +528,34 @@ in {
           accessible to anonymous users.
         '';
       };
+      trusted_third_party_id_servers = mkOption {
+        type = types.listOf types.str;
+        default = ["matrix.org"];
+        description = ''
+          The list of identity servers trusted to verify third party identifiers by this server.
+        '';
+      };
+      room_invite_state_types = mkOption {
+        type = types.listOf types.str;
+        default = ["m.room.join_rules" "m.room.canonical_alias" "m.room.avatar" "m.room.name"];
+        description = ''
+          A list of event types that will be included in the room_invite_state
+        '';
+      };
+      macaroon_secret_key = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Secret key for authentication tokens
+        '';
+      };
+      expire_access_token = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable access token expiration.
+        '';
+      };
       key_refresh_interval = mkOption {
         type = types.str;
         default = "1d";
@@ -522,12 +609,10 @@ in {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
-        if ! test -e /var/lib/matrix-synapse; then
-          mkdir -p /var/lib/matrix-synapse
-          chmod 700 /var/lib/matrix-synapse
-          chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
-          ${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse/ --generate-keys
-        fi
+        ${cfg.package}/bin/homeserver \
+          --config-path ${configFile} \
+          --keys-directory /var/lib/matrix-synapse \
+          --generate-keys
       '';
       serviceConfig = {
         Type = "simple";
@@ -535,7 +620,7 @@ in {
         Group = "matrix-synapse";
         WorkingDirectory = "/var/lib/matrix-synapse";
         PermissionsStartOnly = true;
-        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile}";
+        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse";
       };
     };
   };
diff --git a/nixos/modules/services/misc/mesos-master.nix b/nixos/modules/services/misc/mesos-master.nix
index 497646b2b41..99583ebeebd 100644
--- a/nixos/modules/services/misc/mesos-master.nix
+++ b/nixos/modules/services/misc/mesos-master.nix
@@ -80,7 +80,7 @@ in {
     systemd.services.mesos-master = {
       description = "Mesos Master";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-master \
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 8c29734813a..9ddecb6fe30 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -105,7 +105,7 @@ in {
     systemd.services.mesos-slave = {
       description = "Mesos Slave";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       environment.MESOS_CONTAINERIZERS = concatStringsSep "," containerizers;
       serviceConfig = {
         ExecStart = ''
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 333782d15bc..7101cadfeed 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -105,7 +105,9 @@ in
           If set, Nix will perform builds in a sandboxed environment that it
           will set up automatically for each build.  This prevents
           impurities in builds by disallowing access to dependencies
-          outside of the Nix store.
+          outside of the Nix store. This isn't enabled by default for
+          performance. It doesn't affect derivation hashes, so changing
+          this option will not trigger a rebuild of packages.
         ";
       };
 
@@ -172,8 +174,8 @@ in
             sshKey = "/root/.ssh/id_buildfarm";
             system = "x86_64-linux";
             maxJobs = 2;
-            supportedFeatures = "kvm";
-            mandatoryFeatures = "perf";
+            supportedFeatures = [ "kvm" ];
+            mandatoryFeatures = [ "perf" ];
           }
         ];
         description = ''
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
index 5c13da6e83d..304168c65b0 100644
--- a/nixos/modules/services/misc/nix-gc.nix
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -53,7 +53,7 @@ in
     systemd.services.nix-gc =
       { description = "Nix Garbage Collector";
         script = "exec ${config.nix.package.out}/bin/nix-collect-garbage ${cfg.options}";
-        startAt = optionalString cfg.automatic cfg.dates;
+        startAt = optional cfg.automatic cfg.dates;
       };
 
   };
diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix
new file mode 100644
index 00000000000..a76bfd9f1f1
--- /dev/null
+++ b/nixos/modules/services/misc/nix-optimise.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.nix.optimise;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    nix.optimise = {
+
+      automatic = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Automatically run the nix store optimiser at a specific time.";
+      };
+
+      dates = mkOption {
+        default = ["03:45"];
+        type = types.listOf types.str;
+        description = ''
+          Specification (in the format described by
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          which the optimiser will run.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    systemd.services.nix-optimise =
+      { description = "Nix Store Optimiser";
+        serviceConfig.ExecStart = "${config.nix.package}/bin/nix-store --optimise";
+        startAt = optionals cfg.automatic cfg.dates;
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/nscd-sssd.conf b/nixos/modules/services/misc/nscd-sssd.conf
new file mode 100644
index 00000000000..92380f3e4ba
--- /dev/null
+++ b/nixos/modules/services/misc/nscd-sssd.conf
@@ -0,0 +1,36 @@
+server-user             nscd
+threads                 1
+paranoia                no
+debug-level             0
+
+enable-cache            passwd          yes
+positive-time-to-live   passwd          0
+negative-time-to-live   passwd          0
+suggested-size          passwd          211
+check-files             passwd          yes
+persistent              passwd          no
+shared                  passwd          yes
+
+enable-cache            group           yes
+positive-time-to-live   group           0
+negative-time-to-live   group           0
+suggested-size          group           211
+check-files             group           yes
+persistent              group           no
+shared                  group           yes
+
+enable-cache            hosts           yes
+positive-time-to-live   hosts           600
+negative-time-to-live   hosts           5
+suggested-size          hosts           211
+check-files             hosts           yes
+persistent              hosts           no
+shared                  hosts           yes
+
+enable-cache            services        yes
+positive-time-to-live   services        0
+negative-time-to-live   services        0
+suggested-size          services        211
+check-files             services        yes
+persistent              services        no
+shared                  services        yes
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index 0844190a549..ae3f84333d2 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -6,20 +6,21 @@ let
 
   cfg = config.services.parsoid;
 
-  conf = ''
-    exports.setup = function( parsoidConfig ) {
-      ${toString (mapAttrsToList (name: str: "parsoidConfig.setInterwiki('${name}', '${str}');") cfg.interwikis)}
-
-      parsoidConfig.serverInterface = "${cfg.interface}";
-      parsoidConfig.serverPort = ${toString cfg.port};
-
-      parsoidConfig.useSelser = true;
-
-      ${cfg.extraConfig}
-    };
-  '';
+  confTree = {
+    worker_heartbeat_timeout = 300000;
+    logging = { level = "info"; };
+    services = [{
+      module = "lib/index.js";
+      entrypoint = "apiServiceWorker";
+      conf = {
+        mwApis = map (x: if isAttrs x then x else { uri = x; }) cfg.wikis;
+        serverInterface = cfg.interface;
+        serverPort = cfg.port;
+      };
+    }];
+  };
 
-  confFile = builtins.toFile "localsettings.js" conf;
+  confFile = pkgs.writeText "config.yml" (builtins.toJSON (recursiveUpdate confTree cfg.extraConfig));
 
 in
 {
@@ -38,9 +39,9 @@ in
         '';
       };
 
-      interwikis = mkOption {
-        type = types.attrsOf types.str;
-        example = { localhost = "http://localhost/api.php"; };
+      wikis = mkOption {
+        type = types.listOf (types.either types.str types.attrs);
+        example = [ "http://localhost/api.php" ];
         description = ''
           Used MediaWiki API endpoints.
         '';
@@ -71,8 +72,8 @@ in
       };
 
       extraConfig = mkOption {
-        type = types.lines;
-        default = "";
+        type = types.attrs;
+        default = {};
         description = ''
           Extra configuration to add to parsoid configuration.
         '';
@@ -91,7 +92,8 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/api/server.js -c ${confFile} -n ${toString cfg.workers}";
+        User = "nobody";
+        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix
index 92b352db416..f6bf2dee986 100644
--- a/nixos/modules/services/misc/plex.nix
+++ b/nixos/modules/services/misc/plex.nix
@@ -19,6 +19,14 @@ in
         description = "The directory where Plex stores its data files.";
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the media server
+        '';
+      };
+
       user = mkOption {
         type = types.str;
         default = "plex";
@@ -127,7 +135,7 @@ in
         User = cfg.user;
         Group = cfg.group;
         PermissionsStartOnly = "true";
-        ExecStart = "/bin/sh -c '${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server'";
+        ExecStart = "/bin/sh -c ${cfg.package}/usr/lib/plexmediaserver/Plex\\ Media\\ Server";
         Restart = "on-failure";
       };
       environment = {
@@ -141,6 +149,11 @@ in
       };
     };
 
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 32400 3005 8324 32469 ];
+      allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ];
+    };
+
     users.extraUsers = mkIf (cfg.user == "plex") {
       plex = {
         group = cfg.group;
diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix
index 7c9483911f2..e3f1ec67cbb 100644
--- a/nixos/modules/services/misc/redmine.nix
+++ b/nixos/modules/services/misc/redmine.nix
@@ -71,7 +71,7 @@ in {
       };
 
       extraConfig = mkOption {
-        type = types.str;
+        type = types.lines;
         default = "";
         description = "Extra configuration in configuration.yml";
       };
diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix
index c6b67e8498c..8bcf35a8ad3 100644
--- a/nixos/modules/services/misc/rippled.nix
+++ b/nixos/modules/services/misc/rippled.nix
@@ -154,43 +154,45 @@ let
   };
 
   dbOptions = {
-    type = mkOption {
-      description = "Rippled database type.";
-      type = types.enum ["rocksdb" "nudb"];
-      default = "rocksdb";
-    };
+    options = {
+      type = mkOption {
+        description = "Rippled database type.";
+        type = types.enum ["rocksdb" "nudb"];
+        default = "rocksdb";
+      };
 
-    path = mkOption {
-      description = "Location to store the database.";
-      type = types.path;
-      default = cfg.databasePath;
-    };
+      path = mkOption {
+        description = "Location to store the database.";
+        type = types.path;
+        default = cfg.databasePath;
+      };
 
-    compression = mkOption {
-      description = "Whether to enable snappy compression.";
-      type = types.nullOr types.bool;
-      default = null;
-    };
+      compression = mkOption {
+        description = "Whether to enable snappy compression.";
+        type = types.nullOr types.bool;
+        default = null;
+      };
 
-    onlineDelete = mkOption {
-      description = "Enable automatic purging of older ledger information.";
-      type = types.addCheck (types.nullOr types.int) (v: v > 256);
-      default = cfg.ledgerHistory;
-    };
+      onlineDelete = mkOption {
+        description = "Enable automatic purging of older ledger information.";
+        type = types.addCheck (types.nullOr types.int) (v: v > 256);
+        default = cfg.ledgerHistory;
+      };
 
-    advisoryDelete = mkOption {
-      description = ''
-	If set, then require administrative RPC call "can_delete"
-	to enable online deletion of ledger records.
-      '';
-      type = types.nullOr types.bool;
-      default = null;
-    };
+      advisoryDelete = mkOption {
+        description = ''
+	        If set, then require administrative RPC call "can_delete"
+	        to enable online deletion of ledger records.
+        '';
+        type = types.nullOr types.bool;
+        default = null;
+      };
 
-    extraOpts = mkOption {
-      description = "Extra database options.";
-      type = types.lines;
-      default = "";
+      extraOpts = mkOption {
+        description = "Extra database options.";
+        type = types.lines;
+        default = "";
+      };
     };
   };
 
@@ -213,8 +215,7 @@ in
 
       ports = mkOption {
 	description = "Ports exposed by rippled";
-	type = types.attrsOf types.optionSet;
-	options = [portOptions];
+	type = with types; attrsOf (submodule portOptions);
 	default = {
 	  rpc = {
 	    port = 5005;
@@ -238,8 +239,7 @@ in
 
       nodeDb = mkOption {
 	description = "Rippled main database options.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = {
 	  type = "rocksdb";
 	  extraOpts = ''
@@ -254,15 +254,13 @@ in
 
       tempDb = mkOption {
 	description = "Rippled temporary database options.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = null;
       };
 
       importDb = mkOption {
 	description = "Settings for performing a one-time import.";
-	type = types.nullOr types.optionSet;
-	options = dbOptions;
+	type = with types; nullOr (submodule dbOptions);
 	default = null;
       };
 
diff --git a/nixos/modules/services/misc/sssd.nix b/nixos/modules/services/misc/sssd.nix
new file mode 100644
index 00000000000..e818f4a4804
--- /dev/null
+++ b/nixos/modules/services/misc/sssd.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.sssd;
+  nscd = config.services.nscd;
+in {
+  options = {
+    services.sssd = {
+      enable = mkEnableOption "the System Security Services Daemon.";
+
+      config = mkOption {
+        type = types.lines;
+        description = "Contents of <filename>sssd.conf</filename>.";
+        default = ''
+          [sssd]
+          config_file_version = 2
+          services = nss, pam
+          domains = shadowutils
+
+          [nss]
+
+          [pam]
+
+          [domain/shadowutils]
+          id_provider = proxy
+          proxy_lib_name = files
+          auth_provider = proxy
+          proxy_pam_target = sssd-shadowutils
+          proxy_fast_alias = True
+        '';
+      };
+
+      sshAuthorizedKeysIntegration = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to make sshd look up authorized keys from SSS.
+          For this to work, the <literal>ssh</literal> SSS service must be enabled in the sssd configuration.
+        '';
+      };
+    };
+  };
+  config = mkMerge [
+    (mkIf cfg.enable {
+      assertions = singleton {
+        assertion = nscd.enable;
+        message = "nscd must be enabled through `services.nscd.enable` for SSSD to work.";
+      };
+
+      systemd.services.sssd = {
+        description = "System Security Services Daemon";
+        wantedBy    = [ "multi-user.target" ];
+        before = [ "systemd-user-sessions.service" "nss-user-lookup.target" ];
+        after = [ "network-online.target" "nscd.service" ];
+        requires = [ "network-online.target" "nscd.service" ];
+        wants = [ "nss-user-lookup.target" ];
+        restartTriggers = [
+          config.environment.etc."nscd.conf".source
+          config.environment.etc."sssd/sssd.conf".source
+        ];
+        script = ''
+          export LDB_MODULES_PATH+="''${LDB_MODULES_PATH+:}${pkgs.ldb}/modules/ldb:${pkgs.sssd}/modules/ldb"
+          mkdir -p /var/lib/sss/{pubconf,db,mc,pipes,gpo_cache,secrets} /var/lib/sss/pipes/private /var/lib/sss/pubconf/krb5.include.d
+          ${pkgs.sssd}/bin/sssd -D
+        '';
+        serviceConfig = {
+          Type = "forking";
+          PIDFile = "/run/sssd.pid";
+        };
+      };
+
+      environment.etc."sssd/sssd.conf" = {
+        text = cfg.config;
+        mode = "0400";
+      };
+
+      system.nssModules = optional cfg.enable pkgs.sssd;
+      services.nscd.config = builtins.readFile ./nscd-sssd.conf;
+      services.dbus.packages = [ pkgs.sssd ];
+    })
+
+    (mkIf cfg.sshAuthorizedKeysIntegration {
+    # Ugly: sshd refuses to start if a store path is given because /nix/store is group-writable.
+    # So indirect by a symlink.
+    environment.etc."ssh/authorized_keys_command" = {
+      mode = "0755";
+      text = ''
+        #!/bin/sh
+        exec ${pkgs.sssd}/bin/sss_ssh_authorizedkeys "$@"
+      '';
+    };
+    services.openssh.extraConfig = ''
+      AuthorizedKeysCommand /etc/ssh/authorized_keys_command
+      AuthorizedKeysCommandUser nobody
+    '';
+  })];
+}
diff --git a/nixos/modules/services/misc/svnserve.nix b/nixos/modules/services/misc/svnserve.nix
index c74befac749..04a6cd7bfa9 100644
--- a/nixos/modules/services/misc/svnserve.nix
+++ b/nixos/modules/services/misc/svnserve.nix
@@ -35,7 +35,7 @@ in
 
   config = mkIf cfg.enable {
     systemd.services.svnserve = {
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = "mkdir -p ${cfg.svnBaseDir}";
       script = "${pkgs.subversion.out}/bin/svnserve -r ${cfg.svnBaseDir} -d --foreground --pid-file=/var/run/svnserve.pid";
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index 6d458feec34..ca82a733f6f 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -154,7 +154,7 @@ let
 
   certtool = "${pkgs.gnutls.bin}/bin/certtool";
 
-  nixos-taskserver = pkgs.buildPythonPackage {
+  nixos-taskserver = pkgs.pythonPackages.buildPythonPackage {
     name = "nixos-taskserver";
     namePrefix = "";
 
@@ -292,7 +292,7 @@ in {
       };
 
       allowedClientIDs = mkOption {
-        type = with types; loeOf (either (enum ["all" "none"]) str);
+        type = with types; either str (listOf str);
         default = [];
         example = [ "[Tt]ask [2-9]+" ];
         description = ''
@@ -306,7 +306,7 @@ in {
       };
 
       disallowedClientIDs = mkOption {
-        type = with types; loeOf (either (enum ["all" "none"]) str);
+        type = with types; either str (listOf str);
         default = [];
         example = [ "[Tt]ask [2-9]+" ];
         description = ''
diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix
index 4ce692b6f6a..b7bca8b56b2 100644
--- a/nixos/modules/services/misc/zookeeper.nix
+++ b/nixos/modules/services/misc/zookeeper.nix
@@ -113,7 +113,7 @@ in {
     systemd.services.zookeeper = {
       description = "Zookeeper Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       environment = { ZOOCFGDIR = configDir; };
       serviceConfig = {
         ExecStart = ''
diff --git a/nixos/modules/services/monitoring/bosun.nix b/nixos/modules/services/monitoring/bosun.nix
index 9a1e790d3ab..496838a131b 100644
--- a/nixos/modules/services/monitoring/bosun.nix
+++ b/nixos/modules/services/monitoring/bosun.nix
@@ -107,7 +107,7 @@ in {
       };
 
       extraConfig = mkOption {
-        type = types.string;
+        type = types.lines;
         default = "";
         description = ''
           Extra configuration options for Bosun. You should describe your
diff --git a/nixos/modules/services/monitoring/cadvisor.nix b/nixos/modules/services/monitoring/cadvisor.nix
index a67df158be4..8ae8b12056c 100644
--- a/nixos/modules/services/monitoring/cadvisor.nix
+++ b/nixos/modules/services/monitoring/cadvisor.nix
@@ -90,6 +90,7 @@ in {
             ${optionalString cfg.storageDriverSecure "-storage_driver_secure"}
           ''}
         '';
+        TimeoutStartSec=300;
       };
     };
 
diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix
index 3c3d83c66ed..641da60e9ad 100644
--- a/nixos/modules/services/monitoring/collectd.nix
+++ b/nixos/modules/services/monitoring/collectd.nix
@@ -9,7 +9,7 @@ let
     BaseDir "${cfg.dataDir}"
     PIDFile "${cfg.pidFile}"
     AutoLoadPlugin ${if cfg.autoLoadPlugin then "true" else "false"}
-    Hostname ${config.networking.hostName}
+    Hostname "${config.networking.hostName}"
 
     LoadPlugin syslog
     <Plugin "syslog">
@@ -108,7 +108,8 @@ in {
       };
 
       preStart = ''
-        mkdir -m 0700 -p ${cfg.dataDir}
+        mkdir -p ${cfg.dataDir}
+        chmod 755 ${cfg.dataDir}
         install -D /dev/null ${cfg.pidFile}
         if [ "$(id -u)" = 0 ]; then
           chown -R ${cfg.user} ${cfg.dataDir};
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 08fc3f04dbf..c5352e5887d 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -167,7 +167,7 @@ in {
             CACHE_TYPE: 'filesystem'
             CACHE_DIR: '/tmp/graphite-api-cache'
         '';
-        type = types.str;
+        type = types.lines;
       };
     };
 
@@ -387,7 +387,7 @@ in {
       systemd.services.carbonCache = let name = "carbon-cache"; in {
         description = "Graphite Data Storage Backend";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
@@ -410,7 +410,7 @@ in {
         enable = cfg.carbon.enableAggregator;
         description = "Carbon Data Aggregator";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
@@ -426,7 +426,7 @@ in {
       systemd.services.carbonRelay = let name = "carbon-relay"; in {
         description = "Carbon Data Relay";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         environment = carbonEnv;
         serviceConfig = {
           ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd ${carbonOpts name}";
@@ -448,7 +448,7 @@ in {
       systemd.services.graphiteWeb = {
         description = "Graphite Web Interface";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         path = [ pkgs.perl ];
         environment = {
           PYTHONPATH = let
@@ -501,7 +501,7 @@ in {
       systemd.services.graphiteApi = {
         description = "Graphite Api Interface";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         environment = {
           PYTHONPATH = let
               aenv = pkgs.python.buildEnv.override {
@@ -538,7 +538,7 @@ in {
       systemd.services.seyren = {
         description = "Graphite Alerting Dashboard";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" "mongodb.service" ];
+        after = [ "network.target" "mongodb.service" ];
         environment = seyrenConfig;
         serviceConfig = {
           ExecStart = "${pkgs.seyren}/bin/seyren -httpPort ${toString cfg.seyren.port}";
@@ -561,7 +561,7 @@ in {
       systemd.services.graphitePager = {
         description = "Graphite Pager Alerting Daemon";
         wantedBy = [ "multi-user.target" ];
-        after = [ "network-interfaces.target" "redis.service" ];
+        after = [ "network.target" "redis.service" ];
         environment = {
           REDIS_URL = cfg.pager.redisUrl;
           GRAPHITE_URL = cfg.pager.graphiteUrl;
@@ -585,7 +585,7 @@ in {
         serviceConfig = {
           ExecStart = ''
             ${pkgs.pythonPackages.graphite_beacon}/bin/graphite-beacon \
-              --config ${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)}
+              --config=${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)}
           '';
           User = "graphite";
           Group = "graphite";
diff --git a/nixos/modules/services/monitoring/monit.nix b/nixos/modules/services/monitoring/monit.nix
index 704693969a3..e07ffd2e8b5 100644
--- a/nixos/modules/services/monitoring/monit.nix
+++ b/nixos/modules/services/monitoring/monit.nix
@@ -36,11 +36,16 @@ in
     ];
 
     systemd.services.monit = {
-      description = "Monit system watcher";
-      after = [ "network-interfaces.target" ];
+      description = "Pro-active monitoring utility for unix systems";
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      script = "${pkgs.monit}/bin/monit -I -c /etc/monit.conf";
-      serviceConfig.Restart = "always";
+      serviceConfig = {
+        ExecStart = "${pkgs.monit}/bin/monit -I -c /etc/monit.conf";
+        ExecStop = "${pkgs.monit}/bin/monit -c /etc/monit.conf quit";
+        ExecReload = "${pkgs.monit}/bin/monit -c /etc/monit.conf reload";
+        KillMode = "process";
+        Restart = "always";
+      };
     };
   };
 }
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 08ba161d38b..a80565fa280 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -100,6 +100,7 @@ in
 
       extraConfig = mkOption {
         default = "";
+        type = types.lines;
         description = ''
           <filename>munin-node.conf</filename> extra configuration. See
           <link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' />
diff --git a/nixos/modules/services/monitoring/nagios.nix b/nixos/modules/services/monitoring/nagios.nix
index f2f7710de9e..4914c5db97d 100644
--- a/nixos/modules/services/monitoring/nagios.nix
+++ b/nixos/modules/services/monitoring/nagios.nix
@@ -163,7 +163,7 @@ in
       description = "Nagios monitoring daemon";
       path     = [ pkgs.nagios ];
       wantedBy = [ "multi-user.target" ];
-      after    = [ "network-interfaces.target" ];
+      after    = [ "network.target" ];
 
       serviceConfig = {
         User = "nagios";
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
new file mode 100644
index 00000000000..da2cd02eaa3
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -0,0 +1,116 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.alertmanager;
+  mkConfigFile = pkgs.writeText "alertmanager.yml" (builtins.toJSON cfg.configuration);
+in {
+  options = {
+    services.prometheus.alertmanager = {
+      enable = mkEnableOption "Prometheus Alertmanager";
+
+      user = mkOption {
+        type = types.str;
+        default = "nobody";
+        description = ''
+          User name under which Alertmanager shall be run.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nogroup";
+        description = ''
+          Group under which Alertmanager shall be run.
+        '';
+      };
+
+      configuration = mkOption {
+        type = types.attrs;
+        default = {};
+        description = ''
+          Alertmanager configuration as nix attribute set.
+        '';
+      };
+
+      logFormat = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          If set use a syslog logger or JSON logging.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.enum ["debug" "info" "warn" "error" "fatal"];
+        default = "warn";
+        description = ''
+          Only log messages with the given severity or above.
+        '';
+      };
+
+      webExternalUrl = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy).
+          Used for generating relative and absolute links back to Alertmanager itself.
+          If the URL has a path portion, it will be used to prefix all HTTP endoints served by Alertmanager.
+          If omitted, relevant URL components will be derived automatically.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Address to listen on for the web interface and API.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 9093;
+        description = ''
+          Port to listen on for the web interface and API.
+        '';
+      };
+
+      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.alertmanager = {
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" ];
+      script = ''
+        ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \
+        -config.file ${mkConfigFile} \
+        -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        -log.level ${cfg.logLevel} \
+        ${optionalString (cfg.webExternalUrl != null) ''-web.external-url ${cfg.webExternalUrl} \''}
+        ${optionalString (cfg.logFormat != null) "-log.format ${cfg.logFormat}"}
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart  = "always";
+        PrivateTmp = true;
+        WorkingDirectory = "/tmp";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
new file mode 100644
index 00000000000..7a343299c31
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.blackboxExporter;
+in {
+  options = {
+    services.prometheus.blackboxExporter = {
+      enable = mkEnableOption "prometheus blackbox exporter";
+
+      configFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to configuration file.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 9115;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching the blackbox exporter.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    systemd.services.prometheus-blackbox-exporter = {
+      description = "Prometheus exporter for blackbox probes";
+      unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "nobody";
+        Restart = "always";
+        PrivateTmp = true;
+        WorkingDirectory = /tmp;
+        ExecStart = ''
+          ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
+            -web.listen-address :${toString cfg.port} \
+            -config.file ${cfg.configFile} \
+            ${concatStringsSep " \\\n  " cfg.extraFlags}
+        '';
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
new file mode 100644
index 00000000000..a07445ce167
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -0,0 +1,465 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus;
+  promUser = "prometheus";
+  promGroup = "prometheus";
+
+  # Get a submodule without any embedded metadata:
+  _filter = x: filterAttrs (k: v: k != "_module") x;
+
+  # Pretty-print JSON to a file
+  writePrettyJSON = name: x:
+    pkgs.runCommand name { } ''
+      echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out
+    '';
+
+  # This becomes the main config file
+  promConfig = {
+    global = cfg.globalConfig;
+    rule_files = cfg.ruleFiles ++ [
+      (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules))
+    ];
+    scrape_configs = cfg.scrapeConfigs;
+  };
+
+  generatedPrometheusYml = writePrettyJSON "prometheus.yml" promConfig;
+
+  prometheusYml =
+    if cfg.configText != null then
+      pkgs.writeText "prometheus.yml" cfg.configText
+    else generatedPrometheusYml;
+
+  cmdlineArgs = cfg.extraFlags ++ [
+    "-storage.local.path=${cfg.dataDir}/metrics"
+    "-config.file=${prometheusYml}"
+    "-web.listen-address=${cfg.listenAddress}"
+    "-alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
+    "-alertmanager.timeout=${toString cfg.alertmanagerTimeout}s"
+    (optionalString (cfg.alertmanagerURL != []) "-alertmanager.url=${concatStringsSep "," cfg.alertmanagerURL}")
+  ];
+
+  promTypes.globalConfig = types.submodule {
+    options = {
+      scrape_interval = mkOption {
+        type = types.str;
+        default = "1m";
+        description = ''
+          How frequently to scrape targets by default.
+        '';
+      };
+
+      scrape_timeout = mkOption {
+        type = types.str;
+        default = "10s";
+        description = ''
+          How long until a scrape request times out.
+        '';
+      };
+
+      evaluation_interval = mkOption {
+        type = types.str;
+        default = "1m";
+        description = ''
+          How frequently to evaluate rules by default.
+        '';
+      };
+
+      labels = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        description = ''
+          The labels to add to any timeseries that this Prometheus instance
+          scrapes.
+        '';
+      };
+    };
+  };
+
+  promTypes.scrape_config = types.submodule {
+    options = {
+      job_name = mkOption {
+        type = types.str;
+        description = ''
+          The job name assigned to scraped metrics by default.
+        '';
+      };
+      scrape_interval = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          How frequently to scrape targets from this job. Defaults to the
+          globally configured default.
+        '';
+      };
+      scrape_timeout = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Per-target timeout when scraping this job. Defaults to the
+          globally configured default.
+        '';
+      };
+      metrics_path = mkOption {
+        type = types.str;
+        default = "/metrics";
+        description = ''
+          The HTTP resource path on which to fetch metrics from targets.
+        '';
+      };
+      scheme = mkOption {
+        type = types.enum ["http" "https"];
+        default = "http";
+        description = ''
+          The URL scheme with which to fetch metrics from targets.
+        '';
+      };
+      basic_auth = mkOption {
+        type = types.nullOr (types.submodule {
+          options = {
+            username = mkOption {
+              type = types.str;
+              description = ''
+                HTTP username
+              '';
+            };
+            password = mkOption {
+              type = types.str;
+              description = ''
+                HTTP password
+              '';
+            };
+          };
+        });
+        default = null;
+        description = ''
+          Optional http login credentials for metrics scraping.
+        '';
+      };
+      dns_sd_configs = mkOption {
+        type = types.listOf promTypes.dns_sd_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          List of DNS service discovery configurations.
+        '';
+      };
+      consul_sd_configs = mkOption {
+        type = types.listOf promTypes.consul_sd_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          List of Consul service discovery configurations.
+        '';
+      };
+      file_sd_configs = mkOption {
+        type = types.listOf promTypes.file_sd_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          List of file service discovery configurations.
+        '';
+      };
+      static_configs = mkOption {
+        type = types.listOf promTypes.static_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          List of labeled target groups for this job.
+        '';
+      };
+      relabel_configs = mkOption {
+        type = types.listOf promTypes.relabel_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          List of relabel configurations.
+        '';
+      };
+    };
+  };
+
+  promTypes.static_config = types.submodule {
+    options = {
+      targets = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          The targets specified by the target group.
+        '';
+      };
+      labels = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        description = ''
+          Labels assigned to all metrics scraped from the targets.
+        '';
+      };
+    };
+  };
+
+  promTypes.dns_sd_config = types.submodule {
+    options = {
+      names = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          A list of DNS SRV record names to be queried.
+        '';
+      };
+      refresh_interval = mkOption {
+        type = types.str;
+        default = "30s";
+        description = ''
+          The time after which the provided names are refreshed.
+        '';
+      };
+    };
+  };
+
+  promTypes.consul_sd_config = types.submodule {
+    options = {
+      server = mkOption {
+        type = types.str;
+        description = "Consul server to query.";
+      };
+      token = mkOption {
+        type = types.nullOr types.str;
+        description = "Consul token";
+      };
+      datacenter = mkOption {
+        type = types.nullOr types.str;
+        description = "Consul datacenter";
+      };
+      scheme = mkOption {
+        type = types.nullOr types.str;
+        description = "Consul scheme";
+      };
+      username = mkOption {
+        type = types.nullOr types.str;
+        description = "Consul username";
+      };
+      password = mkOption {
+        type = types.nullOr types.str;
+        description = "Consul password";
+      };
+
+      services = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          A list of services for which targets are retrieved.
+        '';
+      };
+      tag_separator = mkOption {
+        type = types.str;
+        default = ",";
+        description = ''
+          The string by which Consul tags are joined into the tag label.
+        '';
+      };
+    };
+  };
+
+  promTypes.file_sd_config = types.submodule {
+    options = {
+      files = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          Patterns for files from which target groups are extracted. Refer
+          to the Prometheus documentation for permitted filename patterns
+          and formats.
+
+        '';
+      };
+      refresh_interval = mkOption {
+        type = types.str;
+        default = "30s";
+        description = ''
+          Refresh interval to re-read the files.
+        '';
+      };
+    };
+  };
+
+  promTypes.relabel_config = types.submodule {
+    options = {
+      source_labels = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          The source labels select values from existing labels. Their content
+          is concatenated using the configured separator and matched against
+          the configured regular expression.
+        '';
+      };
+      separator = mkOption {
+        type = types.str;
+        default = ";";
+        description = ''
+          Separator placed between concatenated source label values.
+        '';
+      };
+      target_label = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Label to which the resulting value is written in a replace action.
+          It is mandatory for replace actions.
+        '';
+      };
+      regex = mkOption {
+        type = types.str;
+        default = "(.*)";
+        description = ''
+          Regular expression against which the extracted value is matched.
+        '';
+      };
+      replacement = mkOption {
+        type = types.str;
+        default = "$1";
+        description = ''
+          Replacement value against which a regex replace is performed if the
+          regular expression matches.
+        '';
+      };
+      action = mkOption {
+        type = types.enum ["replace" "keep" "drop"];
+        default = "replace";
+        description = ''
+          Action to perform based on regex matching.
+        '';
+      };
+    };
+  };
+
+in {
+  options = {
+    services.prometheus = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the Prometheus monitoring daemon.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0:9090";
+        description = ''
+          Address to listen on for the web interface, API, and telemetry.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/prometheus";
+        description = ''
+          Directory to store Prometheus metrics data.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching Prometheus.
+        '';
+      };
+
+      configText = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          If non-null, this option defines the text that is written to
+          prometheus.yml. If null, the contents of prometheus.yml is generated
+          from the structured config options.
+        '';
+      };
+
+      globalConfig = mkOption {
+        type = promTypes.globalConfig;
+        default = {};
+        apply = _filter;
+        description = ''
+          Parameters that are valid in all  configuration contexts. They
+          also serve as defaults for other configuration sections
+        '';
+      };
+
+      rules = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Alerting and/or Recording rules to evaluate at runtime.
+        '';
+      };
+
+      ruleFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          Any additional rules files to include in this configuration.
+        '';
+      };
+
+      scrapeConfigs = mkOption {
+        type = types.listOf promTypes.scrape_config;
+        default = [];
+        apply = x: map _filter x;
+        description = ''
+          A list of scrape configurations.
+        '';
+      };
+
+      alertmanagerURL = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of Alertmanager URLs to send notifications to.
+        '';
+      };
+
+      alertmanagerNotificationQueueCapacity = mkOption {
+        type = types.int;
+        default = 10000;
+        description = ''
+          The capacity of the queue for pending alert manager notifications.
+        '';
+      };
+
+      alertmanagerTimeout = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Alert manager HTTP API timeout (in seconds).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups.${promGroup}.gid = config.ids.gids.prometheus;
+    users.extraUsers.${promUser} = {
+      description = "Prometheus daemon user";
+      uid = config.ids.uids.prometheus;
+      group = promGroup;
+      home = cfg.dataDir;
+      createHome = true;
+    };
+    systemd.services.prometheus = {
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" ];
+      script = ''
+        #!/bin/sh
+        exec ${pkgs.prometheus}/bin/prometheus \
+          ${concatStringsSep " \\\n  " cmdlineArgs}
+      '';
+      serviceConfig = {
+        User = promUser;
+        Restart  = "always";
+        WorkingDirectory = cfg.dataDir;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix
new file mode 100644
index 00000000000..6bc56df9834
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/json-exporter.nix
@@ -0,0 +1,74 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.jsonExporter;
+in {
+  options = {
+    services.prometheus.jsonExporter = {
+      enable = mkEnableOption "prometheus JSON exporter";
+
+      url = mkOption {
+        type = types.str;
+        description = ''
+          URL to scrape JSON from.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to configuration file.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 7979;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching the JSON exporter.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    systemd.services.prometheus-json-exporter = {
+      description = "Prometheus exporter for JSON over HTTP";
+      unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "nobody";
+        Restart = "always";
+        PrivateTmp = true;
+        WorkingDirectory = /tmp;
+        ExecStart = ''
+          ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
+            --port ${toString cfg.port} \
+            ${cfg.url} ${cfg.configFile} \
+            ${concatStringsSep " \\\n  " cfg.extraFlags}
+        '';
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
new file mode 100644
index 00000000000..1ccafee3b18
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.nginxExporter;
+in {
+  options = {
+    services.prometheus.nginxExporter = {
+      enable = mkEnableOption "prometheus nginx exporter";
+
+      port = mkOption {
+        type = types.int;
+        default = 9113;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.string;
+        default = "0.0.0.0";
+        description = ''
+          Address to listen on.
+        '';
+      };
+
+      scrapeUri = mkOption {
+        type = types.string;
+        default = "http://localhost/nginx_status";
+        description = ''
+          Address to access the nginx status page.
+          Can be enabled with services.nginx.statusPage = true.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching the nginx exporter.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    systemd.services.prometheus-nginx-exporter = {
+      after = [ "network.target" "nginx.service" ];
+      description = "Prometheus exporter for nginx metrics";
+      unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "nobody";
+        Restart  = "always";
+        PrivateTmp = true;
+        WorkingDirectory = /tmp;
+        ExecStart = ''
+          ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
+            -nginx.scrape_uri '${cfg.scrapeUri}' \
+            -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
+            ${concatStringsSep " \\\n  " cfg.extraFlags}
+        '';
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix
new file mode 100644
index 00000000000..0cf0b85afb5
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/node-exporter.nix
@@ -0,0 +1,81 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.nodeExporter;
+  cmdlineArgs = cfg.extraFlags ++ [
+    "-web.listen-address=${cfg.listenAddress}"
+  ];
+in {
+  options = {
+    services.prometheus.nodeExporter = {
+      enable = mkEnableOption "prometheus node exporter";
+
+      port = mkOption {
+        type = types.int;
+        default = 9100;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.string;
+        default = "0.0.0.0";
+        description = ''
+          Address to listen on.
+        '';
+      };
+
+      enabledCollectors = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = ''[ "systemd" ]'';
+        description = ''
+          Collectors to enable, additionally to the defaults.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching the node exporter.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    systemd.services.prometheus-node-exporter = {
+      description = "Prometheus exporter for machine metrics";
+      unitConfig.Documentation = "https://github.com/prometheus/node_exporter";
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \
+          ${optionalString (cfg.enabledCollectors != [])
+            ''-collectors.enabled ${concatStringsSep "," cfg.enabledCollectors}''} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+      serviceConfig = {
+        User = "nobody";
+        Restart  = "always";
+        PrivateTmp = true;
+        WorkingDirectory = /tmp;
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
new file mode 100644
index 00000000000..fe33f8c1f04
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
@@ -0,0 +1,127 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.snmpExporter;
+  mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath);
+in {
+  options = {
+    services.prometheus.snmpExporter = {
+      enable = mkEnableOption "Prometheus snmp exporter";
+
+      user = mkOption {
+        type = types.str;
+        default = "nobody";
+        description = ''
+          User name under which snmp exporter shall be run.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nogroup";
+        description = ''
+          Group under which snmp exporter shall be run.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 9116;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      listenAddress = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Address to listen on for web interface and telemetry.
+        '';
+      };
+
+      configurationPath = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
+        '';
+        example = "./snmp.yml";
+      };
+
+      configuration = mkOption {
+        type = types.nullOr types.attrs;
+        default = {};
+        description = ''
+          Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
+        '';
+        example = ''
+          {
+            "default" = {
+              "version" = 2;
+              "auth" = {
+                "community" = "public";
+              };
+            };
+          };
+        '';
+      };
+
+      logFormat = mkOption {
+        type = types.str;
+        default = "logger:stderr";
+        description = ''
+          Set the log target and format.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.enum ["debug" "info" "warn" "error" "fatal"];
+        default = "info";
+        description = ''
+          Only log messages with the given severity or above.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    assertions = singleton
+      {
+        assertion = (cfg.configurationPath == null) != (cfg.configuration == null);
+        message = "Please ensure you have either 'configuration' or 'configurationPath' set!";
+      };
+
+    systemd.services.prometheus-snmp-exporter = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      script = ''
+        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
+          -config.file ${mkConfigFile} \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port}
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart  = "always";
+        PrivateTmp = true;
+        WorkingDirectory = "/tmp";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
new file mode 100644
index 00000000000..143ebb62aea
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
@@ -0,0 +1,61 @@
+{ config, pkgs, lib, ... }:
+
+# Shamelessly cribbed from nginx-exporter.nix. ~ C.
+with lib;
+
+let
+  cfg = config.services.prometheus.varnishExporter;
+in {
+  options = {
+    services.prometheus.varnishExporter = {
+      enable = mkEnableOption "prometheus Varnish exporter";
+
+      port = mkOption {
+        type = types.int;
+        default = 9131;
+        description = ''
+          Port to listen on.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra commandline options when launching the Varnish exporter.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open port in firewall for incoming connections.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+
+    systemd.services.prometheus-varnish-exporter = {
+      description = "Prometheus exporter for Varnish metrics";
+      unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.varnish ];
+      script = ''
+        exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
+          -web.listen-address :${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+      serviceConfig = {
+        User = "nobody";
+        Restart = "always";
+        PrivateTmp = true;
+        WorkingDirectory = /tmp;
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/riemann-tools.nix b/nixos/modules/services/monitoring/riemann-tools.nix
index ce277f09464..de858813a76 100644
--- a/nixos/modules/services/monitoring/riemann-tools.nix
+++ b/nixos/modules/services/monitoring/riemann-tools.nix
@@ -50,6 +50,7 @@ in {
 
     systemd.services.riemann-health = {
       wantedBy = [ "multi-user.target" ];
+      path = [ procps ];
       serviceConfig = {
         User = "riemanntools";
         ExecStart = "${healthLauncher}/bin/riemann-health";
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index b67519b3424..99fd5c4d367 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -197,8 +197,7 @@ in
       devices = mkOption {
         default = [];
         example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
-        type = types.listOf types.optionSet;
-        options = [ smartdOpts ];
+        type = with types; listOf (submodule smartdOpts);
         description = "List of devices to monitor.";
       };
 
diff --git a/nixos/modules/services/monitoring/telegraf.nix b/nixos/modules/services/monitoring/telegraf.nix
new file mode 100644
index 00000000000..49dc9d8143e
--- /dev/null
+++ b/nixos/modules/services/monitoring/telegraf.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.telegraf;
+
+  configFile = pkgs.runCommand "config.toml" {
+    buildInputs = [ pkgs.remarshal ];
+  } ''
+    remarshal -if json -of toml \
+      < ${pkgs.writeText "config.json" (builtins.toJSON cfg.extraConfig)} \
+      > $out
+  '';
+in {
+  ###### interface
+  options = {
+    services.telegraf = {
+      enable = mkEnableOption "telegraf server";
+
+      package = mkOption {
+        default = pkgs.telegraf;
+        defaultText = "pkgs.telegraf";
+        description = "Which telegraf derivation to use";
+        type = types.package;
+      };
+
+      extraConfig = mkOption {
+        default = {};
+        description = "Extra configuration options for telegraf";
+        type = types.attrs;
+        example = {
+          outputs = {
+            influxdb = {
+              urls = ["http://localhost:8086"];
+              database = "telegraf";
+            };
+          };
+          inputs = {
+            statsd = {
+              service_address = ":8125";
+              delete_timings = true;
+            };
+          };
+        };
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.telegraf.enable {
+    systemd.services.telegraf = {
+      description = "Telegraf Agent";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        ExecStart=''${cfg.package}/bin/telegraf -config "${configFile}"'';
+        ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        User = "telegraf";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers = [{
+      name = "telegraf";
+      uid = config.ids.uids.telegraf;
+      description = "telegraf daemon user";
+    }];
+  };
+}
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index 5f80d547dbc..c4c4ed227b3 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -169,8 +169,7 @@ in
           monitoring directly.  These are usually attached to serial ports,
           but USB devices are also supported.
         '';
-        type = types.attrsOf types.optionSet;
-        options = [ upsOptions ];
+        type = with types; attrsOf (submodule upsOptions);
       };
 
     };
@@ -182,7 +181,8 @@ in
 
     systemd.services.upsmon = {
       description = "Uninterruptible Power Supplies (Monitor)";
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
       serviceConfig.Type = "forking";
       script = "${pkgs.nut}/sbin/upsmon";
       environment.NUT_CONFPATH = "/etc/nut/";
@@ -191,8 +191,8 @@ in
 
     systemd.services.upsd = {
       description = "Uninterruptible Power Supplies (Daemon)";
+      after = [ "network.target" "upsmon.service" ];
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" "upsmon.service" ];
       serviceConfig.Type = "forking";
       # TODO: replace 'root' by another username.
       script = "${pkgs.nut}/sbin/upsd -u root";
@@ -202,8 +202,8 @@ in
 
     systemd.services.upsdrv = {
       description = "Uninterruptible Power Supplies (Register all UPS)";
-      wantedBy = [ "multi-user.target" ];
       after = [ "upsd.service" ];
+      wantedBy = [ "multi-user.target" ];
       # TODO: replace 'root' by another username.
       script = ''${pkgs.nut}/bin/upsdrvctl -u root start'';
       serviceConfig = {
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
index a943075be0c..88a63b4bf16 100644
--- a/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -53,6 +53,7 @@ in
 
       extraConfig = mkOption {
         default = "";
+        type = types.lines;
         description = ''
           Configuration that is injected verbatim into the configuration file.
         '';
diff --git a/nixos/modules/services/network-filesystems/cachefilesd.nix b/nixos/modules/services/network-filesystems/cachefilesd.nix
new file mode 100644
index 00000000000..61981340840
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/cachefilesd.nix
@@ -0,0 +1,59 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.cachefilesd;
+
+  cfgFile = pkgs.writeText "cachefilesd.conf" ''
+    dir ${cfg.cacheDir}
+    ${cfg.extraConfig}
+  '';
+
+in
+
+{
+  options = {
+    services.cachefilesd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable cachefilesd network filesystems caching daemon.";
+      };
+
+      cacheDir = mkOption {
+        type = types.str;
+        default = "/var/cache/fscache";
+        description = "Directory to contain filesystem cache.";
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "brun 10%";
+        description = "Additional configuration file entries. See cachefilesd.conf(5) for more information.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.cachefilesd = {
+      description = "Local network file caching management daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.kmod pkgs.cachefilesd ];
+      script = ''
+        modprobe -qab cachefiles
+        mkdir -p ${cfg.cacheDir}
+        chmod 700 ${cfg.cacheDir}
+        exec cachefilesd -n -f ${cfgFile}
+      '';
+    };
+
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/drbd.nix b/nixos/modules/services/network-filesystems/drbd.nix
index 9896a93b189..57b1fbb597c 100644
--- a/nixos/modules/services/network-filesystems/drbd.nix
+++ b/nixos/modules/services/network-filesystems/drbd.nix
@@ -53,9 +53,9 @@ let cfg = config.services.drbd; in
       };
 
     systemd.services.drbd = {
-      after = [ "systemd-udev.settle.service" ];
+      after = [ "systemd-udev.settle.service" "network.target" ];
       wants = [ "systemd-udev.settle.service" ];
-      wantedBy = [ "ip-up.target" ];
+      wantedBy = [ "multi-user.target" ];
       script = ''
         ${pkgs.drbd}/sbin/drbdadm up all
       '';
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
new file mode 100644
index 00000000000..104b5b92620
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (pkgs) ipfs;
+
+  cfg = config.services.ipfs;
+
+  ipfsFlags = ''${if cfg.autoMigrate then "--migrate" else ""} ${if cfg.enableGC then "--enable-gc" else ""} ${toString cfg.extraFlags}'';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ipfs = {
+
+      enable = mkEnableOption "Interplanetary File System";
+
+      user = mkOption {
+        type = types.str;
+        default = "ipfs";
+        description = "User under which the IPFS daemon runs";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "ipfs";
+        description = "Group under which the IPFS daemon runs";
+      };
+
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/ipfs";
+        description = "The data dir for IPFS";
+      };
+
+      autoMigrate = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether IPFS should try to migrate the file system automatically.
+        '';
+      };
+
+      gatewayAddress = mkOption {
+        type = types.str;
+        default = "/ip4/127.0.0.1/tcp/8080";
+        description = "Where the IPFS Gateway can be reached";
+      };
+
+      apiAddress = mkOption {
+        type = types.str;
+        default = "/ip4/127.0.0.1/tcp/5001";
+        description = "Where IPFS exposes its API to";
+      };
+
+      enableGC = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable automatic garbage collection.
+        '';
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        description = "Extra flags passed to the IPFS daemon";
+        default = [];
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.ipfs ];
+
+    users.extraUsers = mkIf (cfg.user == "ipfs") {
+      ipfs = {
+        group = cfg.group;
+        home = cfg.dataDir;
+        createHome = false;
+        uid = config.ids.uids.ipfs;
+        description = "IPFS daemon user";
+      };
+    };
+
+    users.extraGroups = mkIf (cfg.group == "ipfs") {
+      ipfs = {
+        gid = config.ids.gids.ipfs;
+      };
+    };
+
+    systemd.services.ipfs = {
+      description = "IPFS Daemon";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "local-fs.target" ];
+      path  = [ pkgs.ipfs pkgs.su pkgs.bash ];
+
+      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}"
+        '';
+
+      serviceConfig = {
+        ExecStart = "${ipfs}/bin/ipfs daemon ${ipfsFlags}";
+        User = cfg.user;
+        Group = cfg.group;
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
index 891f41c8dcd..6f51e287910 100644
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix
@@ -75,7 +75,7 @@ in
     systemd.services.afsd = {
       description = "AFS client";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
       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 91f1a333be7..884966363b8 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -56,6 +56,7 @@ let
       serviceConfig = {
         ExecStart = "${samba}/sbin/${appName} ${args}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Type = "notify";
       };
 
       restartTriggers = [ configFile ];
@@ -167,12 +168,12 @@ in
         type = types.attrsOf (types.attrsOf types.unspecified);
         example =
           { public =
-             { path = "/srv/public";
-               "read only" = true;
-               browseable = "yes";
-               "guest ok" = "yes";
-                comment = "Public samba share.";
-             };
+            { path = "/srv/public";
+              "read only" = true;
+              browseable = "yes";
+              "guest ok" = "yes";
+              comment = "Public samba share.";
+            };
           };
       };
 
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index d4b6c05e943..ab9eac3829f 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -8,148 +8,189 @@ in
     options.services.tahoe = {
       introducers = mkOption {
         default = {};
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule {
+          options = {
+            nickname = mkOption {
+              type = types.str;
+              description = ''
+                The nickname of this Tahoe introducer.
+              '';
+            };
+            tub.port = mkOption {
+              default = 3458;
+              type = types.int;
+              description = ''
+                The port on which the introducer will listen.
+              '';
+            };
+            tub.location = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The external location that the introducer should listen on.
+
+                If specified, the port should be included.
+              '';
+            };
+            package = mkOption {
+              default = pkgs.tahoelafs;
+              defaultText = "pkgs.tahoelafs";
+              type = types.package;
+              example = literalExample "pkgs.tahoelafs";
+              description = ''
+                The package to use for the Tahoe LAFS daemon.
+              '';
+            };
+          };
+        });
         description = ''
           The Tahoe introducers.
         '';
-        options = {
-          nickname = mkOption {
-            type = types.str;
-            description = ''
-              The nickname of this Tahoe introducer.
-            '';
-          };
-          tub.port = mkOption {
-            default = 3458;
-            type = types.int;
-            description = ''
-              The port on which the introducer will listen.
-            '';
-          };
-          tub.location = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The external location that the introducer should listen on.
-
-              If specified, the port should be included.
-            '';
-          };
-          package = mkOption {
-            default = pkgs.tahoelafs;
-            defaultText = "pkgs.tahoelafs";
-            type = types.package;
-            example = literalExample "pkgs.tahoelafs";
-            description = ''
-              The package to use for the Tahoe LAFS daemon.
-            '';
-          };
-        };
       };
       nodes = mkOption {
         default = {};
-        type = types.loaOf types.optionSet;
-        description = ''
-          The Tahoe nodes.
-        '';
-        options = {
-          nickname = mkOption {
-            type = types.str;
-            description = ''
-              The nickname of this Tahoe node.
-            '';
-          };
-          tub.port = mkOption {
-            default = 3457;
-            type = types.int;
-            description = ''
-              The port on which the tub will listen.
+        type = with types; loaOf (submodule {
+          options = {
+            nickname = mkOption {
+              type = types.str;
+              description = ''
+                The nickname of this Tahoe node.
+              '';
+            };
+            tub.port = mkOption {
+              default = 3457;
+              type = types.int;
+              description = ''
+                The port on which the tub will listen.
 
-              This is the correct setting to tweak if you want Tahoe's storage
-              system to listen on a different port.
-            '';
-          };
-          tub.location = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The external location that the node should listen on.
+                This is the correct setting to tweak if you want Tahoe's storage
+                system to listen on a different port.
+              '';
+            };
+            tub.location = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The external location that the node should listen on.
 
-              This is the setting to tweak if there are multiple interfaces
-              and you want to alter which interface Tahoe is advertising.
+                This is the setting to tweak if there are multiple interfaces
+                and you want to alter which interface Tahoe is advertising.
 
-              If specified, the port should be included.
-            '';
-          };
-          web.port = mkOption {
-            default = 3456;
-            type = types.int;
-            description = ''
-              The port on which the Web server will listen.
+                If specified, the port should be included.
+              '';
+            };
+            web.port = mkOption {
+              default = 3456;
+              type = types.int;
+              description = ''
+                The port on which the Web server will listen.
 
-              This is the correct setting to tweak if you want Tahoe's WUI to
-              listen on a different port.
-            '';
-          };
-          client.introducer = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The furl for a Tahoe introducer node.
+                This is the correct setting to tweak if you want Tahoe's WUI to
+                listen on a different port.
+              '';
+            };
+            client.introducer = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The furl for a Tahoe introducer node.
 
-              Like all furls, keep this safe and don't share it.
-            '';
-          };
-          client.helper = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The furl for a Tahoe helper node.
+                Like all furls, keep this safe and don't share it.
+              '';
+            };
+            client.helper = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The furl for a Tahoe helper node.
 
-              Like all furls, keep this safe and don't share it.
-            '';
-          };
-          client.shares.needed = mkOption {
-            default = 3;
-            type = types.int;
-            description = ''
-              The number of shares required to reconstitute a file.
-            '';
-          };
-          client.shares.happy = mkOption {
-            default = 7;
-            type = types.int;
-            description = ''
-              The number of distinct storage nodes required to store
-              a file.
-            '';
-          };
-          client.shares.total = mkOption {
-            default = 10;
-            type = types.int;
-            description = ''
-              The number of shares required to store a file.
-            '';
-          };
-          storage.enable = mkEnableOption "storage service";
-          storage.reservedSpace = mkOption {
-            default = "1G";
-            type = types.str;
-            description = ''
-              The amount of filesystem space to not use for storage.
-            '';
-          };
-          helper.enable = mkEnableOption "helper service";
-          package = mkOption {
-            default = pkgs.tahoelafs;
-            defaultText = "pkgs.tahoelafs";
-            type = types.package;
-            example = literalExample "pkgs.tahoelafs";
-            description = ''
-              The package to use for the Tahoe LAFS daemon.
-            '';
+                Like all furls, keep this safe and don't share it.
+              '';
+            };
+            client.shares.needed = mkOption {
+              default = 3;
+              type = types.int;
+              description = ''
+                The number of shares required to reconstitute a file.
+              '';
+            };
+            client.shares.happy = mkOption {
+              default = 7;
+              type = types.int;
+              description = ''
+                The number of distinct storage nodes required to store
+                a file.
+              '';
+            };
+            client.shares.total = mkOption {
+              default = 10;
+              type = types.int;
+              description = ''
+                The number of shares required to store a file.
+              '';
+            };
+            storage.enable = mkEnableOption "storage service";
+            storage.reservedSpace = mkOption {
+              default = "1G";
+              type = types.str;
+              description = ''
+                The amount of filesystem space to not use for storage.
+              '';
+            };
+            helper.enable = mkEnableOption "helper service";
+            sftpd.enable = mkEnableOption "SFTP service";
+            sftpd.port = mkOption {
+              default = null;
+              type = types.nullOr types.int;
+              description = ''
+                The port on which the SFTP server will listen.
+
+                This is the correct setting to tweak if you want Tahoe's SFTP
+                daemon to listen on a different port.
+              '';
+            };
+            sftpd.hostPublicKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path to the SSH host public key.
+              '';
+            };
+            sftpd.hostPrivateKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path to the SSH host private key.
+              '';
+            };
+            sftpd.accounts.file = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path to the accounts file.
+              '';
+            };
+            sftpd.accounts.url = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                URL of the accounts server.
+              '';
+            };
+            package = mkOption {
+              default = pkgs.tahoelafs;
+              defaultText = "pkgs.tahoelafs";
+              type = types.package;
+              example = literalExample "pkgs.tahoelafs";
+              description = ''
+                The package to use for the Tahoe LAFS daemon.
+              '';
+            };
           };
-        };
+        });
+        description = ''
+          The Tahoe nodes.
+        '';
       };
     };
     config = mkMerge [
@@ -192,6 +233,12 @@ in
             serviceConfig = {
               Type = "simple";
               PIDFile = pidfile;
+              # Believe it or not, Tahoe is very brittle about the order of
+              # arguments to $(tahoe start). The node directory must come first,
+              # and arguments which alter Twisted's behavior come afterwards.
+              ExecStart = ''
+                ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
+              '';
             };
             preStart = ''
               if [ \! -d ${nodedir} ]; then
@@ -207,12 +254,6 @@ in
               # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
               cp /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
             '';
-            # Believe it or not, Tahoe is very brittle about the order of
-            # arguments to $(tahoe start). The node directory must come first,
-            # and arguments which alter Twisted's behavior come afterwards.
-            script = ''
-              tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
-            '';
           });
         users.extraUsers = flip mapAttrs' cfg.introducers (node: _:
           nameValuePair "tahoe.introducer-${node}" {
@@ -254,6 +295,19 @@ in
 
                 [helper]
                 enabled = ${if settings.helper.enable then "true" else "false"}
+
+                [sftpd]
+                enabled = ${if settings.sftpd.enable then "true" else "false"}
+                ${optionalString (settings.sftpd.port != null)
+                  "port = ${toString settings.sftpd.port}"}
+                ${optionalString (settings.sftpd.hostPublicKeyFile != null)
+                  "host_pubkey_file = ${settings.sftpd.hostPublicKeyFile}"}
+                ${optionalString (settings.sftpd.hostPrivateKeyFile != null)
+                  "host_privkey_file = ${settings.sftpd.hostPrivateKeyFile}"}
+                ${optionalString (settings.sftpd.accounts.file != null)
+                  "accounts.file = ${settings.sftpd.accounts.file}"}
+                ${optionalString (settings.sftpd.accounts.url != null)
+                  "accounts.url = ${settings.sftpd.accounts.url}"}
               '';
             });
           # Actually require Tahoe, so that we will have it installed.
@@ -279,6 +333,12 @@ in
             serviceConfig = {
               Type = "simple";
               PIDFile = pidfile;
+              # Believe it or not, Tahoe is very brittle about the order of
+              # arguments to $(tahoe start). The node directory must come first,
+              # and arguments which alter Twisted's behavior come afterwards.
+              ExecStart = ''
+                ${settings.package}/bin/tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
+              '';
             };
             preStart = ''
               if [ \! -d ${nodedir} ]; then
@@ -294,12 +354,6 @@ in
               # ln -s /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
               cp /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
             '';
-            # Believe it or not, Tahoe is very brittle about the order of
-            # arguments to $(tahoe start). The node directory must come first,
-            # and arguments which alter Twisted's behavior come afterwards.
-            script = ''
-              tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
-            '';
           });
         users.extraUsers = flip mapAttrs' cfg.nodes (node: _:
           nameValuePair "tahoe.${node}" {
diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix
index b051214e1d0..0c6714563d8 100644
--- a/nixos/modules/services/network-filesystems/xtreemfs.nix
+++ b/nixos/modules/services/network-filesystems/xtreemfs.nix
@@ -153,6 +153,7 @@ in
           '';
         };
         extraConfig = mkOption {
+          type = types.lines;
           default = "";
           example = ''
             # specify whether SSL is required
@@ -173,6 +174,7 @@ in
         replication = {
           enable = mkEnableOption "XtreemFS DIR replication plugin";
           extraConfig = mkOption {
+            type = types.lines;
             example = ''
               # participants of the replication including this replica
               babudb.repl.participant.0 = 192.168.0.10
@@ -269,6 +271,7 @@ in
           '';
         };
         extraConfig = mkOption {
+          type = types.lines;
           example = ''
             osd_check_interval = 300
             no_atime = true
@@ -307,6 +310,7 @@ in
         replication = {
           enable = mkEnableOption "XtreemFS MRC replication plugin";
           extraConfig = mkOption {
+            type = types.lines;
             example = ''
               # participants of the replication including this replica
               babudb.repl.participant.0 = 192.168.0.10
@@ -385,6 +389,7 @@ in
           '';
         };
         extraConfig = mkOption {
+          type = types.lines;
           example = ''
             local_clock_renewal = 0
             remote_time_sync = 30000
diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix
index 982b6ca5ea7..4de20664133 100644
--- a/nixos/modules/services/network-filesystems/yandex-disk.nix
+++ b/nixos/modules/services/network-filesystems/yandex-disk.nix
@@ -55,6 +55,15 @@ in
         description = "The directory to use for Yandex.Disk storage";
       };
 
+      excludes = mkOption {
+        default = "";
+        type = types.string;
+        example = "data,backup";
+        description = ''
+          Comma-separated list of directories which are excluded from synchronization.
+        '';
+      };
+
     };
 
   };
@@ -86,7 +95,7 @@ in
         chown ${u} ${dir}
 
         if ! test -d "${cfg.directory}" ; then
-          mkdir -p -m 755 ${cfg.directory} ||
+          (mkdir -p -m 755 ${cfg.directory} && chown ${u} ${cfg.directory}) ||
             exit 1
         fi
 
@@ -94,7 +103,7 @@ in
           -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token'
 
         ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
-          -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory}'
+          -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}'
       '';
 
     };
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
index bc488d0e910..fc7d56a24fa 100644
--- a/nixos/modules/services/networking/amuled.nix
+++ b/nixos/modules/services/networking/amuled.nix
@@ -59,7 +59,8 @@ in
 
     systemd.services.amuled = {
       description = "AMule daemon";
-      wantedBy = [ "ip-up.target" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
 
       preStart = ''
         mkdir -p ${cfg.dataDir}
diff --git a/nixos/modules/services/networking/asterisk.nix b/nixos/modules/services/networking/asterisk.nix
index 13617a1b6c5..5c71a1d8dda 100644
--- a/nixos/modules/services/networking/asterisk.nix
+++ b/nixos/modules/services/networking/asterisk.nix
@@ -6,29 +6,38 @@ let
   cfg = config.services.asterisk;
 
   asteriskUser = "asterisk";
+  asteriskGroup = "asterisk";
 
   varlibdir = "/var/lib/asterisk";
   spooldir = "/var/spool/asterisk";
   logdir = "/var/log/asterisk";
 
+  # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override
+  defaultConfFiles = subtractLists (attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles;
+  allConfFiles =
+    cfg.confFiles //
+    builtins.listToAttrs (map (x: { name = x;
+                                    value = builtins.readFile (pkgs.asterisk + "/etc/asterisk/" + x); })
+                              defaultConfFiles);
+
   asteriskEtc = pkgs.stdenv.mkDerivation
   ((mapAttrs' (name: value: nameValuePair
         # Fudge the names to make bash happy
         ((replaceChars ["."] ["_"] name) + "_")
         (value)
-      ) cfg.confFiles) //
+      ) allConfFiles) //
   {
     confFilesString = concatStringsSep " " (
-      attrNames cfg.confFiles
+      attrNames allConfFiles
     );
 
-    name = "asterisk.etc";
+    name = "asterisk-etc";
 
     # Default asterisk.conf file
     # (Notice that astetcdir will be set to the path of this derivation)
     asteriskConf = ''
       [directories]
-      astetcdir => @out@
+      astetcdir => /etc/asterisk
       astmoddir => ${pkgs.asterisk}/lib/asterisk/modules
       astvarlibdir => /var/lib/asterisk
       astdbdir => /var/lib/asterisk
@@ -169,6 +178,16 @@ in
         '';
       };
 
+      useTheseDefaultConfFiles = mkOption {
+        default = [ "ari.conf" "acl.conf" "agents.conf" "amd.conf" "calendar.conf" "cdr.conf" "cdr_syslog.conf" "cdr_custom.conf" "cel.conf" "cel_custom.conf" "cli_aliases.conf" "confbridge.conf" "dundi.conf" "features.conf" "hep.conf" "iax.conf" "pjsip.conf" "pjsip_wizard.conf" "phone.conf" "phoneprov.conf" "queues.conf" "res_config_sqlite3.conf" "res_parking.conf" "statsd.conf" "udptl.conf" "unistim.conf" ];
+        type = types.listOf types.str;
+        example = [ "sip.conf" "dundi.conf" ];
+        description = ''Sets these config files to the default content. The default value for
+          this option contains all necesscary files to avoid errors at startup.
+          This does not override settings via <option>services.asterisk.confFiles</option>.
+        '';
+      };
+
       extraArguments = mkOption {
         default = [];
         type = types.listOf types.str;
@@ -182,12 +201,22 @@ in
   };
 
   config = mkIf cfg.enable {
-    users.extraUsers = singleton
-    { name = asteriskUser;
-      uid = config.ids.uids.asterisk;
-      description = "Asterisk daemon user";
-      home = varlibdir;
-    };
+    environment.systemPackages = [ pkgs.asterisk ];
+
+    environment.etc.asterisk.source = asteriskEtc;
+
+    users.extraUsers.asterisk =
+      { name = asteriskUser;
+        group = asteriskGroup;
+        uid = config.ids.uids.asterisk;
+        description = "Asterisk daemon user";
+        home = varlibdir;
+      };
+
+    users.extraGroups.asterisk =
+      { name = asteriskGroup;
+        gid = config.ids.gids.asterisk;
+      };
 
     systemd.services.asterisk = {
       description = ''
@@ -196,14 +225,17 @@ in
 
       wantedBy = [ "multi-user.target" ];
 
+      # Do not restart, to avoid disruption of running calls. Restart unit by yourself!
+      restartIfChanged = false;
+
       preStart = ''
         # Copy skeleton directory tree to /var
         for d in '${varlibdir}' '${spooldir}' '${logdir}'; do
           # TODO: Make exceptions for /var directories that likely should be updated
           if [ ! -e "$d" ]; then
             mkdir -p "$d"
-            cp --recursive ${pkgs.asterisk}/"$d" "$d"
-            chown --recursive ${asteriskUser} "$d"
+            cp --recursive ${pkgs.asterisk}/"$d"/* "$d"/
+            chown --recursive ${asteriskUser}:${asteriskGroup} "$d"
             find "$d" -type d | xargs chmod 0755
           fi
         done
@@ -215,7 +247,9 @@ in
             # FIXME: This doesn't account for arguments with spaces
             argString = concatStringsSep " " cfg.extraArguments;
           in
-          "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C ${asteriskEtc}/asterisk.conf ${argString} -F";
+          "${pkgs.asterisk}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F";
+        ExecReload = ''${pkgs.asterisk}/bin/asterisk -x "core reload"
+          '';
         Type = "forking";
         PIDFile = "/var/run/asterisk/asterisk.pid";
       };
diff --git a/nixos/modules/services/networking/atftpd.nix b/nixos/modules/services/networking/atftpd.nix
index d875ddc6352..e7fd48c99a8 100644
--- a/nixos/modules/services/networking/atftpd.nix
+++ b/nixos/modules/services/networking/atftpd.nix
@@ -20,13 +20,27 @@ in
         default = false;
         type = types.bool;
         description = ''
-          Whenever to enable the atftpd TFTP server.
+          Whether to enable the atftpd TFTP server. By default, the server
+          binds to address 0.0.0.0.
+        '';
+      };
+
+      extraOptions = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = literalExample ''
+          [ "--bind-address 192.168.9.1"
+            "--verbose=7"
+          ]
+        '';
+        description = ''
+          Extra command line arguments to pass to atftp.
         '';
       };
 
       root = mkOption {
-        default = "/var/empty";
-        type = types.str;
+        default = "/srv/tftp";
+        type = types.path;
         description = ''
           Document root directory for the atftpd.
         '';
@@ -39,11 +53,11 @@ in
   config = mkIf cfg.enable {
 
     systemd.services.atftpd = {
-      description = "atftpd TFTP server";
+      description = "TFTP Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       # runs as nobody
-      serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address 0.0.0.0 ${cfg.root}";
+      serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork ${lib.concatStringsSep " " cfg.extraOptions} ${cfg.root}";
     };
 
   };
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 7650f45c557..6a786e75bbc 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -7,10 +7,6 @@ let
 
   cfg = config.services.avahi;
 
-  # We must escape interfaces due to the systemd interpretation
-  subsystemDevice = interface:
-    "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device";
-
   avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
     [server]
     ${# Users can set `networking.hostName' to the empty string, when getting
@@ -75,7 +71,8 @@ in
       };
 
       browseDomains = mkOption {
-        default = [ "0pointer.de" "zeroconf.org" ];
+        default = [ ];
+        example = [ "0pointer.de" "zeroconf.org" ];
         description = ''
           List of non-local DNS domains to be browsed.
         '';
@@ -178,17 +175,20 @@ in
 
     environment.systemPackages = [ pkgs.avahi ];
 
+    systemd.sockets.avahi-daemon =
+      { description = "Avahi mDNS/DNS-SD Stack Activation Socket";
+        listenStreams = [ "/var/run/avahi-daemon/socket" ];
+        wantedBy = [ "sockets.target" ];
+      };
+
     systemd.services.avahi-daemon =
-      let
-        deps = optionals (cfg.interfaces!=null) (map subsystemDevice cfg.interfaces);
-      in
-      { description = "Avahi daemon";
-        wantedBy = [ "ip-up.target" ];
-        bindsTo = deps;
-        after = deps;
-        before = [ "ip-up.target" ];
-        # Receive restart event after resume
-        partOf = [ "post-resume.target" ];
+      { description = "Avahi mDNS/DNS-SD Stack";
+        wantedBy = [ "multi-user.target" ];
+        requires = [ "avahi-daemon.socket" ];
+
+        serviceConfig."NotifyAccess" = "main";
+        serviceConfig."BusName" = "org.freedesktop.Avahi";
+        serviceConfig."Type" = "dbus";
 
         path = [ pkgs.coreutils pkgs.avahi ];
 
diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix
index 08afafceff2..0272b6ceff2 100644
--- a/nixos/modules/services/networking/bind.nix
+++ b/nixos/modules/services/networking/bind.nix
@@ -113,6 +113,7 @@ in
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "";
         description = "
           Extra lines to be added verbatim to the generated named configuration file.
@@ -145,8 +146,8 @@ in
       };
 
     systemd.services.bind = {
-      description = "BIND name server job";
-      after = [ "network-interfaces.target" ];
+      description = "BIND Domain Name Server";
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
       preStart = ''
@@ -154,7 +155,8 @@ in
         chown ${bindUser} /var/run/named
       '';
 
-      script = "${pkgs.bind.bin}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
+      script = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
+      unitConfig.Documentation = "man:named(8)";
     };
   };
 }
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index e76cdac14ca..1a7a1e24b70 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -1,76 +1,69 @@
 { config, lib, pkgs, ... }:
 
 let
-  inherit (lib) mkEnableOption mkIf mkOption singleton types;
-  inherit (pkgs) bird;
-  cfg = config.services.bird;
-
-  configFile = pkgs.writeText "bird.conf" ''
-    ${cfg.config}
-  '';
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.bird = {
-
-      enable = mkEnableOption "BIRD Internet Routing Daemon";
-
-      config = mkOption {
-        type = types.string;
-        description = ''
-          BIRD Internet Routing Daemon configuration file.
-          <link xlink:href='http://bird.network.cz/'/>
+  inherit (lib) mkEnableOption mkIf mkOption types;
+
+  generic = variant:
+    let
+      cfg = config.services.${variant};
+      pkg = pkgs.${variant};
+      birdc = if variant == "bird6" then "birdc6" else "birdc";
+      configFile = pkgs.stdenv.mkDerivation {
+        name = "${variant}.conf";
+        text = cfg.config;
+        preferLocalBuild = true;
+        buildCommand = ''
+          echo -n "$text" > $out
+          ${pkg}/bin/${variant} -d -p -c $out
         '';
       };
-
-      user = mkOption {
-        type = types.string;
-        default = "bird";
-        description = ''
-          BIRD Internet Routing Daemon user.
-        '';
+    in {
+      ###### interface
+      options = {
+        services.${variant} = {
+          enable = mkEnableOption "BIRD Internet Routing Daemon";
+          config = mkOption {
+            type = types.lines;
+            description = ''
+              BIRD Internet Routing Daemon configuration file.
+              <link xlink:href='http://bird.network.cz/'/>
+            '';
+          };
+        };
       };
 
-      group = mkOption {
-        type = types.string;
-        default = "bird";
-        description = ''
-          BIRD Internet Routing Daemon group.
-        '';
+      ###### implementation
+      config = mkIf cfg.enable {
+        environment.systemPackages = [ pkg ];
+        systemd.services.${variant} = {
+          description = "BIRD Internet Routing Daemon";
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            Type = "forking";
+            Restart = "on-failure";
+            ExecStart = "${pkg}/bin/${variant} -c ${configFile} -u ${variant} -g ${variant}";
+            ExecReload = "${pkg}/bin/${birdc} configure";
+            ExecStop = "${pkg}/bin/${birdc} down";
+            CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID"
+                                      # see bird/sysdep/linux/syspriv.h
+                                      "CAP_NET_BIND_SERVICE" "CAP_NET_BROADCAST" "CAP_NET_ADMIN" "CAP_NET_RAW" ];
+            ProtectSystem = "full";
+            ProtectHome = "yes";
+            SystemCallFilter="~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io";
+            MemoryDenyWriteExecute = "yes";
+          };
+        };
+        users = {
+          extraUsers.${variant} = {
+            description = "BIRD Internet Routing Daemon user";
+            group = "${variant}";
+          };
+          extraGroups.${variant} = {};
+        };
       };
-
     };
 
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    users.extraUsers = singleton {
-      name = cfg.user;
-      description = "BIRD Internet Routing Daemon user";
-      uid = config.ids.uids.bird;
-      group = cfg.group;
-    };
-
-    users.extraGroups = singleton {
-      name = cfg.group;
-      gid = config.ids.gids.bird;
-    };
-
-    systemd.services.bird = {
-      description = "BIRD Internet Routing Daemon";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        ExecStart   = "${bird}/bin/bird -d -c ${configFile} -s /var/run/bird.ctl -u ${cfg.user} -g ${cfg.group}";
-      };
-    };
-  };
+  inherit (config.services) bird bird6;
+in {
+  imports = [(generic "bird") (generic "bird6")];
 }
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 5e6847097a9..e72ea20ccce 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -7,11 +7,6 @@ let
   cfg = config.services.bitlbee;
   bitlbeeUid = config.ids.uids.bitlbee;
 
-  authModeCheck = v:
-    v == "Open" ||
-    v == "Closed" ||
-    v == "Registered";
-
   bitlbeeConfig = pkgs.writeText "bitlbee.conf"
     ''
     [settings]
@@ -67,7 +62,7 @@ in
 
       authMode = mkOption {
         default = "Open";
-        type = types.addCheck types.str authModeCheck;
+        type = types.enum [ "Open" "Closed" "Registered" ];
         description = ''
           The following authentication modes are available:
             Open -- Accept connections from anyone, use NickServ for user authentication.
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
index a38142b4a08..f2ff11633b1 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/chrony.nix
@@ -31,7 +31,7 @@ in
       };
 
       servers = mkOption {
-        default = config.services.ntp.servers;
+        default = config.networking.timeServers;
         description = ''
           The set of NTP servers from which to synchronise.
         '';
@@ -51,6 +51,7 @@ in
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "";
         description = ''
           Extra configuration directives that should be added to
@@ -101,7 +102,7 @@ in
         home = stateDir;
       };
 
-    systemd.services.ntpd.enable = mkForce false;
+    systemd.services.timesyncd.enable = mkForce false;
 
     systemd.services.chronyd =
       { description = "chrony NTP daemon";
diff --git a/nixos/modules/services/networking/cjdns-hosts.sh b/nixos/modules/services/networking/cjdns-hosts.sh
deleted file mode 100644
index 8a2b47e5214..00000000000
--- a/nixos/modules/services/networking/cjdns-hosts.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-pubs=($pubs)
-hosts=($hosts)
-
-lines="''\n"
-for ((i = 0; i < ${#pubs[*]}; i++)); do
-    addr=$($cjdns/bin/publictoip6 ${pubs[i]})
-    lines="${lines}$addr ${hosts[i]}\n"
-done
-lines="${lines}''"
-
-echo -ne $lines > $out
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 0495b32c6fa..a10851c1652 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -28,21 +28,18 @@ let
     };
   };
 
-  peers = mapAttrsToList (n: v: v) (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo);
-
-  pubs  = toString (map (p: if p.hostname == "" then "" else p.publicKey) peers);
-  hosts = toString (map (p: if p.hostname == "" then "" else p.hostname)  peers);
-
-  cjdnsHosts =
-    if hosts != "" then
-      import (pkgs.stdenv.mkDerivation {
-        name = "cjdns-hosts";
-        builder = ./cjdns-hosts.sh;
-
-        inherit (pkgs) cjdns;
-        inherit pubs hosts;
-      })
-    else "";
+  # Additional /etc/hosts entries for peers with an associated hostname
+  cjdnsExtraHosts = import (pkgs.runCommand "cjdns-hosts" {}
+    # Generate a builder that produces an output usable as a Nix string value
+    ''
+      exec >$out
+      echo \'\'
+      ${concatStringsSep "\n" (mapAttrsToList (k: v:
+          optionalString (v.hostname != "")
+            "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}")
+          (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))}
+      echo \'\'
+    '');
 
   parseModules = x:
     x // { connectTo = mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; };
@@ -95,8 +92,8 @@ in
       };
 
       confFile = mkOption {
-        type = types.str;
-        default = "";
+        type = types.nullOr types.path;
+        default = null;
         example = "/etc/cjdroute.conf";
         description = ''
           Ignore all other cjdns options and load configuration from this file.
@@ -112,14 +109,14 @@ in
           "49275fut6tmzu354pq70sr5b95qq0vj"
         ];
         description = ''
-          Any remote cjdns nodes that offer these passwords on 
+          Any remote cjdns nodes that offer these passwords on
           connection will be allowed to route through this node.
         '';
       };
-    
+
       admin = {
         bind = mkOption {
-          type = types.string;
+          type = types.str;
           default = "127.0.0.1:11234";
           description = ''
             Bind the administration port to this address and port.
@@ -129,7 +126,7 @@ in
 
       UDPInterface = {
         bind = mkOption {
-          type = types.string;
+          type = types.str;
           default = "";
           example = "192.168.1.32:43211";
           description = ''
@@ -154,6 +151,7 @@ in
 
       ETHInterface = {
         bind = mkOption {
+          type = types.str;
           default = "";
           example = "eth0";
           description =
@@ -197,22 +195,33 @@ in
         };
       };
 
+      addExtraHosts = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add cjdns peers with an associated hostname to
+          <filename>/etc/hosts</filename>.  Beware that enabling this
+          incurs heavy eval-time costs.
+        '';
+      };
+
     };
 
   };
 
-  config = mkIf config.services.cjdns.enable {
+  config = mkIf cfg.enable {
 
     boot.kernelModules = [ "tun" ];
 
     # networking.firewall.allowedUDPPorts = ...
 
     systemd.services.cjdns = {
-      description = "encrypted networking for everybody";
-      wantedBy = [ "network.target" ];
-      after = [ "networkSetup.service" "network-interfaces.target" ];
+      description = "cjdns: routing engine designed for security, scalability, speed and ease of use";
+      wantedBy = [ "multi-user.target" "sleep.target"];
+      after = [ "network-online.target" ];
+      bindsTo = [ "network-online.target" ];
 
-      preStart = if cfg.confFile != "" then "" else ''
+      preStart = if cfg.confFile != null then "" else ''
         [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys
 
         if [ -z "$CJDNS_PRIVATE_KEY" ]; then
@@ -228,13 +237,13 @@ in
         fi
 
         if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
-            echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \
+            echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 96)" \
                 >> /etc/cjdns.keys
         fi
       '';
 
       script = (
-        if cfg.confFile != "" then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
+        if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else
           ''
             source /etc/cjdns.keys
             echo '${cjdrouteConf}' | sed \
@@ -246,14 +255,22 @@ in
 
       serviceConfig = {
         Type = "forking";
-        Restart = "on-failure";
+        Restart = "always";
+        StartLimitInterval = 0;
+        RestartSec = 1;
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW";
+        AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW";
+        ProtectSystem = "full";
+        MemoryDenyWriteExecute = true;
+        ProtectHome = true;
+        PrivateTmp = true;
       };
     };
 
-    networking.extraHosts = "${cjdnsHosts}";
+    networking.extraHosts = mkIf cfg.addExtraHosts cjdnsExtraHosts;
 
     assertions = [
-      { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != "" );
+      { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null );
         message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
       }
       { assertion = config.networking.enableIPv6;
diff --git a/nixos/modules/services/networking/cntlm.nix b/nixos/modules/services/networking/cntlm.nix
index 76c0fd7d0ea..890ff508407 100644
--- a/nixos/modules/services/networking/cntlm.nix
+++ b/nixos/modules/services/networking/cntlm.nix
@@ -61,6 +61,7 @@ in
       };
 
      extraConfig = mkOption {
+        type = types.lines;
         default = "";
         description = "Verbatim contents of <filename>cntlm.conf</filename>.";
      };
diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix
index 3fecfbb13a0..d0683b87780 100644
--- a/nixos/modules/services/networking/connman.nix
+++ b/nixos/modules/services/networking/connman.nix
@@ -27,6 +27,14 @@ in {
         '';
       };
 
+      enableVPN = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable ConnMan VPN service.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = ''
@@ -78,7 +86,7 @@ in {
       };
     };
 
-    systemd.services."connman-vpn" = {
+    systemd.services."connman-vpn" = mkIf cfg.enableVPN {
       description = "ConnMan VPN service";
       wantedBy = [ "multi-user.target" ];
       after = [ "syslog.target" ];
@@ -91,7 +99,7 @@ in {
       };
     };
 
-    systemd.services."net-connman-vpn" = {
+    systemd.services."net-connman-vpn" = mkIf cfg.enableVPN {
       description = "D-BUS Service";
       serviceConfig = {
         Name = "net.connman.vpn";
diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix
new file mode 100644
index 00000000000..a9a77f3412a
--- /dev/null
+++ b/nixos/modules/services/networking/dante.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg = config.services.dante;
+  confFile = pkgs.writeText "dante-sockd.conf" ''
+    user.privileged: root
+    user.unprivileged: dante
+
+    ${cfg.config}
+  '';
+in
+
+{
+  meta = {
+    maintainers = with maintainers; [ arobyn ];
+  };
+
+  options = {
+    services.dante = {
+      enable = mkEnableOption "Dante SOCKS proxy";
+
+      config = mkOption {
+        default     = null;
+        type        = types.nullOr types.str;
+        description = ''
+          Contents of Dante's configuration file
+          NOTE: user.privileged/user.unprivileged are set by the service
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      { assertion   = cfg.config != null;
+        message     = "please provide Dante configuration file contents";
+      }
+    ];
+
+    users.users.dante = {
+      description   = "Dante SOCKS proxy daemon user";
+      isSystemUser  = true;
+      group         = "dante";
+    };
+    users.groups.dante = {};
+
+    systemd.services.dante = {
+      description   = "Dante SOCKS v4 and v5 compatible proxy server";
+      after         = [ "network.target" ];
+      wantedBy      = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type        = "simple";
+        ExecStart   = "${pkgs.dante}/bin/sockd -f ${confFile}";
+        ExecReload  = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        Restart     = "always";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 005c57dce7c..d1900deceaf 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 /etc/ddclient.conf";
+  ddclientFlags = "-foreground -verbose -noquiet -file ${config.services.ddclient.configFile}";
   ddclientPIDFile = "${stateDir}/ddclient.pid";
 
 in
@@ -52,6 +52,17 @@ in
         '';
       };
 
+      configFile = mkOption {
+        default = "/etc/ddclient.conf";
+        type = path;
+        description = ''
+          Path to configuration file.
+          When set to the default '/etc/ddclient.conf' it will be populated with the various other options in this module. When it is changed (for example: '/root/nixos/secrets/ddclient.conf') the file read directly to configure ddclient. This is a source of impurity.
+          The purpose of this is to avoid placing secrets into the store.
+        '';
+        example = "/root/nixos/secrets/ddclient.conf";
+      };
+
       protocol = mkOption {
         default = "dyndns2";
         type = str;
@@ -78,7 +89,7 @@ in
 
       extraConfig = mkOption {
         default = "";
-        type = str;
+        type = lines;
         description = ''
           Extra configuration. Contents will be added verbatim to the configuration file.
         '';
@@ -88,7 +99,7 @@ in
         default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '";
         type = str;
         description = ''
-          Method to determine the IP address to send to the dymanic DNS provider.
+          Method to determine the IP address to send to the dynamic DNS provider.
         '';
       };
     };
@@ -109,9 +120,11 @@ in
     };
 
     environment.etc."ddclient.conf" = {
+      enable = config.services.ddclient.configFile == "/etc/ddclient.conf";
       uid = config.ids.uids.ddclient;
       mode = "0600";
       text = ''
+        # This file can be used as a template for configFile or is automatically generated by Nix options.
         daemon=600
         cache=${stateDir}/ddclient.cache
         pid=${ddclientPIDFile}
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index b31d479ab4f..87c0aa50a1f 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -10,7 +10,8 @@ let
 
   interfaces = attrValues config.networking.interfaces;
 
-  enableDHCP = config.networking.useDHCP || any (i: i.useDHCP == true) interfaces;
+  enableDHCP = config.networking.dhcpcd.enable &&
+        (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces);
 
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
@@ -61,7 +62,6 @@ let
       ${cfg.extraConfig}
     '';
 
-  # Hook for emitting ip-up/ip-down events.
   exitHook = pkgs.writeText "dhcpcd.exit-hook"
     ''
       if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
@@ -73,14 +73,8 @@ let
           # 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 start ip-up.target
       fi
 
-      #if [ "$reason" = EXPIRE -o "$reason" = RELEASE -o "$reason" = NOCARRIER ] ; then
-      #    ${config.systemd.package}/bin/systemctl start ip-down.target
-      #fi
-
       ${cfg.runHook}
     '';
 
@@ -92,6 +86,15 @@ in
 
   options = {
 
+    networking.dhcpcd.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable dhcpcd for device configuration. This is mainly to
+        explicitly disable dhcpcd (for example when using networkd).
+      '';
+    };
+
     networking.dhcpcd.persistent = mkOption {
       type = types.bool;
       default = false;
@@ -154,10 +157,9 @@ in
     systemd.services.dhcpcd =
       { description = "DHCP Client";
 
-        wantedBy = [ "network.target" ];
-        # Work-around to deal with problems where the kernel would remove &
-        # re-create Wifi interfaces early during boot.
-        after = [ "network-interfaces.target" ];
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        wants = [ "network.target" ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
         # because it brings down the network interfaces configured by
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index 900df67b53a..d2cd00e74a1 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -47,6 +47,7 @@ in
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "";
         example = ''
           option subnet-mask 255.255.255.0;
@@ -66,6 +67,14 @@ in
         ";
       };
 
+      extraFlags = mkOption {
+        default = "";
+        example = "-6";
+        description = "
+          Additional command line flags to be passed to the dhcpd daemon.
+        ";
+      };
+
       configFile = mkOption {
         default = null;
         description = "
@@ -138,6 +147,7 @@ in
           { 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";
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index 2714e8d7599..462039803f8 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -5,15 +5,25 @@ 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}";
 
-  daemonArgs =
-    [ "--local-address=${localAddress}"
-      (optionalString cfg.tcpOnly "--tcp-only")
-      (optionalString cfg.ephemeralKeys "-E")
-    ]
-    ++ resolverArgs;
+  # The minisign public key used to sign the upstream resolver list.
+  # This is somewhat more flexible than preloading the key as an
+  # embedded string.
+  upstreamResolverListPubKey = pkgs.fetchurl {
+    url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub;
+    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
@@ -22,9 +32,16 @@ let
         "--provider-key=${cfg.customResolver.key}"
       ]
     else
-      [ "--resolvers-list=${cfg.resolverList}"
-        "--resolver-name=${toString cfg.resolverName}"
+      [ "--resolvers-list=${resolverList}"
+        "--resolver-name=${cfg.resolverName}"
       ];
+
+  # The final command line arguments passed to the daemon
+  daemonArgs =
+    [ "--local-address=${localAddress}" ]
+    ++ optional cfg.tcpOnly "--tcp-only"
+    ++ optional cfg.ephemeralKeys "-E"
+    ++ resolverArgs;
 in
 
 {
@@ -35,7 +52,11 @@ in
 
   options = {
     services.dnscrypt-proxy = {
-      enable = mkEnableOption "DNSCrypt client proxy";
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the DNSCrypt client proxy";
+      };
 
       localAddress = mkOption {
         default = "127.0.0.1";
@@ -62,24 +83,20 @@ in
         default = "dnscrypt.eu-nl";
         type = types.nullOr types.str;
         description = ''
-          The name of the upstream DNSCrypt resolver to use, taken from the
-          list named in the <literal>resolverList</literal> option.
-          The default resolver is located in Holland, supports DNS security
-          extensions, and claims to not keep logs.
+          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 = ''
-          The list of upstream DNSCrypt resolvers. By default, we use the most
-          recent list published by upstream.
+          List of DNSCrypt resolvers.  The default is to use the list of
+          public resolvers provided by upstream.
         '';
-        example = literalExample "${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv";
-        default = pkgs.fetchurl {
-          url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv;
-          sha256 = "1i9wzw4zl052h5nyp28bwl8d66cgj0awvjhw5wgwz0warkjl1g8g";
-        };
-        defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }";
       };
 
       customResolver = mkOption {
@@ -146,7 +163,7 @@ in
       }
     ];
 
-    security.apparmor.profiles = mkIf apparmorEnabled (singleton (pkgs.writeText "apparmor-dnscrypt-proxy" ''
+    security.apparmor.profiles = optional apparmorEnabled (pkgs.writeText "apparmor-dnscrypt-proxy" ''
       ${dnscrypt-proxy}/bin/dnscrypt-proxy {
         /dev/null rw,
         /dev/urandom r,
@@ -173,9 +190,9 @@ in
         ${getLib pkgs.lz4}/lib/liblz4.so.* mr,
         ${getLib pkgs.attr}/lib/libattr.so.* mr,
 
-        ${cfg.resolverList} r,
+        ${resolverList} r,
       }
-    ''));
+    '');
 
     users.users.dnscrypt-proxy = {
       description = "dnscrypt-proxy daemon user";
@@ -184,11 +201,61 @@ in
     };
     users.groups.dnscrypt-proxy = {};
 
+    systemd.services.init-dnscrypt-proxy-statedir = optionalAttrs useUpstreamResolverList {
+      description = "Initialize dnscrypt-proxy state directory";
+      script = ''
+        mkdir -pv ${stateDirectory}
+        chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory}
+        cp --preserve=timestamps -uv \
+          ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \
+          ${stateDirectory}
+      '';
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+
+    systemd.services.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+      description = "Update list of DNSCrypt resolvers";
+
+      requires = [ "init-dnscrypt-proxy-statedir.service" ];
+      after = [ "init-dnscrypt-proxy-statedir.service" ];
+
+      path = with pkgs; [ curl 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
+        mv dnscrypt-resolvers.csv.minisig{.tmp,}
+        minisign -q -V -p ${upstreamResolverListPubKey} \
+          -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig
+        mv dnscrypt-resolvers.csv{.tmp,}
+      '';
+
+      serviceConfig = {
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHome = true;
+        ProtectSystem = true;
+      };
+    };
+
+    systemd.timers.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList {
+      timerConfig = {
+        OnBootSec = "5min";
+        OnUnitActiveSec = "6h";
+      };
+      wantedBy = [ "timers.target" ];
+    };
+
     systemd.sockets.dnscrypt-proxy = {
       description = "dnscrypt-proxy listening socket";
       socketConfig = {
-        ListenStream = "${localAddress}";
-        ListenDatagram = "${localAddress}";
+        ListenStream = localAddress;
+        ListenDatagram = localAddress;
       };
       wantedBy = [ "sockets.target" ];
     };
@@ -196,8 +263,15 @@ in
     systemd.services.dnscrypt-proxy = {
       description = "dnscrypt-proxy daemon";
 
-      after = [ "network.target" ] ++ optional apparmorEnabled "apparmor.service";
-      requires = [ "dnscrypt-proxy.socket "] ++ optional apparmorEnabled "apparmor.service";
+      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";
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml
index e212a8d3e2c..982961833ad 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.xml
+++ b/nixos/modules/services/networking/dnscrypt-proxy.xml
@@ -49,8 +49,8 @@
   <para>
     <programlisting>
       {
-      services.dnsmasq.enable = true;
-      services.dnsmasq.servers = [ "127.0.0.1#43" ];
+        services.dnsmasq.enable = true;
+        services.dnsmasq.servers = [ "127.0.0.1#43" ];
       }
     </programlisting>
   </para>
@@ -60,12 +60,9 @@
   <para>
     <programlisting>
       {
-      networking.nameservers = [ "127.0.0.1" ];
-      services.unbound.enable = true;
-      services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
-      services.unbound.extraConfig = ''
-        do-not-query-localhost: no
-      '';
+        networking.nameservers = [ "127.0.0.1" ];
+        services.unbound.enable = true;
+        services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
       }
     </programlisting>
   </para>
diff --git a/nixos/modules/services/networking/docker-registry-server.nix b/nixos/modules/services/networking/docker-registry-server.nix
deleted file mode 100644
index d21bbb6a86c..00000000000
--- a/nixos/modules/services/networking/docker-registry-server.nix
+++ /dev/null
@@ -1,98 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.nodeDockerRegistry;
-
-in {
-  options.services.nodeDockerRegistry = {
-    enable = mkEnableOption "docker registry service";
-
-    port = mkOption {
-      description = "Docker registry listening port.";
-      default = 8080;
-      type = types.int;
-    };
-
-    users = mkOption {
-      description = "Docker registry list of users.";
-      default = [];
-      options = [{
-        user = mkOption {
-          description = "Docker registry user username.";
-          type = types.str;
-        };
-
-        pass = mkOption {
-          description = "Docker registry user password.";
-          type = types.str;
-        };
-      }];
-      type = types.listOf types.optionSet;
-    };
-
-    onTag = mkOption {
-      description = "Docker registry hook triggered when an image is tagged.";
-      default = "";
-      type = types.str;
-    };
-
-    onImage = mkOption {
-      description = "Docker registry hook triggered when an image metadata is uploaded.";
-      default = "";
-      type = types.str;
-    };
-
-    onLayer = mkOption {
-      description = "Docker registry hook triggered when an when an image layer is uploaded.";
-      default = "";
-      type = types.str;
-    };
-
-    onVerify = mkOption {
-      description = "Docker registry hook triggered when an image layer+metadata has been verified.";
-      default = "";
-      type = types.str;
-    };
-
-    onIndex = mkOption {
-      description = "Docker registry hook triggered when an when an image file system data has been indexed.";
-      default = "";
-      type = types.str;
-    };
-
-    dataDir = mkOption {
-      description = "Docker registry data directory";
-      default = "/var/lib/docker-registry";
-      type = types.path;
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.docker-registry-server = {
-      description = "Docker Registry Service.";
-      wantedBy = ["multi-user.target"];
-      after = ["network.target"];
-      script = ''
-        ${pkgs.nodePackages.docker-registry-server}/bin/docker-registry-server \
-          --dir ${cfg.dataDir} \
-          --port ${toString cfg.port} \
-          ${concatMapStringsSep " " (u: "--user ${u.user}:${u.pass}") cfg.users} \
-          ${optionalString (cfg.onTag != "") "--on-tag '${cfg.onTag}'"} \
-          ${optionalString (cfg.onImage != "") "--on-image '${cfg.onImage}'"} \
-          ${optionalString (cfg.onVerify != "") "--on-verify '${cfg.onVerify}'"} \
-          ${optionalString (cfg.onIndex != "") "--on-index '${cfg.onIndex}'"}
-      '';
-
-      serviceConfig.User = "docker-registry";
-    };
-
-    users.extraUsers.docker-registry = {
-      uid = config.ids.uids.docker-registry;
-      description = "Docker registry user";
-      createHome = true;
-      home = cfg.dataDir;
-    };
-  };
-}
diff --git a/nixos/modules/services/networking/fakeroute.nix b/nixos/modules/services/networking/fakeroute.nix
new file mode 100644
index 00000000000..82a9fb729d8
--- /dev/null
+++ b/nixos/modules/services/networking/fakeroute.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fakeroute;
+  routeConf = pkgs.writeText "route.conf" (concatStringsSep "\n" cfg.route);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fakeroute = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the fakeroute service.
+        '';
+      };
+
+      route = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [
+          "216.102.187.130"
+          "4.0.1.122"
+          "198.116.142.34"
+          "63.199.8.242"
+        ];
+        description = ''
+         Fake route that will appear after the real
+         one to any host running a traceroute.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.fakeroute = {
+      description = "Fakeroute Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        User = "root";
+        ExecStart = "${pkgs.fakeroute}/bin/fakeroute -f ${routeConf}";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/ferm.nix b/nixos/modules/services/networking/ferm.nix
index 6271e82541f..8933e166f59 100644
--- a/nixos/modules/services/networking/ferm.nix
+++ b/nixos/modules/services/networking/ferm.nix
@@ -51,6 +51,7 @@ in {
       before = [ "network-pre.target" ];
       wants = [ "network-pre.target" ];
       wantedBy = [ "multi-user.target" ];
+      reloadIfChanged = true;
       serviceConfig = {
         Type="oneshot";
         RemainAfterExit = "yes";
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 138153306dd..1c0ea5034df 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -100,13 +100,13 @@ let
 
     # Perform a reverse-path test to refuse spoofers
     # For now, we just drop, as the raw table doesn't have a log-refuse yet
-    ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
+    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
       # Clean up rpfilter rules
       ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true
       ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true
       ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
 
-      ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter -j RETURN
+      ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
 
       # Allows this host to act as a DHCPv4 server
       iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
@@ -200,7 +200,7 @@ let
     # Clean up after added ruleset
     ip46tables -D INPUT -j nixos-fw 2>/dev/null || true
 
-    ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
+    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
       ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true
     ''}
 
@@ -373,7 +373,7 @@ in
 
     networking.firewall.checkReversePath = mkOption {
       default = kernelHasRPFilter;
-      type = types.bool;
+      type = types.either types.bool (types.enum ["strict" "loose"]);
       description =
         ''
           Performs a reverse path filter test on a packet.
@@ -381,7 +381,8 @@ in
           that the packet arrived on, it is refused.
 
           If using asymmetric routing or other complicated routing,
-          disable this setting and setup your own counter-measures.
+          set this option to loose mode or disable it and setup your
+          own counter-measures.
 
           (needs kernel 3.3+)
         '';
@@ -482,7 +483,7 @@ in
       options nf_conntrack nf_conntrack_helper=0
     '';
 
-    assertions = [ { assertion = ! cfg.checkReversePath || kernelHasRPFilter;
+    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"; }
@@ -490,7 +491,8 @@ in
 
     systemd.services.firewall = {
       description = "Firewall";
-      wantedBy = [ "network-pre.target" ];
+      wantedBy = [ "sysinit.target" ];
+      wants = [ "network-pre.target" ];
       before = [ "network-pre.target" ];
       after = [ "systemd-modules-load.service" ];
 
@@ -500,6 +502,7 @@ in
       # 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;
 
       reloadIfChanged = true;
 
diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix
new file mode 100644
index 00000000000..ca47a18bc1f
--- /dev/null
+++ b/nixos/modules/services/networking/flannel.nix
@@ -0,0 +1,154 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.flannel;
+
+  networkConfig = filterAttrs (n: v: v != null) {
+    Network = cfg.network;
+    SubnetLen = cfg.subnetLen;
+    SubnetMin = cfg.subnetMin;
+    SubnetMax = cfg.subnetMax;
+    Backend = cfg.backend;
+  };
+in {
+  options.services.flannel = {
+    enable = mkEnableOption "flannel";
+
+    package = mkOption {
+      description = "Package to use for flannel";
+      type = types.package;
+      default = pkgs.flannel.bin;
+      defaultText = "pkgs.flannel.bin";
+    };
+
+    publicIp = mkOption {
+      description = ''
+        IP accessible by other nodes for inter-host communication.
+        Defaults to the IP of the interface being used for communication.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    iface = mkOption {
+      description = ''
+        Interface to use (IP or name) for inter-host communication.
+        Defaults to the interface for the default route on the machine.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    etcd = {
+      endpoints = mkOption {
+        description = "Etcd endpoints";
+        type = types.listOf types.str;
+        default = ["http://127.0.0.1:2379"];
+      };
+
+      prefix = mkOption {
+        description = "Etcd key prefix";
+        type = types.str;
+        default = "/coreos.com/network";
+      };
+
+      caFile = mkOption {
+        description = "Etcd certificate authority file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      certFile = mkOption {
+        description = "Etcd cert file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+
+      keyFile = mkOption {
+        description = "Etcd key file";
+        type = types.nullOr types.path;
+        default = null;
+      };
+    };
+
+    network = mkOption {
+      description = " IPv4 network in CIDR format to use for the entire flannel network.";
+      type = types.str;
+    };
+
+    subnetLen = mkOption {
+      description = ''
+        The size of the subnet allocated to each host. Defaults to 24 (i.e. /24)
+        unless the Network was configured to be smaller than a /24 in which case
+        it is one less than the network.
+      '';
+      type = types.int;
+      default = 24;
+    };
+
+    subnetMin = mkOption {
+      description = ''
+        The beginning of IP range which the subnet allocation should start with.
+        Defaults to the first subnet of Network.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    subnetMax = mkOption {
+      description = ''
+        The end of IP range which the subnet allocation should start with.
+        Defaults to the last subnet of Network.
+      '';
+      type = types.nullOr types.str;
+      default = null;
+    };
+
+    backend = mkOption {
+      description = "Type of backend to use and specific configurations for that backend.";
+      type = types.attrs;
+      default = {
+        Type = "vxlan";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.flannel = {
+      description = "Flannel Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        FLANNELD_PUBLIC_IP = cfg.publicIp;
+        FLANNELD_ETCD_ENDPOINTS = concatStringsSep "," cfg.etcd.endpoints;
+        FLANNELD_ETCD_KEYFILE = cfg.etcd.keyFile;
+        FLANNELD_ETCD_CERTFILE = cfg.etcd.certFile;
+        FLANNELD_ETCD_CAFILE = cfg.etcd.caFile;
+        FLANNELD_IFACE = cfg.iface;
+        ETCDCTL_CERT_FILE = cfg.etcd.certFile;
+        ETCDCTL_KEY_FILE = cfg.etcd.keyFile;
+        ETCDCTL_CA_FILE = cfg.etcd.caFile;
+        ETCDCTL_PEERS = concatStringsSep "," cfg.etcd.endpoints;
+      };
+      preStart = ''
+        echo "setting network configuration"
+        until ${pkgs.etcdctl.bin}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}'
+        do
+          echo "setting network configuration, retry"
+          sleep 1
+        done
+      '';
+      postStart = ''
+        while [ ! -f /run/flannel/subnet.env ]
+        do
+          sleep 1
+        done
+      '';
+      serviceConfig.ExecStart = "${cfg.package}/bin/flannel";
+    };
+
+    services.etcd.enable = mkDefault cfg.etcd.endpoints == ["http://127.0.0.1:2379"];
+  };
+}
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
index 215ffe48a56..cd3fcd0f8f6 100644
--- a/nixos/modules/services/networking/git-daemon.nix
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -116,7 +116,8 @@ in
       };
 
     systemd.services."git-daemon" = {
-      wantedBy = [ "ip-up.target" ];
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
       script = "${pkgs.git}/bin/git daemon --reuseaddr "
         + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ")
         + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
diff --git a/nixos/modules/services/networking/gvpe.nix b/nixos/modules/services/networking/gvpe.nix
index 27b64b5bb95..3ef3548e0a0 100644
--- a/nixos/modules/services/networking/gvpe.nix
+++ b/nixos/modules/services/networking/gvpe.nix
@@ -105,7 +105,7 @@ in
   config = mkIf cfg.enable {
     systemd.services.gvpe = {
       description = "GNU Virtual Private Ethernet node";
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
       preStart = ''
diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix
index 287964aab07..fd4545e88e2 100644
--- a/nixos/modules/services/networking/hostapd.nix
+++ b/nixos/modules/services/networking/hostapd.nix
@@ -86,7 +86,7 @@ in
 
       hwMode = mkOption {
         default = "g";
-        type = types.string;
+        type = types.enum [ "a" "b" "g" ];
         description = ''
           Operation mode.
           (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g).
@@ -140,7 +140,7 @@ in
           ieee80211n=1
           ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
           '';
-        type = types.string;
+        type = types.lines;
         description = "Extra configuration options to put in hostapd.conf.";
       };
     };
@@ -152,9 +152,6 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = (cfg.hwMode == "a" || cfg.hwMode == "b" || cfg.hwMode == "g");
-        message = "hwMode must be a/b/g";
-      }
       { assertion = (cfg.channel >= 1 && cfg.channel <= 13);
         message = "channel must be between 1 and 13";
       }];
diff --git a/nixos/modules/services/networking/htpdate.nix b/nixos/modules/services/networking/htpdate.nix
new file mode 100644
index 00000000000..f5d512c7cd5
--- /dev/null
+++ b/nixos/modules/services/networking/htpdate.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (pkgs) htpdate;
+
+  cfg = config.services.htpdate;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.htpdate = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable htpdate daemon.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Additional command line arguments to pass to htpdate.
+        '';
+      };
+
+      servers = mkOption {
+        type = types.listOf types.str;
+        default = [ "www.google.com" ];
+        description = ''
+          HTTP servers to use for time synchronization.
+        '';
+      };
+
+      proxy = mkOption {
+        type = types.str;
+        default = "";
+        example = "127.0.0.1:8118";
+        description = ''
+          HTTP proxy used for requests.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.htpdate = {
+      description = "htpdate daemon";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        PIDFile = "/var/run/htpdate.pid";
+        ExecStart = concatStringsSep " " [
+          "${htpdate}/bin/htpdate"
+          "-D -u nobody"
+          "-a -s"
+          "-l"
+          "${optionalString (cfg.proxy != "") "-P ${cfg.proxy}"}"
+          "${cfg.extraOptions}"
+          "${concatStringsSep " " cfg.servers}"
+        ];
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 0cbf57314c4..abb7a4e9137 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -10,7 +10,7 @@ let
 
   extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')";
 
-  toYesNo = b: if b then "yes" else "no";
+  toYesNo = b: if b then "true" else "false";
 
   mkEndpointOpt = name: addr: port: {
     enable = mkEnableOption name;
@@ -31,6 +31,17 @@ let
     };
   };
 
+  mkKeyedEndpointOpt = name: addr: port: keyFile:
+  (mkEndpointOpt name addr port) // {
+    keys = mkOption {
+      type = types.str;
+      default = "";
+      description = ''
+        File to persist ${lib.toUpper name} keys.
+      '';
+    };
+  };
+
   commonTunOpts = let
     i2cpOpts = {
       length = mkOption {
@@ -63,19 +74,49 @@ let
     };
   } // mkEndpointOpt name "127.0.0.1" 0;
 
-  i2pdConf = pkgs.writeText "i2pd.conf" ''
-      ipv6 = ${toYesNo cfg.enableIPv6}
-      notransit = ${toYesNo cfg.notransit}
-      floodfill = ${toYesNo cfg.floodfill}
-      ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
-      ${flip concatMapStrings
-        (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
-        (proto: let portStr = toString proto.port; in ''
-      [${proto.name}]
-      address = ${proto.address}
-      port = ${toString proto.port}
-      enabled = ${toYesNo proto.enable}
-      '')
+  i2pdConf = pkgs.writeText "i2pd.conf"
+  ''
+  ipv4 = ${toYesNo cfg.enableIPv4}
+  ipv6 = ${toYesNo cfg.enableIPv6}
+  notransit = ${toYesNo cfg.notransit}
+  floodfill = ${toYesNo 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}"}
+
+  [limits]
+  transittunnels = ${toString cfg.limits.transittunnels}
+
+  [upnp]
+  enabled = ${toYesNo cfg.upnp.enable}
+  name = ${cfg.upnp.name}
+
+  [precomputation]
+  elgamal = ${toYesNo cfg.precomputation.elgamal}
+
+  [reseed]
+  verify = ${toYesNo cfg.reseed.verify}
+  file = ${cfg.reseed.file}
+  urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
+
+  [addressbook]
+  defaulturl = ${cfg.addressbook.defaulturl}
+  subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions}
+  ${flip concatMapStrings
+      (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
+      (proto: let portStr = toString proto.port; in
+        ''
+          [${proto.name}]
+          enabled = ${toYesNo 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 ? user then "user = ${proto.user}" else ""}
+          ${if proto ? pass then "pass = ${proto.pass}" else ""}
+          ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
+          ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""}
+        '')
       }
   '';
 
@@ -106,7 +147,7 @@ let
   host = ${tun.address}
   port = ${tun.port}
   inport = ${tun.inPort}
-  accesslist = ${concatStringSep "," tun.accessList}
+  accesslist = ${builtins.concatStringsSep "," tun.accessList}
   '')
   }
   '';
@@ -114,7 +155,7 @@ let
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
     ${if isNull cfg.extIp then extip else ""}
-    ${pkgs.i2pd}/bin/i2pd --log=1 \
+    ${pkgs.i2pd}/bin/i2pd \
       --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \
       --conf=${i2pdConf} \
       --tunconf=${i2pdTunnelConf}
@@ -135,6 +176,8 @@ in
         default = false;
         description = ''
           Enables I2Pd as a running service upon activation.
+          Please read http://i2pd.readthedocs.io/en/latest/ for further
+          configuration help.
         '';
       };
 
@@ -162,6 +205,22 @@ in
         '';
       };
 
+      netid = mkOption {
+        type = types.int;
+        default = 2;
+        description = ''
+          I2P overlay netid.
+        '';
+      };
+
+      bandwidth = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = ''
+           Set a router bandwidth limit integer in kbps or letters: L (32), O (256), P (2048), X (>9000)
+        '';
+      };
+
       port = mkOption {
         type = with types; nullOr int;
         default = null;
@@ -170,6 +229,14 @@ in
         '';
       };
 
+      enableIPv4 = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enables IPv4 connectivity. Enabled by default.
+        '';
+      };
+
       enableIPv6 = mkOption {
         type = types.bool;
         default = false;
@@ -178,53 +245,177 @@ in
         '';
       };
 
-      proto.http = mkEndpointOpt "http" "127.0.0.1" 7070;
+      upnp = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enables UPnP.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "I2Pd";
+          description = ''
+            Name i2pd appears in UPnP forwardings list.
+          '';
+        };
+      };
+
+      precomputation.elgamal = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use ElGamal precomputated tables.
+        '';
+      };
+
+      reseed = {
+        verify = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Request SU3 signature verification
+          '';
+        };
+
+        file = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            Full path to SU3 file to reseed from
+          '';
+        };
+
+        urls = mkOption {
+          type = with types; listOf str;
+          default = [
+            "https://reseed.i2p-project.de/"
+            "https://i2p.mooo.com/netDb/"
+            "https://netdb.i2p2.no/"
+            "https://us.reseed.i2p2.no:444/"
+            "https://uk.reseed.i2p2.no:444/"
+            "https://i2p.manas.ca:8443/"
+          ];
+          description = ''
+            Reseed URLs
+          '';
+        };
+      };
+
+      addressbook = {
+       defaulturl = mkOption {
+          type = types.str;
+          default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
+          description = ''
+            AddressBook subscription URL for initial setup
+          '';
+        };
+       subscriptions = mkOption {
+          type = with types; listOf str;
+          default = [
+            "http://inr.i2p/export/alive-hosts.txt"
+            "http://i2p-projekt.i2p/hosts.txt"
+            "http://stats.i2p/cgi-bin/newhosts.txt"
+          ];
+          description = ''
+            AddressBook subscription URLs
+          '';
+        };
+      };
+
+      limits.transittunnels = mkOption {
+        type = types.int;
+        default = 2500;
+        description = ''
+          Maximum number of active transit sessions
+        '';
+      };
+
+      proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
+        auth = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable authentication for webconsole.
+          '';
+        };
+        user = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Username for webconsole access
+          '';
+        };
+        pass = mkOption {
+          type = types.str;
+          default = "i2pd";
+          description = ''
+            Password for webconsole access.
+          '';
+        };
+      };
+
+      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4446 "";
+      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "")
+      // {
+        outproxy = mkOption {
+          type = types.str;
+          default = "127.0.0.1";
+          description = "Upstream outproxy bind address.";
+        };
+        outproxyPort = mkOption {
+          type = types.int;
+          default = 4444;
+          description = "Upstream outproxy bind port.";
+        };
+      };
+
       proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656;
       proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827;
+      proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654;
       proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650;
-      proto.httpProxy = mkEndpointOpt "httpproxy" "127.0.0.1" 4446;
-      proto.socksProxy = mkEndpointOpt "socksproxy" "127.0.0.1" 4447;
 
       outTunnels = mkOption {
         default = {};
-        type = with types; loaOf optionSet;
+        type = with types; loaOf (submodule (
+          { name, config, ... }: {
+            options = commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
         description = ''
           Connect to someone as a client and establish a local accept endpoint
         '';
-        options = [ ({ name, config, ... }: {
-          options = commonTunOpts name;
-          config = {
-            name = mkDefault name;
-          };
-        }) ];
       };
 
       inTunnels = mkOption {
         default = {};
-        type = with types; loaOf optionSet;
+        type = with types; loaOf (submodule (
+          { name, config, ... }: {
+            options = {
+              inPort = mkOption {
+                type = types.int;
+                default = 0;
+                description = "Service port. Default to the tunnel's listen port.";
+              };
+              accessList = mkOption {
+                type = with types; listOf str;
+                default = [];
+                description = "I2P nodes that are allowed to connect to this service.";
+              };
+            } // commonTunOpts name;
+            config = {
+              name = mkDefault name;
+            };
+          }
+        ));
         description = ''
           Serve something on I2P network at port and delegate requests to address inPort.
         '';
-        options = [ ({ name, config, ... }: {
-
-          options = {
-            inPort = mkOption {
-              type = types.int;
-              default = 0;
-              description = "Service port. Default to the tunnel's listen port.";
-            };
-            accessList = mkOption {
-              type = with types; listOf str;
-              default = [];
-              description = "I2P nodes that are allowed to connect to this service.";
-            };
-          } // commonTunOpts name;
-
-          config = {
-            name = mkDefault name;
-          };
-
-        }) ];
       };
     };
   };
diff --git a/nixos/modules/services/networking/iodine.nix b/nixos/modules/services/networking/iodine.nix
index 1b0d2d9a517..512dbd77ae4 100644
--- a/nixos/modules/services/networking/iodine.nix
+++ b/nixos/modules/services/networking/iodine.nix
@@ -106,7 +106,8 @@ in
       createIodineClientService = name: cfg:
       {
         description = "iodine client - ${name}";
-        wantedBy = [ "ip-up.target" ];
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           RestartSec = "30s";
           Restart = "always";
@@ -121,7 +122,8 @@ in
     ) // {
       iodined = mkIf (cfg.server.enable) {
         description = "iodine, ip over dns server daemon";
-        wantedBy = [ "ip-up.target" ];
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
         serviceConfig.ExecStart = "${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${cfg.server.ip} ${cfg.server.domain}";
       };
     };
diff --git a/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixos/modules/services/networking/ircd-hybrid/builder.sh
index f2c92878a4d..38312210df2 100644
--- a/nixos/modules/services/networking/ircd-hybrid/builder.sh
+++ b/nixos/modules/services/networking/ircd-hybrid/builder.sh
@@ -12,7 +12,7 @@ for i in $scripts; do
     if test "$(echo $i | cut -c1-2)" = "=>"; then
         subDir=$(echo $i | cut -c3-)
     else
-        dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//')
+        dst=$out/$subDir/$(stripHash $i | sed 's/\.in//')
         doSub $i $dst
         chmod +x $dst # !!!
     fi
@@ -23,7 +23,7 @@ for i in $substFiles; do
     if test "$(echo $i | cut -c1-2)" = "=>"; then
         subDir=$(echo $i | cut -c3-)
     else
-        dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//')
+        dst=$out/$subDir/$(stripHash $i | sed 's/\.in//')
         doSub $i $dst
     fi
 done
diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix
index 5f3efcd133a..834de4fdc09 100644
--- a/nixos/modules/services/networking/kippo.nix
+++ b/nixos/modules/services/networking/kippo.nix
@@ -46,7 +46,7 @@ rec {
       };
       extraConfig = mkOption {
         default = "";
-        type = types.string;
+        type = types.lines;
         description = ''Extra verbatim configuration added to the end of kippo.cfg.'';
       };
     };
@@ -54,7 +54,7 @@ rec {
   };
   config = mkIf cfg.enable {
     environment.systemPackages = with pkgs.pythonPackages; [
-      python twisted_11 pycrypto pyasn1 ];
+      python pkgs.kippo.twisted pycrypto pyasn1 ];
 
     environment.etc."kippo.cfg".text = ''
         # Automatically generated by NixOS.
@@ -84,7 +84,7 @@ rec {
       description = "Kippo Web Server";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted_11}/lib/python2.7/site-packages/:.";
+      environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.kippo.twisted}/lib/python2.7/site-packages/:.";
       preStart = ''
         if [ ! -d ${cfg.varPath}/ ] ; then
             mkdir -p ${cfg.logPath}/tty
@@ -107,7 +107,7 @@ rec {
         fi
       '';
 
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted_11}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n";
+      serviceConfig.ExecStart = "${pkgs.kippo.twisted}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n";
       serviceConfig.PermissionsStartOnly = true;
       serviceConfig.User = "kippo"; 
       serviceConfig.Group = "kippo"; 
diff --git a/nixos/modules/services/networking/miredo.nix b/nixos/modules/services/networking/miredo.nix
new file mode 100644
index 00000000000..932d6cf2903
--- /dev/null
+++ b/nixos/modules/services/networking/miredo.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.miredo;
+  pidFile = "/run/miredo.pid";
+  miredoConf = pkgs.writeText "miredo.conf" ''
+    InterfaceName ${cfg.interfaceName}
+    ServerAddress ${cfg.serverAddress}
+    ${optionalString (cfg.bindAddress != null) "BindAddress ${cfg.bindAddress}"}
+    ${optionalString (cfg.bindPort != null) "BindPort ${cfg.bindPort}"}
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.miredo = {
+
+      enable = mkEnableOption "Whether miredo should be run on startup.";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.miredo;
+        defaultText = "pkgs.miredo";
+        description = ''
+          The package to use for the miredo daemon's binary.
+        '';
+      };
+
+      serverAddress = mkOption {
+        default = "teredo.remlab.net";
+        type = types.str;
+        description = ''
+          The hostname or primary IPv4 address of the Teredo server.
+          This setting is required if Miredo runs as a Teredo client.
+          "teredo.remlab.net" is an experimental service for testing only.
+          Please use another server for production and/or large scale deployments.
+        '';
+      };
+
+      interfaceName = mkOption {
+        default = "teredo";
+        type = types.str;
+        description = ''
+          Name of the network tunneling interface.
+        '';
+      };
+
+      bindAddress = mkOption {
+        default = null;
+        type = types.nullOr types.str;
+        description = ''
+          Depending on the local firewall/NAT rules, you might need to force
+          Miredo to use a fixed UDP port and or IPv4 address.
+        '';
+      };
+
+      bindPort = mkOption {
+        default = null;
+        type = types.nullOr types.str;
+        description = ''
+          Depending on the local firewall/NAT rules, you might need to force
+          Miredo to use a fixed UDP port and or IPv4 address.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.miredo = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      description = "Teredo IPv6 Tunneling Daemon";
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
+        ExecStartPre = "${cfg.package}/bin/miredo-checkconf -f ${miredoConf}";
+        ExecStart = "${cfg.package}/bin/miredo -c ${miredoConf} -p ${pidFile} -f";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/mjpg-streamer.nix b/nixos/modules/services/networking/mjpg-streamer.nix
index 9986f549aec..1286b0c7ef6 100644
--- a/nixos/modules/services/networking/mjpg-streamer.nix
+++ b/nixos/modules/services/networking/mjpg-streamer.nix
@@ -59,8 +59,12 @@ in {
       description = "mjpg-streamer webcam streamer";
       wantedBy = [ "multi-user.target" ];
 
-      serviceConfig.User = cfg.user;
-      serviceConfig.Group = cfg.group;
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
 
       script = ''
         IPLUGIN="${cfg.inputPlugin}"
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
index 1cc19a2c9e0..81f968ae9fe 100644
--- a/nixos/modules/services/networking/murmur.nix
+++ b/nixos/modules/services/networking/murmur.nix
@@ -15,7 +15,7 @@ let
     logfile=/var/log/murmur/murmurd.log
     pidfile=${cfg.pidfile}
 
-    welcome="${cfg.welcome}"
+    welcometext="${cfg.welcometext}"
     port=${toString cfg.port}
 
     ${if cfg.hostName == "" then "" else "host="+cfg.hostName}
@@ -84,7 +84,7 @@ in
         description = "Path to PID file for Murmur daemon.";
       };
 
-      welcome = mkOption {
+      welcometext = mkOption {
         type = types.str;
         default = "";
         description = "Welcome message for connected clients.";
@@ -230,7 +230,7 @@ in
       };
 
       extraConfig = mkOption {
-        type = types.str;
+        type = types.lines;
         default = "";
         description = "Extra configuration to put into mumur.ini.";
       };
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 9d163e60d5e..08ba2fdb164 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -122,23 +122,23 @@ in
     };
 
     networking.nat.forwardPorts = mkOption {
-      type = types.listOf types.optionSet;
+      type = with types; listOf (submodule {
+        options = {
+          sourcePort = mkOption {
+            type = types.int;
+            example = 8080;
+            description = "Source port of the external interface";
+          };
+
+          destination = mkOption {
+            type = types.str;
+            example = "10.0.0.1:80";
+            description = "Forward tcp connection to destination ip:port";
+          };
+        };
+      });
       default = [];
       example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; } ];
-      options = {
-        sourcePort = mkOption {
-          type = types.int;
-          example = 8080;
-          description = "Source port of the external interface";
-        };
-
-        destination = mkOption {
-          type = types.str;
-          example = "10.0.0.1:80";
-          description = "Forward tcp connection to destination ip:port";
-        };
-      };
-
       description =
         ''
           List of forwarded ports from the external interface to
@@ -171,7 +171,7 @@ in
     systemd.services = mkIf (!config.networking.firewall.enable) { nat = {
       description = "Network Address Translation";
       wantedBy = [ "network.target" ];
-      after = [ "network-interfaces.target" "systemd-modules-load.service" ];
+      after = [ "network-pre.target" "systemd-modules-load.service" ];
       path = [ pkgs.iptables ];
       unitConfig.ConditionCapability = "CAP_NET_ADMIN";
 
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index d198e3bfc02..8f353979d3f 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -52,14 +52,6 @@ let
     });
   '';
 
-  ipUpScript = writeScript "01nixos-ip-up" ''
-    #!/bin/sh
-    if test "$2" = "up"; then
-      ${config.systemd.package}/bin/systemctl start ip-up.target
-      ${config.systemd.package}/bin/systemctl start network-online.target
-    fi
-  '';
-
   ns = xs: writeText "nameservers" (
     concatStrings (map (s: "nameserver ${s}\n") xs)
   );
@@ -188,9 +180,6 @@ in {
     boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
 
     environment.etc = with cfg.basePackages; [
-      { source = ipUpScript;
-        target = "NetworkManager/dispatcher.d/01nixos-ip-up";
-      }
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
       }
@@ -209,6 +198,9 @@ in {
       { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
+      { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name";
+        target = "NetworkManager/VPN/nm-strongswan-service.name";
+      }
     ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
            { source = overrideNameserversScript;
              target = "NetworkManager/dispatcher.d/02overridedns";
diff --git a/nixos/modules/services/networking/nntp-proxy.nix b/nixos/modules/services/networking/nntp-proxy.nix
index dca8ccac762..7eebecb23b0 100644
--- a/nixos/modules/services/networking/nntp-proxy.nix
+++ b/nixos/modules/services/networking/nntp-proxy.nix
@@ -148,11 +148,11 @@ in
       };
 
       verbosity = mkOption {
-        type = types.str;
+        type = types.enum [ "error" "warning" "notice" "info" "debug" ];
         default = "info";
         example = "error";
         description = ''
-          Verbosity level (error, warning, notice, info, debug)
+          Verbosity level
         '';
       };
 
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index 333a3378c4c..481e267f6c3 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -71,6 +71,7 @@ let
       # interfaces
     ${forEach "  ip-address: " cfg.interfaces}
 
+      ip-freebind:         ${yesOrNo  cfg.ipFreebind}
       hide-version:        ${yesOrNo  cfg.hideVersion}
       identity:            "${cfg.identity}"
       ip-transparent:      ${yesOrNo  cfg.ipTransparent}
@@ -84,7 +85,7 @@ let
       reuseport:           ${yesOrNo  cfg.reuseport}
       round-robin:         ${yesOrNo  cfg.roundRobin}
       server-count:        ${toString cfg.serverCount}
-      ${if cfg.statistics == null then "" else "statistics:          ${toString cfg.statistics}"}
+      ${maybeToString "statistics: " cfg.statistics}
       tcp-count:           ${toString cfg.tcpCount}
       tcp-query-count:     ${toString cfg.tcpQueryCount}
       tcp-timeout:         ${toString cfg.tcpTimeout}
@@ -117,7 +118,8 @@ let
   '';
 
   yesOrNo = b: if b then "yes" else "no";
-  maybeString = pre: s: if s == null then "" else ''${pre} "${s}"'';
+  maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"'';
+  maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}'';
   forEach = pre: l: concatMapStrings (x: pre + x + "\n") l;
 
 
@@ -146,6 +148,11 @@ let
     ${forEach     "  rrl-whitelist: "      zone.rrlWhitelist}
       ${maybeString "zonestats: "          zone.zoneStats}
 
+      ${maybeToString "max-refresh-time: " zone.maxRefreshSecs}
+      ${maybeToString "min-refresh-time: " zone.minRefreshSecs}
+      ${maybeToString "max-retry-time:   " zone.maxRetrySecs}
+      ${maybeToString "min-retry-time:   " zone.minRetrySecs}
+
       allow-axfr-fallback: ${yesOrNo       zone.allowAXFRFallback}
     ${forEach     "  allow-notify: "       zone.allowNotify}
     ${forEach     "  request-xfr: "        zone.requestXFR}
@@ -241,6 +248,44 @@ let
         '';
       };
 
+      maxRefreshSecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit refresh time for secondary zones. This is the timer which
+          checks to see if the zone has to be refetched when it expires.
+          Normally the value from the SOA record is used, but this  option
+          restricts that value.
+        '';
+      };
+
+      minRefreshSecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit refresh time for secondary zones.
+        '';
+      };
+
+      maxRetrySecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit retry time for secondary zones. This is the timeout after
+          a failed fetch attempt for the zone. Normally the value from
+          the SOA record is used, but this option restricts that value.
+        '';
+      };
+
+      minRetrySecs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Limit retry time for secondary zones.
+        '';
+      };
+
+
       notify = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -300,12 +345,10 @@ let
       };
 
       rrlWhitelist = mkOption {
-        type = types.listOf types.str;
+        type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]);
         default = [];
         description = ''
           Whitelists the given rrl-types.
-          The RRL classification types are:  nxdomain,  error, referral, any,
-          rrsig, wildcard, nodata, dnskey, positive, all
         '';
       };
 
@@ -366,6 +409,15 @@ in
       '';
     };
 
+    ipFreebind = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to bind to nonlocal addresses and interfaces that are down.
+        Similar to ip-transparent.
+      '';
+    };
+
     ipTransparent = mkOption {
       type = types.bool;
       default = false;
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix
index c8a08567928..88e6dbf22b9 100644
--- a/nixos/modules/services/networking/ntpd.nix
+++ b/nixos/modules/services/networking/ntpd.nix
@@ -34,7 +34,7 @@ in
     services.ntp = {
 
       enable = mkOption {
-        default = !config.boot.isContainer;
+        default = false;
         description = ''
           Whether to synchronise your machine's time using the NTP
           protocol.
@@ -42,12 +42,7 @@ in
       };
 
       servers = mkOption {
-        default = [
-          "0.nixos.pool.ntp.org"
-          "1.nixos.pool.ntp.org"
-          "2.nixos.pool.ntp.org"
-          "3.nixos.pool.ntp.org"
-        ];
+        default = config.networking.timeServers;
         description = ''
           The set of NTP servers from which to synchronise.
         '';
@@ -70,6 +65,7 @@ in
 
     # Make tools such as ntpq available in the system path.
     environment.systemPackages = [ pkgs.ntp ];
+    services.timesyncd.enable = mkForce false;
 
     users.extraUsers = singleton
       { name = ntpUser;
diff --git a/nixos/modules/services/networking/oidentd.nix b/nixos/modules/services/networking/oidentd.nix
index 651bb8e967c..ba7acd87954 100644
--- a/nixos/modules/services/networking/oidentd.nix
+++ b/nixos/modules/services/networking/oidentd.nix
@@ -25,7 +25,7 @@ with lib;
 
   config = mkIf config.services.oidentd.enable {
     systemd.services.oidentd = {
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig.Type = "forking";
       script = "${pkgs.oidentd}/sbin/oidentd -u oidentd -g nogroup" +
diff --git a/nixos/modules/services/networking/openfire.nix b/nixos/modules/services/networking/openfire.nix
index ed91b45ec94..4059eb3db83 100644
--- a/nixos/modules/services/networking/openfire.nix
+++ b/nixos/modules/services/networking/openfire.nix
@@ -34,7 +34,7 @@ with lib;
 
     assertions = singleton
       { assertion = !(config.services.openfire.usePostgreSQL -> config.services.postgresql.enable);
-        message = "OpenFire assertion failed.";
+        message = "OpenFire configured to use PostgreSQL but services.postgresql.enable is not enabled.";
       };
 
     systemd.services.openfire = {
@@ -47,7 +47,7 @@ with lib;
         export HOME=/tmp
         mkdir /var/log/openfire || true
         mkdir /etc/openfire || true
-        for i in ${openfire}/conf.inst/*; do
+        for i in ${pkgs.openfire}/conf.inst/*; do
             if ! test -f /etc/openfire/$(basename $i); then
                 cp $i /etc/openfire/
             fi
diff --git a/nixos/modules/services/networking/openntpd.nix b/nixos/modules/services/networking/openntpd.nix
index a8625fa2fa9..13a1b5258ce 100644
--- a/nixos/modules/services/networking/openntpd.nix
+++ b/nixos/modules/services/networking/openntpd.nix
@@ -49,7 +49,7 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    services.ntp.enable = mkForce false;
+    services.timesyncd.enable = mkForce false;
 
     # Add ntpctl to the environment for status checking
     environment.systemPackages = [ package ];
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 82173a841a3..3fbf5a9f022 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -56,7 +56,7 @@ let
       description = "OpenVPN instance ‘${name}’";
 
       wantedBy = optional cfg.autoStart "multi-user.target";
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
       path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
 
@@ -116,52 +116,54 @@ in
         attribute name.
       '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        config = mkOption {
-          type = types.lines;
-          description = ''
-            Configuration of this OpenVPN instance.  See
-            <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            for details.
-          '';
-        };
+          config = mkOption {
+            type = types.lines;
+            description = ''
+              Configuration of this OpenVPN instance.  See
+              <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+              for details.
+            '';
+          };
 
-        up = mkOption {
-          default = "";
-          type = types.lines;
-          description = ''
-            Shell commands executed when the instance is starting.
-          '';
-        };
+          up = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is starting.
+            '';
+          };
 
-        down = mkOption {
-          default = "";
-          type = types.lines;
-          description = ''
-            Shell commands executed when the instance is shutting down.
-          '';
-        };
+          down = mkOption {
+            default = "";
+            type = types.lines;
+            description = ''
+              Shell commands executed when the instance is shutting down.
+            '';
+          };
 
-        autoStart = mkOption {
-          default = true;
-          type = types.bool;
-          description = "Whether this OpenVPN instance should be started automatically.";
-        };
+          autoStart = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Whether this OpenVPN instance should be started automatically.";
+          };
+
+          updateResolvConf = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Use the script from the update-resolv-conf package to automatically
+              update resolv.conf with the DNS information provided by openvpn. The
+              script will be run after the "up" commands and before the "down" commands.
+            '';
+          };
 
-        updateResolvConf = mkOption {
-          default = false;
-          type = types.bool;
-          description = ''
-            Use the script from the update-resolv-conf package to automatically
-            update resolv.conf with the DNS information provided by openvpn. The
-            script will be run after the "up" commands and before the "down" commands.
-          '';
         };
 
-      };
+      });
 
     };
 
diff --git a/nixos/modules/services/networking/powerdns.nix b/nixos/modules/services/networking/powerdns.nix
new file mode 100644
index 00000000000..ba05e15389f
--- /dev/null
+++ b/nixos/modules/services/networking/powerdns.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.powerdns;
+  configDir = pkgs.writeTextDir "pdns.conf" "${cfg.extraConfig}";
+in {
+  options = {
+    services.powerdns = {
+      enable = mkEnableOption "Powerdns domain name server";
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "launch=bind";
+        description = ''
+          Extra lines to be added verbatim to pdns.conf.
+          Powerdns will chroot to /var/lib/powerdns.
+          So any file, powerdns is supposed to be read,
+          should be in /var/lib/powerdns and needs to specified
+          relative to the chroot.
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.powerdns.enable {
+    systemd.services.pdns = {
+      unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)";
+      description = "Powerdns name server";
+      wantedBy = [ "multi-user.target" ];
+      after = ["network.target" "mysql.service" "postgresql.service" "openldap.service"];
+
+      serviceConfig = {
+        Restart="on-failure";
+        RestartSec="1";
+        StartLimitInterval="0";
+        PrivateDevices=true;
+        CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT";
+        NoNewPrivileges=true;
+        ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/powerdns";
+        ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=nobody --setgid=nogroup --chroot=/var/lib/powerdns --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
+        ProtectSystem="full";
+        ProtectHome=true;
+        RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
index 4e1d66bc110..67d8cece611 100644
--- a/nixos/modules/services/networking/prayer.nix
+++ b/nixos/modules/services/networking/prayer.nix
@@ -56,6 +56,7 @@ in
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = "" ;
         description = ''
           Extra configuration. Contents will be added verbatim to the configuration file.
diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
index 94beb78ef5a..49ca839a2c3 100644
--- a/nixos/modules/services/networking/privoxy.nix
+++ b/nixos/modules/services/networking/privoxy.nix
@@ -6,8 +6,6 @@ let
 
   inherit (pkgs) privoxy;
 
-  privoxyUser = "privoxy";
-
   cfg = config.services.privoxy;
 
   confFile = pkgs.writeText "privoxy.conf" ''
@@ -88,18 +86,25 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-  
-    users.extraUsers = singleton
-      { name = privoxyUser;
-        uid = config.ids.uids.privoxy;
-        description = "Privoxy daemon user";
-      };
+
+    users.users.privoxy = {
+      isSystemUser = true;
+      home = "/var/empty";
+      group = "privoxy";
+    };
+
+    users.groups.privoxy = {};
 
     systemd.services.privoxy = {
       description = "Filtering web proxy";
       after = [ "network.target" "nss-lookup.target" ];
       wantedBy = [ "multi-user.target" ];
-      serviceConfig.ExecStart = "${privoxy}/sbin/privoxy --no-daemon --user ${privoxyUser} ${confFile}";
+      serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}";
+
+      serviceConfig.PrivateDevices = true;
+      serviceConfig.PrivateTmp = true;
+      serviceConfig.ProtectHome = true;
+      serviceConfig.ProtectSystem = "full";
     };
 
   };
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index f82f8bfddbb..5682b506344 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -164,7 +164,7 @@ in
 
         description = "Define the virtual hosts";
 
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule vHostOpts);
 
         example = {
           myhost = {
@@ -180,7 +180,6 @@ in
           };
         };
 
-        options = [ vHostOpts ];
       };
 
       ssl = mkOption {
@@ -196,6 +195,7 @@ in
       };
 
       extraConfig = mkOption {
+        type = types.lines;
         default = '''';
         description = "Additional prosody configuration";
       };
diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix
new file mode 100644
index 00000000000..ac83da92063
--- /dev/null
+++ b/nixos/modules/services/networking/quagga.nix
@@ -0,0 +1,187 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.quagga;
+
+  services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ];
+  allServices = services ++ [ "zebra" ];
+
+  isEnabled = service: cfg.${service}.enable;
+
+  daemonName = service: if service == "zebra" then service else "${service}d";
+
+  configFile = service:
+    let
+      scfg = cfg.${service};
+    in
+      if scfg.configFile != null then scfg.configFile
+      else pkgs.writeText "${daemonName service}.conf"
+        ''
+          ! Quagga ${daemonName service} configuration
+          !
+          hostname ${config.networking.hostName}
+          log syslog
+          service password-encryption
+          !
+          ${scfg.config}
+          !
+          end
+        '';
+
+  serviceOptions = service:
+    {
+      enable = mkEnableOption "the Quagga ${toUpper service} routing protocol";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/etc/quagga/${daemonName service}.conf";
+        description = ''
+          Configuration file to use for Quagga ${daemonName service}.
+          By default the NixOS generated files are used.
+        '';
+      };
+
+      config = mkOption {
+        type = types.lines;
+        default = "";
+        example =
+          let
+            examples = {
+              rip = ''
+                router rip
+                  network 10.0.0.0/8
+              '';
+
+              ospf = ''
+                router ospf
+                  network 10.0.0.0/8 area 0
+              '';
+
+              bgp = ''
+                router bgp 65001
+                  neighbor 10.0.0.1 remote-as 65001
+              '';
+            };
+          in
+            examples.${service} or "";
+        description = ''
+          ${daemonName service} configuration statements.
+        '';
+      };
+
+      vtyListenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          Address to bind to for the VTY interface.
+        '';
+      };
+
+      vtyListenPort = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          TCP Port to bind to for the VTY interface.
+        '';
+      };
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options.services.quagga =
+    {
+
+      zebra = (serviceOptions "zebra") // {
+
+        enable = mkOption {
+          type = types.bool;
+          default = any isEnabled services;
+          example = true;
+          description = ''
+            Whether to enable the Zebra routing manager.
+
+            The Zebra routing manager is automatically enabled
+            if any routing protocols are configured.
+          '';
+        };
+
+      };
+
+    } // (genAttrs services serviceOptions);
+
+  ###### implementation
+
+  config = mkIf (any isEnabled allServices) {
+
+    environment.systemPackages = [
+      pkgs.quagga               # for the vtysh tool
+    ];
+
+    users.users.quagga = {
+      description = "Quagga daemon user";
+      isSystemUser = true;
+      group = "quagga";
+    };
+
+    users.groups = {
+      quagga = {};
+      # Members of the quaggavty group can use vtysh to inspect the Quagga daemons
+      quaggavty = {};
+    };
+
+    systemd.services =
+      let
+        quaggaService = service:
+          let
+            scfg = cfg.${service};
+            daemon = daemonName service;
+          in
+            nameValuePair daemon ({
+              wantedBy = [ "multi-user.target" ];
+              restartTriggers = [ (configFile service) ];
+
+              serviceConfig = {
+                Type = "forking";
+                PIDFile = "/run/quagga/${daemon}.pid";
+                ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}"
+                  + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
+                  + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}";
+                ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+                Restart = "on-abort";
+              };
+            } // (
+              if service == "zebra" then
+                {
+                  description = "Quagga Zebra routing manager";
+                  unitConfig.Documentation = "man:zebra(8)";
+                  after = [ "network.target" ];
+                  preStart = ''
+                    install -m 0755 -o quagga -g quagga -d /run/quagga
+
+                    ${pkgs.iproute}/bin/ip route flush proto zebra
+                  '';
+                }
+              else
+                {
+                  description = "Quagga ${toUpper service} routing daemon";
+                  unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)";
+                  bindsTo = [ "zebra.service" ];
+                  after = [ "network.target" "zebra.service" ];
+                }
+            ));
+       in
+         listToAttrs (map quaggaService (filter isEnabled allServices));
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ tavyc ];
+
+}
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
index 99269c49e8f..edcc12170b2 100644
--- a/nixos/modules/services/networking/quassel.nix
+++ b/nixos/modules/services/networking/quassel.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  quassel = pkgs.kde4.quasselDaemon;
   cfg = config.services.quassel;
+  quassel = cfg.package;
   user = if cfg.user != null then cfg.user else "quassel";
 in
 
@@ -23,6 +23,16 @@ in
         '';
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.kde4.quasselDaemon;
+        defaultText = "pkgs.kde4.quasselDaemon";
+        description = ''
+          The package of the quassel daemon.
+        '';
+        example = literalExample "pkgs.quasselDaemon";
+      };
+
       interfaces = mkOption {
         default = [ "127.0.0.1" ];
         description = ''
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix
index 19762f4e570..f9300fdabc5 100644
--- a/nixos/modules/services/networking/radicale.nix
+++ b/nixos/modules/services/networking/radicale.nix
@@ -33,7 +33,7 @@ in
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.pythonPackages.radicale ];
+    environment.systemPackages = [ pkgs.radicale ];
 
     users.extraUsers = singleton
       { name = "radicale";
@@ -50,10 +50,9 @@ in
 
     systemd.services.radicale = {
       description = "A Simple Calendar and Contact Server";
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      script = "${pkgs.pythonPackages.radicale}/bin/radicale -C ${confFile} -d";
-      serviceConfig.Type = "forking";
+      script = "${pkgs.radicale}/bin/radicale -C ${confFile} -f";
       serviceConfig.User = "radicale";
       serviceConfig.Group = "radicale";
     };
diff --git a/nixos/modules/services/networking/skydns.nix b/nixos/modules/services/networking/skydns.nix
index ba913482e3c..6ad18bb2240 100644
--- a/nixos/modules/services/networking/skydns.nix
+++ b/nixos/modules/services/networking/skydns.nix
@@ -11,7 +11,7 @@ in {
 
     etcd = {
       machines = mkOption {
-        default = [ "http://localhost:4001" ];
+        default = [ "http://127.0.0.1:2379" ];
         type = types.listOf types.str;
         description = "Skydns list of etcd endpoints to connect to.";
       };
diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix
index f7a5926dc64..04312c39062 100644
--- a/nixos/modules/services/networking/smokeping.nix
+++ b/nixos/modules/services/networking/smokeping.nix
@@ -6,31 +6,39 @@ let
   cfg = config.services.smokeping;
   smokepingHome = "/var/lib/smokeping";
   smokepingPidDir = "/run";
-  configFile = ''
-    *** General ***
-    owner = ${cfg.owner}
-    contact = ${cfg.ownerEmail}
-    mailhost = ${cfg.mailHost}
-    #sendmail = /var/setuid-wrappers/sendmail
-    imgcache = ${smokepingHome}/cache
-    imgurl   = http://${cfg.hostName}:${builtins.toString cfg.port}/cache
-    datadir  = ${smokepingHome}/data
-    piddir  = ${smokepingPidDir}
-    cgiurl   = http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi
-    smokemail = ${cfg.smokeMailTemplate}
-    *** Presentation ***
-    template = ${cfg.presentationTemplate}
-    ${cfg.presentationConfig}
-    #*** Alerts ***
-    #${cfg.alertConfig}
-    *** Database ***
-    ${cfg.databaseConfig}
-    *** Probes ***
-    ${cfg.probeConfig}
-    *** Targets ***
-    ${cfg.targetConfig}
-    ${cfg.extraConfig}
-  '';
+  configFile =
+    if cfg.config == null
+      then
+        ''
+          *** General ***
+          cgiurl   = ${cfg.cgiUrl}
+          contact = ${cfg.ownerEmail}
+          datadir  = ${smokepingHome}/data
+          imgcache = ${smokepingHome}/cache
+          imgurl   = ${cfg.imgUrl}
+          linkstyle = ${cfg.linkStyle}
+          ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"}
+          owner = ${cfg.owner}
+          pagedir = ${smokepingHome}/cache
+          piddir  = ${smokepingPidDir}
+          ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"}
+          smokemail = ${cfg.smokeMailTemplate}
+          *** Presentation ***
+          template = ${cfg.presentationTemplate}
+          ${cfg.presentationConfig}
+          *** Alerts ***
+          ${cfg.alertConfig}
+          *** Database ***
+          ${cfg.databaseConfig}
+          *** Probes ***
+          ${cfg.probeConfig}
+          *** Targets ***
+          ${cfg.targetConfig}
+          ${cfg.extraConfig}
+        ''
+      else
+        cfg.config;
+
   configPath = pkgs.writeText "smokeping.conf" configFile;
   cgiHome = pkgs.writeScript "smokeping.fcgi" ''
     #!${pkgs.bash}/bin/bash
@@ -46,58 +54,36 @@ in
         default = false;
         description = "Enable the smokeping service";
       };
-      webService = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Enable a smokeping web interface";
-      };
-
-      user = mkOption {
-        type = types.string;
-        default = "smokeping";
-        description = "User that runs smokeping and (optionally) thttpd";
-      };
-      mailHost = mkOption {
-        type = types.string;
-        default = "127.0.0.1";
-        description = "Use this SMTP server rather than localhost";
-      };
-      smokeMailTemplate = mkOption {
+      alertConfig = mkOption {
         type = types.string;
-        default = "${cfg.package}/etc/smokemail.dist";
-        description = "Specify the smokemail template for alerts.";
-      };
+        default = ''
+          to = root@localhost
+          from = smokeping@localhost
+        '';
+        example = literalExample ''
+          to = alertee@address.somewhere
+          from = smokealert@company.xy
 
-      package = mkOption {
-        type = types.package;
-        default = pkgs.smokeping;
-        description = "Specify a custom smokeping package";
-      };
-      owner = mkOption {
-        type = types.string;
-        default = "nobody";
-        example = "Joe Admin";
-        description = "Real name of the owner of the instance";
+          +someloss
+          type = loss
+          # in percent
+          pattern = >0%,*12*,>0%,*12*,>0%
+          comment = loss 3 times  in a row;
+        '';
+        description = "Configuration for alerts.";
       };
-      hostName = mkOption {
+      cgiUrl = mkOption {
         type = types.string;
-        default = config.networking.hostName;
-        example = "somewhere.example.com";
-        description = "DNS name for the urls generated in the cgi.";
-      };
-      port = mkOption {
-        type = types.int;
-        default = 8081;
-        example = 8081;
-        description = "TCP port to use for the web server.";
+        default = "http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi";
+        example = "https://somewhere.example.com/smokeping.cgi";
+        description = "URL to the smokeping cgi.";
       };
-      ownerEmail = mkOption {
-        type = types.string;
-        default = "no-reply@${cfg.hostName}";
-        example = "no-reply@yourdomain.com";
-        description = "Email contact for owner";
+      config = mkOption {
+        type = types.nullOr types.string;
+        default = null;
+        description = "Full smokeping config supplied by the user. Overrides " +
+          "and replaces any other configuration supplied.";
       };
-
       databaseConfig = mkOption {
         type = types.string;
         default = ''
@@ -130,27 +116,59 @@ in
           Once set, changing the interval will require deletion or migration of all
           the collected data.'';
       };
-      alertConfig = mkOption {
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Any additional customization not already included.";
+      };
+      hostName = mkOption {
+        type = types.string;
+        default = config.networking.hostName;
+        example = "somewhere.example.com";
+        description = "DNS name for the urls generated in the cgi.";
+      };
+      imgUrl = mkOption {
+        type = types.string;
+        default = "http://${cfg.hostName}:${builtins.toString cfg.port}/cache";
+        example = "https://somewhere.example.com/cache";
+        description = "Base url for images generated in the cgi.";
+      };
+      linkStyle = mkOption {
+        type = types.enum ["original" "absolute" "relative"];
+        default = "relative";
+        example = "absolute";
+        description = "DNS name for the urls generated in the cgi.";
+      };
+      mailHost = mkOption {
         type = types.string;
         default = "";
-        example = literalExample ''
-          to = alertee@address.somewhere
-          from = smokealert@company.xy
-
-          +someloss
-          type = loss
-          # in percent
-          pattern = >0%,*12*,>0%,*12*,>0%
-          comment = loss 3 times  in a row;
-        '';
-        description = "Configuration for alerts.";
+        example = "localhost";
+        description = "Use this SMTP server to send alerts";
       };
-      presentationTemplate = mkOption {
+      owner = mkOption {
         type = types.string;
-        default = "${pkgs.smokeping}/etc/basepage.html.dist";
-        description = "Default page layout for the web UI.";
+        default = "nobody";
+        example = "Joe Admin";
+        description = "Real name of the owner of the instance";
+      };
+      ownerEmail = mkOption {
+        type = types.string;
+        default = "no-reply@${cfg.hostName}";
+        example = "no-reply@yourdomain.com";
+        description = "Email contact for owner";
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.smokeping;
+        defaultText = "pkgs.smokeping";
+        description = "Specify a custom smokeping package";
+      };
+      port = mkOption {
+        type = types.int;
+        default = 8081;
+        example = 8081;
+        description = "TCP port to use for the web server.";
       };
-
       presentationConfig = mkOption {
         type = types.string;
         default = ''
@@ -192,14 +210,30 @@ in
         '';
         description = "presentation graph style";
       };
+      presentationTemplate = mkOption {
+        type = types.string;
+        default = "${pkgs.smokeping}/etc/basepage.html.dist";
+        description = "Default page layout for the web UI.";
+      };
       probeConfig = mkOption {
         type = types.string;
         default = ''
           + FPing
-          binary = ${pkgs.fping}/bin/fping
+          binary = ${config.security.wrapperDir}/fping
         '';
         description = "Probe configuration";
       };
+      sendmail = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/setuid-wrappers/sendmail";
+        description = "Use this sendmail compatible script to deliver alerts";
+      };
+      smokeMailTemplate = mkOption {
+        type = types.string;
+        default = "${cfg.package}/etc/smokemail.dist";
+        description = "Specify the smokemail template for alerts.";
+      };
       targetConfig = mkOption {
         type = types.string;
         default = ''
@@ -218,17 +252,29 @@ in
         '';
         description = "Target configuration";
       };
-      extraConfig = mkOption {
+      user = mkOption {
         type = types.string;
-        default = "";
-        description = "Any additional customization not already included.";
+        default = "smokeping";
+        description = "User that runs smokeping and (optionally) thttpd";
+      };
+      webService = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Enable a smokeping web interface";
       };
-
     };
 
   };
 
   config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !(cfg.sendmail != null && cfg.mailHost != "");
+        message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
+      }
+    ];
+    security.setuidPrograms = [ "fping" ];
+    environment.systemPackages = [ pkgs.fping ];
     users.extraUsers = singleton {
       name = cfg.user;
       isNormalUser = false;
@@ -243,9 +289,12 @@ in
       serviceConfig.PermissionsStartOnly = true;
       preStart = ''
         mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
-        chown -R ${cfg.user} ${smokepingHome}
+        rm -f ${smokepingHome}/cropper
+        ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
         cp ${cgiHome} ${smokepingHome}/smokeping.fcgi
         ${cfg.package}/bin/smokeping --check --config=${configPath}
+        ${cfg.package}/bin/smokeping --static --config=${configPath}
+        chown -R ${cfg.user} ${smokepingHome}
       '';
       script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon'';
     };
@@ -253,8 +302,9 @@ in
       wantedBy = [ "multi-user.target"];
       requires = [ "smokeping.service"];
       partOf = [ "smokeping.service"];
-      path = with pkgs; [ bash rrdtool smokeping ];
-      script = ''${pkgs.thttpd}/bin/thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D'';
+      path = with pkgs; [ bash rrdtool smokeping thttpd ];
+      script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos'';
+      serviceConfig.Restart = "always";
     };
   };
 }
diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix
index 5e49efc3aa3..16530078b97 100644
--- a/nixos/modules/services/networking/softether.nix
+++ b/nixos/modules/services/networking/softether.nix
@@ -63,7 +63,6 @@ in
         ];
       systemd.services."softether-init" = {
         description = "SoftEther VPN services initial task";
-        wantedBy = [ "network-interfaces.target" ];
         serviceConfig = {
           Type = "oneshot";
           RemainAfterExit = false;
@@ -84,8 +83,9 @@ in
     (mkIf (cfg.vpnserver.enable) {
       systemd.services.vpnserver = {
         description = "SoftEther VPN Server";
-        after = [ "softether-init.service" ];
-        wantedBy = [ "network-interfaces.target" ];
+        after = [ "softether-init.service" "network.target" ];
+        wants = [ "softether-init.service" ];
+        wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           Type = "forking";
           ExecStart = "${pkg}/bin/vpnserver start";
@@ -104,8 +104,9 @@ in
     (mkIf (cfg.vpnbridge.enable) {
       systemd.services.vpnbridge = {
         description = "SoftEther VPN Bridge";
-        after = [ "softether-init.service" ];
-        wantedBy = [ "network-interfaces.target" ];
+        after = [ "softether-init.service" "network.target" ];
+        wants = [ "softether-init.service" ];
+        wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           Type = "forking";
           ExecStart = "${pkg}/bin/vpnbridge start";
@@ -124,8 +125,9 @@ in
     (mkIf (cfg.vpnclient.enable) {
       systemd.services.vpnclient = {
         description = "SoftEther VPN Client";
-        after = [ "softether-init.service" ];
-        wantedBy = [ "network-interfaces.target" ];
+        after = [ "softether-init.service" "network.target" ];
+        wants = [ "softether-init.service" ];
+        wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           Type = "forking";
           ExecStart = "${pkg}/bin/vpnclient start";
diff --git a/nixos/modules/services/networking/ssh/lshd.nix b/nixos/modules/services/networking/ssh/lshd.nix
index 661a6a52463..eca599afb33 100644
--- a/nixos/modules/services/networking/ssh/lshd.nix
+++ b/nixos/modules/services/networking/ssh/lshd.nix
@@ -120,7 +120,7 @@ in
     systemd.services.lshd = {
       description = "GNU lshd SSH2 daemon";
 
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
       wantedBy = [ "multi-user.target" ];
 
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index f900ef494ab..80659f19c59 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -85,7 +85,7 @@ in
 
       forwardX11 = mkOption {
         type = types.bool;
-        default = cfgc.setXAuthLocation;
+        default = false;
         description = ''
           Whether to allow X11 connections to be forwarded.
         '';
@@ -102,8 +102,8 @@ in
       };
 
       permitRootLogin = mkOption {
-        default = "without-password";
-        type = types.enum ["yes" "without-password" "forced-commands-only" "no"];
+        default = "prohibit-password";
+        type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"];
         description = ''
           Whether the root user can login using ssh.
         '';
@@ -129,7 +129,24 @@ in
       };
 
       listenAddresses = mkOption {
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule {
+          options = {
+            addr = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                Host, IPv4 or IPv6 address to listen to.
+              '';
+            };
+            port = mkOption {
+              type = types.nullOr types.int;
+              default = null;
+              description = ''
+                Port to listen to.
+              '';
+            };
+          };
+        });
         default = [];
         example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ];
         description = ''
@@ -140,22 +157,6 @@ in
           NOTE: setting this option won't automatically enable given ports
           in firewall configuration.
         '';
-        options = {
-          addr = mkOption {
-            type = types.nullOr types.str;
-            default = null;
-            description = ''
-              Host, IPv4 or IPv6 address to listen to.
-            '';
-          };
-          port = mkOption {
-            type = types.nullOr types.int;
-            default = null;
-            description = ''
-              Port to listen to.
-            '';
-          };
-        };
       };
 
       passwordAuthentication = mkOption {
@@ -239,7 +240,7 @@ in
 
     systemd =
       let
-        service =
+        sshd-service =
           { description = "SSH Daemon";
 
             wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
@@ -250,16 +251,8 @@ in
 
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
-            preStart =
-              ''
-                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
-                '')}
-              '';
+            wants = [ "sshd-keygen.service" ];
+            after = [ "sshd-keygen.service" ];
 
             serviceConfig =
               { ExecStart =
@@ -271,10 +264,29 @@ in
                 StandardInput = "socket";
               } else {
                 Restart = "always";
-                Type = "forking";
-                PIDFile = "/run/sshd.pid";
+                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 {
@@ -286,11 +298,13 @@ in
             socketConfig.Accept = true;
           };
 
-        services."sshd@" = service;
+        services.sshd-keygen = sshd-keygen-service;
+        services."sshd@" = sshd-service;
 
       } else {
 
-        services.sshd = service;
+        services.sshd-keygen = sshd-keygen-service;
+        services.sshd = sshd-service;
 
       };
 
@@ -307,8 +321,6 @@ in
 
     services.openssh.extraConfig = mkOrder 0
       ''
-        PidFile /run/sshd.pid
-
         Protocol 2
 
         UsePAM yes
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 16c4ee7e33b..0c459fb1dd0 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -34,7 +34,8 @@ let
       '';
     in
       { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}";
-        wantedBy = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ] ++ deps;
+        wants = [ "network.target" ];
         bindsTo = deps;
         after = deps;
         before = [ "network.target" ];
@@ -75,7 +76,107 @@ in
   options = {
 
     networking.supplicant = mkOption {
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
+        options = {
+  
+          configFile = {
+  
+            path = mkOption {
+              type = types.path;
+              example = literalExample "/etc/wpa_supplicant.conf";
+              description = ''
+                External <literal>wpa_supplicant.conf</literal> configuration file.
+                The configuration options defined declaratively within <literal>networking.supplicant</literal> have
+                precedence over options defined in <literal>configFile</literal>.
+              '';
+            };
+  
+            writable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether the configuration file at <literal>configFile.path</literal> should be written to by
+                <literal>wpa_supplicant</literal>.
+              '';
+            };
+  
+          };
+  
+          extraConf = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              ap_scan=1
+              device_name=My-NixOS-Device
+              device_type=1-0050F204-1
+              driver_param=use_p2p_group_interface=1
+              disable_scan_offload=1
+              p2p_listen_reg_class=81
+              p2p_listen_channel=1
+              p2p_oper_reg_class=81
+              p2p_oper_channel=1
+              manufacturer=NixOS
+              model_name=NixOS_Unstable
+              model_number=2015
+            '';
+            description = ''
+              Configuration options for <literal>wpa_supplicant.conf</literal>.
+              Options defined here have precedence over options in <literal>configFile</literal>.
+              NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will
+              be world-readable in the <literal>nix-store</literal>. For sensitive information
+              use the <literal>configFile</literal> instead.
+            '';
+          };
+  
+          extraCmdArgs = mkOption {
+            type = types.str;
+            default = "";
+            example = "-e/var/run/wpa_supplicant/entropy.bin";
+            description =
+              "Command line arguments to add when executing <literal>wpa_supplicant</literal>.";
+          };
+  
+          driver = mkOption {
+            type = types.nullOr types.str;
+            default = "nl80211,wext";
+            description = "Force a specific wpa_supplicant driver.";
+          };
+  
+          bridge = mkOption {
+            type = types.str;
+            default = "";
+            description = "Name of the bridge interface that wpa_supplicant should listen at.";
+          };
+  
+          userControlled = {
+  
+            enable = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
+                This is useful for laptop users that switch networks a lot and don't want
+                to depend on a large package such as NetworkManager just to pick nearby
+                access points.
+              '';
+            };
+  
+            socketDir = mkOption {
+              type = types.str;
+              default = "/var/run/wpa_supplicant";
+              description = "Directory of sockets for controlling wpa_supplicant.";
+            };
+  
+            group = mkOption {
+              type = types.str;
+              default = "wheel";
+              example = "network";
+              description = "Members of this group can control wpa_supplicant.";
+            };
+  
+          };
+        };
+      });
 
       default = { };
 
@@ -109,107 +210,6 @@ in
         service that can be accessed through <literal>D-Bus</literal>.
       '';
 
-      options = {
-
-        configFile = {
-
-          path = mkOption {
-            type = types.path;
-            example = literalExample "/etc/wpa_supplicant.conf";
-            description = ''
-              External <literal>wpa_supplicant.conf</literal> configuration file.
-              The configuration options defined declaratively within <literal>networking.supplicant</literal> have
-              precedence over options defined in <literal>configFile</literal>.
-            '';
-          };
-
-          writable = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Whether the configuration file at <literal>configFile.path</literal> should be written to by
-              <literal>wpa_supplicant</literal>.
-            '';
-          };
-
-        };
-
-        extraConf = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            ap_scan=1
-            device_name=My-NixOS-Device
-            device_type=1-0050F204-1
-            driver_param=use_p2p_group_interface=1
-            disable_scan_offload=1
-            p2p_listen_reg_class=81
-            p2p_listen_channel=1
-            p2p_oper_reg_class=81
-            p2p_oper_channel=1
-            manufacturer=NixOS
-            model_name=NixOS_Unstable
-            model_number=2015
-          '';
-          description = ''
-            Configuration options for <literal>wpa_supplicant.conf</literal>.
-            Options defined here have precedence over options in <literal>configFile</literal>.
-            NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will
-            be world-readable in the <literal>nix-store</literal>. For sensitive information
-            use the <literal>configFile</literal> instead.
-          '';
-        };
-
-        extraCmdArgs = mkOption {
-          type = types.str;
-          default = "";
-          example = "-e/var/run/wpa_supplicant/entropy.bin";
-          description =
-            "Command line arguments to add when executing <literal>wpa_supplicant</literal>.";
-        };
-
-        driver = mkOption {
-          type = types.nullOr types.str;
-          default = "nl80211,wext";
-          description = "Force a specific wpa_supplicant driver.";
-        };
-
-        bridge = mkOption {
-          type = types.str;
-          default = "";
-          description = "Name of the bridge interface that wpa_supplicant should listen at.";
-        };
-
-        userControlled = {
-
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = ''
-              Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
-              This is useful for laptop users that switch networks a lot and don't want
-              to depend on a large package such as NetworkManager just to pick nearby
-              access points.
-            '';
-          };
-
-          socketDir = mkOption {
-            type = types.str;
-            default = "/var/run/wpa_supplicant";
-            description = "Directory of sockets for controlling wpa_supplicant.";
-          };
-
-          group = mkOption {
-            type = types.str;
-            default = "wheel";
-            example = "network";
-            description = "Members of this group can control wpa_supplicant.";
-          };
-
-        };
-
-      };
-
     };
 
   };
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 8a430734319..dcdc203bdc6 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -3,46 +3,11 @@
 with lib;
 
 let
-
   cfg = config.services.syncthing;
   defaultUser = "syncthing";
-
-  header = {
-    description = "Syncthing service";
-    after = [ "network.target" ];
-    environment = {
-      STNORESTART = "yes";
-      STNOUPGRADE = "yes";
-      inherit (cfg) all_proxy;
-    } // config.networking.proxy.envVars;
-  };
-
-  service = {
-    Restart = "on-failure";
-    SuccessExitStatus = "2 3 4";
-    RestartForceExitStatus="3 4";
-  };
-
-  iNotifyHeader = {
-    description = "Syncthing Inotify File Watcher service";
-    after = [ "network.target" "syncthing.service" ];
-    requires = [ "syncthing.service" ];
-  };
-
-  iNotifyService = {
-    SuccessExitStatus = "2";
-    RestartForceExitStatus = "3";
-    Restart = "on-failure";
-  };
-
-in
-
-{
-
+in {
   ###### interface
-
   options = {
-
     services.syncthing = {
 
       enable = mkEnableOption ''
@@ -100,6 +65,19 @@ in
         '';
       };
 
+      openDefaultPorts = mkOption {
+        type = types.bool;
+        default = false;
+        example = literalExample "true";
+        description = ''
+          Open the default ports in the firewall:
+            - TCP 22000 for transfers
+            - UDP 21027 for discovery
+          If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled.
+          Alternatively, if are running only a single instance on this machine using the default ports, enable this.
+        '';
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.syncthing;
@@ -117,6 +95,14 @@ in
 
   config = mkIf cfg.enable {
 
+    networking.firewall = mkIf cfg.openDefaultPorts {
+      allowedTCPPorts = [ 22000 ];
+      allowedUDPPorts = [ 21027 ];
+    };
+
+    systemd.packages = [ pkgs.syncthing ]
+                       ++ lib.optional cfg.useInotify pkgs.syncthing-inotify;
+
     users = mkIf (cfg.user == defaultUser) {
       extraUsers."${defaultUser}" =
         { group = cfg.group;
@@ -131,39 +117,44 @@ in
     };
 
     systemd.services = {
-      syncthing = mkIf cfg.systemService (header // {
-          wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ];
-          wantedBy = [ "multi-user.target" ];
-          serviceConfig = service // {
-            User = cfg.user;
-            Group = cfg.group;
-            PermissionsStartOnly = true;
-            ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}";
-          };
-      });
-
-      syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) (iNotifyHeader // {
+      syncthing = mkIf cfg.systemService {
+        description = "Syncthing service";
+        after = [ "network.target" ];
+        environment = {
+          STNORESTART = "yes";
+          STNOUPGRADE = "yes";
+          inherit (cfg) all_proxy;
+        } // config.networking.proxy.envVars;
+        wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ];
         wantedBy = [ "multi-user.target" ];
-        serviceConfig = iNotifyService // {
+        serviceConfig = {
+          Restart = "on-failure";
+          SuccessExitStatus = "2 3 4";
+          RestartForceExitStatus="3 4";
           User = cfg.user;
-          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0";
+          Group = cfg.group;
+          PermissionsStartOnly = true;
+          ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}";
         };
-      });
-    };
+      };
 
-    systemd.user.services = {
-      syncthing = header // {
-        serviceConfig = service // {
-          ExecStart = "${cfg.package}/bin/syncthing -no-browser";
-        };
+      syncthing-resume = {
+        wantedBy = [ "suspend.target" ];
       };
 
-      syncthing-inotify = mkIf cfg.useInotify (iNotifyHeader // {
-        serviceConfig = iNotifyService // {
-          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -logflags=0";
+      syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) {
+        description = "Syncthing Inotify File Watcher service";
+        after = [ "network.target" "syncthing.service" ];
+        requires = [ "syncthing.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          SuccessExitStatus = "2";
+          RestartForceExitStatus = "3";
+          Restart = "on-failure";
+          User = cfg.user;
+          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0";
         };
-      });
+      };
     };
-
   };
 }
diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix
index 267653abce0..2f304165eb4 100644
--- a/nixos/modules/services/networking/tcpcrypt.nix
+++ b/nixos/modules/services/networking/tcpcrypt.nix
@@ -39,7 +39,7 @@ in
       description = "tcpcrypt";
 
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
       path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ];
 
diff --git a/nixos/modules/services/networking/tftpd.nix b/nixos/modules/services/networking/tftpd.nix
index 9b3cc6b8ec4..c9c0a2b321d 100644
--- a/nixos/modules/services/networking/tftpd.nix
+++ b/nixos/modules/services/networking/tftpd.nix
@@ -13,12 +13,13 @@ with lib;
       default = false;
       description = ''
         Whether to enable tftpd, a Trivial File Transfer Protocol server.
+        The server will be run as an xinetd service.
       '';
     };
 
     services.tftpd.path = mkOption {
       type = types.path;
-      default = "/home/tftp";
+      default = "/srv/tftp";
       description = ''
         Where the tftp server files are stored.
       '';
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index 8da0f817ae2..f8e68fda7fc 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -18,94 +18,96 @@ in
 
       networks = mkOption {
         default = { };
-        type = types.loaOf types.optionSet;
+        type = with types; loaOf (submodule {
+          options = {
+
+            extraConfig = mkOption {
+              default = "";
+              type = types.lines;
+              description = ''
+                Extra lines to add to the tinc service configuration file.
+              '';
+            };
+
+            name = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The name of the node which is used as an identifier when communicating
+                with the remote nodes in the mesh. If null then the hostname of the system
+                is used.
+              '';
+            };
+
+            ed25519PrivateKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                Path of the private ed25519 keyfile.
+              '';
+            };
+
+            debugLevel = mkOption {
+              default = 0;
+              type = types.addCheck types.int (l: l >= 0 && l <= 5);
+              description = ''
+                The amount of debugging information to add to the log. 0 means little
+                logging while 5 is the most logging. <command>man tincd</command> for
+                more details.
+              '';
+            };
+
+            hosts = mkOption {
+              default = { };
+              type = types.loaOf 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.
+              '';
+            };
+
+            interfaceType = mkOption {
+              default = "tun";
+              type = types.enum [ "tun" "tap" ];
+              description = ''
+                The type of virtual interface used for the network connection
+              '';
+            };
+
+            listenAddress = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              description = ''
+                The ip adress to bind to.
+              '';
+            };
+
+            package = mkOption {
+              type = types.package;
+              default = pkgs.tinc_pre;
+              defaultText = "pkgs.tinc_pre";
+              description = ''
+                The package to use for the tinc daemon's binary.
+              '';
+            };
+
+            chroot = mkOption {
+              default = true;
+              type = types.bool;
+              description = ''
+                Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
+                The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
+
+                Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
+              '';
+            };
+          };
+        });
+
         description = ''
           Defines the tinc networks which will be started.
           Each network invokes a different daemon.
         '';
-        options = {
-
-          extraConfig = mkOption {
-            default = "";
-            type = types.lines;
-            description = ''
-              Extra lines to add to the tinc service configuration file.
-            '';
-          };
-
-          name = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The name of the node which is used as an identifier when communicating
-              with the remote nodes in the mesh. If null then the hostname of the system
-              is used.
-            '';
-          };
-
-          ed25519PrivateKeyFile = mkOption {
-            default = null;
-            type = types.nullOr types.path;
-            description = ''
-              Path of the private ed25519 keyfile.
-            '';
-          };
-
-          debugLevel = mkOption {
-            default = 0;
-            type = types.addCheck types.int (l: l >= 0 && l <= 5);
-            description = ''
-              The amount of debugging information to add to the log. 0 means little
-              logging while 5 is the most logging. <command>man tincd</command> for
-              more details.
-            '';
-          };
-
-          hosts = mkOption {
-            default = { };
-            type = types.loaOf 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.
-            '';
-          };
-
-          interfaceType = mkOption {
-            default = "tun";
-            type = types.addCheck types.str (n: n == "tun" || n == "tap");
-            description = ''
-              The type of virtual interface used for the network connection
-            '';
-          };
-
-          listenAddress = mkOption {
-            default = null;
-            type = types.nullOr types.str;
-            description = ''
-              The ip adress to bind to.
-            '';
-          };
-
-          package = mkOption {
-            type = types.package;
-            default = pkgs.tinc_pre;
-            defaultText = "pkgs.tinc_pre";
-            description = ''
-              The package to use for the tinc daemon's binary.
-            '';
-          };
-
-          chroot = mkOption {
-            default = true;
-            type = types.bool;
-            description = ''
-              Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
-              The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
-
-              Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
-            '';
-          };
-        };
       };
     };
 
@@ -149,8 +151,8 @@ in
       ("tinc.${network}")
       ({
         description = "Tinc Daemon - ${network}";
-        wantedBy = [ "network.target" ];
-        after = [ "network-interfaces.target" ];
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
         path = [ data.package ];
         restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ]
           ++ mapAttrsToList (host: _ : config.environment.etc."tinc/${network}/hosts/${host}".source) data.hosts;
diff --git a/nixos/modules/services/networking/toxvpn.nix b/nixos/modules/services/networking/toxvpn.nix
index c38424c8e27..911836fdee4 100644
--- a/nixos/modules/services/networking/toxvpn.nix
+++ b/nixos/modules/services/networking/toxvpn.nix
@@ -25,8 +25,8 @@ with lib;
     systemd.services.toxvpn = {
       description = "toxvpn daemon";
 
-      requires = [ "network-online.target" ]; # consider replacing by NetworkManager-wait-online.service
       wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
 
       preStart = ''
         mkdir -p /run/toxvpn || true
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index ed0744c44cc..f3a04d97c98 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -12,9 +12,17 @@ let
 
   interfaces = concatMapStrings (x: "  interface: ${x}\n") cfg.interfaces;
 
-  forward = optionalString (length cfg.forwardAddresses != 0)
-    "forward-zone:\n  name: .\n" +
-    concatMapStrings (x: "  forward-addr: ${x}\n") cfg.forwardAddresses;
+  isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1";
+
+  forward =
+    optionalString (any isLocalAddress cfg.forwardAddresses) ''
+      do-not-query-localhost: no
+    '' +
+    optionalString (cfg.forwardAddresses != []) ''
+      forward-zone:
+        name: .
+    '' +
+    concatMapStringsSep "\n" (x: "    forward-addr: ${x}") cfg.forwardAddresses;
 
   rootTrustAnchorFile = "${stateDir}/root.key";
 
@@ -71,8 +79,12 @@ in
 
       extraConfig = mkOption {
         default = "";
-        type = types.str;
-        description = "Extra lines of unbound config.";
+        type = types.lines;
+        description = ''
+          Extra unbound config. See
+          <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8
+          </manvolnum></citerefentry>.
+        '';
       };
 
     };
@@ -84,12 +96,9 @@ in
 
     environment.systemPackages = [ pkgs.unbound ];
 
-    users.extraUsers = singleton {
-      name = "unbound";
-      uid = config.ids.uids.unbound;
+    users.users.unbound = {
       description = "unbound daemon user";
-      home = stateDir;
-      createHome = true;
+      isSystemUser = true;
     };
 
     systemd.services.unbound = {
@@ -107,12 +116,16 @@ in
         chown unbound ${stateDir} ${rootTrustAnchorFile}
         ''}
         touch ${stateDir}/dev/random
-        ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random
+        ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random
       '';
 
       serviceConfig = {
         ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf";
         ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
+
+        ProtectSystem = true;
+        ProtectHome = true;
+        PrivateDevices = true;
       };
     };
 
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 7ec484941ed..deff645d9bf 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -100,6 +100,10 @@ let
         seccomp_sandbox=NO
       ''}
       anon_umask=${cfg.anonymousUmask}
+      ${optionalString cfg.anonymousUser ''
+        anon_root=${cfg.anonymousUserHome}
+      ''}
+      ${cfg.extraConfig}
     '';
 
 in
@@ -163,6 +167,13 @@ in
         description = "Anonymous write umask.";
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "ftpd_banner=Hello";
+        description = "Extra configuration to add at the bottom of the generated configuration file.";
+      };
+
     } // (listToAttrs (catAttrs "nixosOption" optionDescription));
 
   };
diff --git a/nixos/modules/services/networking/wicd.nix b/nixos/modules/services/networking/wicd.nix
index 9e5a437b485..03c6bd28aab 100644
--- a/nixos/modules/services/networking/wicd.nix
+++ b/nixos/modules/services/networking/wicd.nix
@@ -26,7 +26,9 @@ with lib;
     environment.systemPackages = [pkgs.wicd];
 
     systemd.services.wicd = {
-      after = [ "network-interfaces.target" ];
+      after = [ "network-pre.target" ];
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       script = "${pkgs.wicd}/sbin/wicd -f";
     };
diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
new file mode 100644
index 00000000000..368d89e2e32
--- /dev/null
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -0,0 +1,231 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.wireguard;
+
+  kernel = config.boot.kernelPackages;
+
+  # interface options
+
+  interfaceOpts = { name, ... }: {
+
+    options = {
+
+      ips = mkOption {
+        example = [ "192.168.2.1/24" ];
+        default = [];
+        type = with types; listOf str;
+        description = "The IP addresses of the interface.";
+      };
+
+      privateKey = mkOption {
+        example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+        type = types.str;
+        description = "Base64 private key generated by wg genkey.";
+      };
+
+      presharedKey = mkOption {
+        default = null;
+        example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I=";
+        type = with types; nullOr str;
+        description = ''
+          base64 preshared key generated by wg genpsk. Optional,
+          and may be omitted. This option adds an additional layer of
+          symmetric-key cryptography to be mixed into the already existing
+          public-key  cryptography, for post-quantum resistance.
+        '';
+      };
+
+      listenPort = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 51820;
+        description = ''
+          16-bit port for listening. Optional; if not specified,
+          automatically generated based on interface name.
+        '';
+      };
+
+      preSetup = mkOption {
+        example = literalExample [''
+          ${pkgs.iproute}/bin/ip netns add foo
+        ''];
+        default = [];
+        type = with types; listOf str;
+        description = ''
+          A list of commands called at the start of the interface setup.
+        '';
+      };
+
+      postSetup = mkOption {
+        example = literalExample [''
+          ${pkgs.bash} -c 'printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0'
+        ''];
+        default = [];
+        type = with types; listOf str;
+        description = "A list of commands called at the end of the interface setup.";
+      };
+
+      postShutdown = mkOption {
+        example = literalExample ["${pkgs.openresolv}/bin/resolvconf -d wg0"];
+        default = [];
+        type = with types; listOf str;
+        description = "A list of commands called after shutting down the interface.";
+      };
+
+      peers = mkOption {
+        default = [];
+        description = "Peers linked to the interface.";
+        type = with types; listOf (submodule peerOpts);
+      };
+
+    };
+
+  };
+
+  # peer options
+
+  peerOpts = {
+
+    options = {
+
+      publicKey = mkOption {
+        example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+        type = types.str;
+        description = "The base64 public key the peer.";
+      };
+
+      allowedIPs = mkOption {
+        example = [ "10.192.122.3/32" "10.192.124.1/24" ];
+        type = with types; listOf str;
+        description = ''List of IP (v4 or v6) addresses with CIDR masks from
+        which this peer is allowed to send incoming traffic and to which
+        outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may
+        be specified for matching all IPv4 addresses, and ::/0 may be specified
+        for matching all IPv6 addresses.'';
+      };
+
+      endpoint = mkOption {
+        default = null;
+        example = "demo.wireguard.io:12913";
+        type = with types; nullOr str;
+        description = ''Endpoint IP or hostname of the peer, followed by a colon,
+        and then a port number of the peer.'';
+      };
+
+      persistentKeepalive = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 25;
+        description = ''This is optional and is by default off, because most
+        users will not need it. It represents, in seconds, between 1 and 65535
+        inclusive, how often to send an authenticated empty packet to the peer,
+        for the purpose of keeping a stateful firewall or NAT mapping valid
+        persistently. For example, if the interface very rarely sends traffic,
+        but it might at anytime receive traffic from a peer, and it is behind
+        NAT, the interface might benefit from having a persistent keepalive
+        interval of 25 seconds; however, most users will not need this.'';
+      };
+
+    };
+
+  };
+
+  generateConf = name: values: pkgs.writeText "wireguard-${name}.conf" ''
+    [Interface]
+    PrivateKey = ${values.privateKey}
+    ${optionalString (values.presharedKey != null) "PresharedKey = ${values.presharedKey}"}
+    ${optionalString (values.listenPort != null)   "ListenPort = ${toString values.listenPort}"}
+
+    ${concatStringsSep "\n\n" (map (peer: ''
+    [Peer]
+    PublicKey = ${peer.publicKey}
+    ${optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep ", " peer.allowedIPs}"}
+    ${optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}"}
+    ${optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}"}
+    '') values.peers)}
+  '';
+
+  ipCommand = "${pkgs.iproute}/bin/ip";
+  wgCommand = "${pkgs.wireguard}/bin/wg";
+
+  generateUnit = name: values:
+    nameValuePair "wireguard-${name}"
+      {
+        description = "WireGuard Tunnel - ${name}";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = lib.flatten([
+            values.preSetup
+
+            "-${ipCommand} link del dev ${name}"
+            "${ipCommand} link add dev ${name} type wireguard"
+            "${wgCommand} setconf ${name} ${generateConf name values}"
+
+            (map (ip:
+            ''${ipCommand} address add ${ip} dev ${name}''
+            ) values.ips)
+
+            "${ipCommand} link set up dev ${name}"
+
+            (flatten (map (peer: (map (ip:
+            "${ipCommand} route add ${ip} dev ${name}"
+            ) peer.allowedIPs)) values.peers))
+
+            values.postSetup
+          ]);
+
+          ExecStop = [ ''${ipCommand} link del dev "${name}"'' ] ++ values.postShutdown;
+        };
+      };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.wireguard = {
+
+      interfaces = mkOption {
+        description = "Wireguard interfaces.";
+        default = {};
+        example = {
+          wg0 = {
+            ips = [ "192.168.20.4/24" ];
+            privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=";
+            peers = [
+              { allowedIPs = [ "192.168.20.1/32" ];
+                publicKey  = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=";
+                endpoint   = "demo.wireguard.io:12913"; }
+            ];
+          };
+        };
+        type = with types; attrsOf (submodule interfaceOpts);
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.interfaces != {}) {
+
+    boot.extraModulePackages = [ kernel.wireguard ];
+    environment.systemPackages = [ pkgs.wireguard ];
+
+    systemd.services = mapAttrs' generateUnit cfg.interfaces;
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index de99ce4f026..c91ba91fcb4 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -12,11 +12,13 @@ let
       psk = if networkConfig.psk != null
         then ''"${networkConfig.psk}"''
         else networkConfig.pskRaw;
+      priority = networkConfig.priority;
     in ''
       network={
         ssid="${ssid}"
         ${optionalString (psk != null) ''psk=${psk}''}
         ${optionalString (psk == null) ''key_mgmt=NONE''}
+        ${optionalString (priority != null) ''priority=${toString priority}''}
       }
     '') cfg.networks)}
   '' else "/etc/wpa_supplicant.conf";
@@ -68,6 +70,19 @@ in {
                 Mutually exclusive with <varname>psk</varname>.
               '';
             };
+            priority = mkOption {
+              type = types.nullOr types.int;
+              default = null;
+              description = ''
+                By default, all networks will get same priority group (0). If some of the
+                networks are more desirable, this field can be used to change the order in
+                which wpa_supplicant goes through the networks when selecting a BSS. The
+                priority groups will be iterated in decreasing priority (i.e., the larger the
+                priority value, the sooner the network is matched against the scan results).
+                Within each priority group, networks will be selected based on security
+                policy, signal strength, etc.
+              '';
+            };
           };
         });
         description = ''
@@ -128,9 +143,11 @@ in {
     in {
       description = "WPA Supplicant";
 
-      after = [ "network-interfaces.target" ] ++ lib.concatMap deviceUnit ifaces;
+      after = lib.concatMap deviceUnit ifaces;
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
       requires = lib.concatMap deviceUnit ifaces;
-      wantedBy = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
 
       path = [ pkgs.wpa_supplicant ];
 
diff --git a/nixos/modules/services/networking/xinetd.nix b/nixos/modules/services/networking/xinetd.nix
index 08680b51780..00224502780 100644
--- a/nixos/modules/services/networking/xinetd.nix
+++ b/nixos/modules/services/networking/xinetd.nix
@@ -65,71 +65,73 @@ in
         A list of services provided by xinetd.
       '';
 
-      type = types.listOf types.optionSet;
+      type = with types; listOf (submodule ({
+
+        options = {
+
+          name = mkOption {
+            type = types.string;
+            example = "login";
+            description = "Name of the service.";
+          };
+
+          protocol = mkOption {
+            type = types.string;
+            default = "tcp";
+            description =
+              "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
+          };
+
+          port = mkOption {
+            type = types.int;
+            default = 0;
+            example = 123;
+            description = "Port number of the service.";
+          };
+
+          user = mkOption {
+            type = types.string;
+            default = "nobody";
+            description = "User account for the service";
+          };
+
+          server = mkOption {
+            type = types.string;
+            example = "/foo/bin/ftpd";
+            description = "Path of the program that implements the service.";
+          };
+
+          serverArgs = mkOption {
+            type = types.string;
+            default = "";
+            description = "Command-line arguments for the server program.";
+          };
+
+          flags = mkOption {
+            type = types.string;
+            default = "";
+            description = "";
+          };
+
+          unlisted = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              Whether this server is listed in
+              <filename>/etc/services</filename>.  If so, the port
+              number can be omitted.
+            '';
+          };
+
+          extraConfig = mkOption {
+            type = types.lines;
+            default = "";
+            description = "Extra configuration-lines added to the section of the service.";
+          };
 
-      options = {
-
-        name = mkOption {
-          type = types.string;
-          example = "login";
-          description = "Name of the service.";
-        };
-
-        protocol = mkOption {
-          type = types.string;
-          default = "tcp";
-          description =
-            "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 0;
-          example = 123;
-          description = "Port number of the service.";
-        };
-
-        user = mkOption {
-          type = types.string;
-          default = "nobody";
-          description = "User account for the service";
-        };
-
-        server = mkOption {
-          type = types.string;
-          example = "/foo/bin/ftpd";
-          description = "Path of the program that implements the service.";
-        };
-
-        serverArgs = mkOption {
-          type = types.string;
-          default = "";
-          description = "Command-line arguments for the server program.";
-        };
-
-        flags = mkOption {
-          type = types.string;
-          default = "";
-          description = "";
-        };
-
-        unlisted = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Whether this server is listed in
-            <filename>/etc/services</filename>.  If so, the port
-            number can be omitted.
-          '';
-        };
-
-        extraConfig = mkOption {
-          type = types.string;
-          default = "";
-          description = "Extra configuration-lines added to the section of the service.";
         };
 
-      };
+      }));
 
     };
 
@@ -141,7 +143,7 @@ in
   config = mkIf cfg.enable {
     systemd.services.xinetd = {
       description = "xinetd server";
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.xinetd ];
       script = "xinetd -syslog daemon -dontfork -stayalive -f ${configFile}";
diff --git a/nixos/modules/services/networking/zerobin.nix b/nixos/modules/services/networking/zerobin.nix
index 1c524602f8e..274bbca53fa 100644
--- a/nixos/modules/services/networking/zerobin.nix
+++ b/nixos/modules/services/networking/zerobin.nix
@@ -86,15 +86,15 @@ in
 
       systemd.services.zerobin = {
         enable = true;
-        after = [ "network-interfaces.target" ];
+        after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
         serviceConfig.ExecStart = "${pkgs.pythonPackages.zerobin}/bin/zerobin ${cfg.listenAddress} ${toString cfg.listenPort} false ${cfg.user} ${cfg.group} ${zerobin_config}";
         serviceConfig.PrivateTmp="yes";
         serviceConfig.User = cfg.user;
         serviceConfig.Group = cfg.group;
         preStart = ''
-        mkdir -p ${cfg.dataDir}
-        chown ${cfg.user} ${cfg.dataDir}
+          mkdir -p ${cfg.dataDir}
+          chown ${cfg.user} ${cfg.dataDir}
         '';
       };
     };
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
index 196a14dd40e..76ba78ff366 100644
--- a/nixos/modules/services/networking/znc.nix
+++ b/nixos/modules/services/networking/znc.nix
@@ -26,53 +26,35 @@ let
   };
 
   # Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
+  notNull = a: ! isNull a;
   mkZncConf = confOpts: ''
-    // Also check http://en.znc.in/wiki/Configuration
-    
-    AnonIPLimit = 10
-    ConnectDelay = 5
-    # Add `LoadModule = x` for each module...
+    Version = 1.6.3
     ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules}
-    MaxBufferSize = 500
-    ProtectWebSessions = true
-    SSLCertFile = ${cfg.dataDir}/znc.pem
-    ServerThrottle = 30
-    Skin = dark-clouds
-    StatusPrefix = *
-    Version = 1.2
-
-    <Listener listener0>
-            AllowIRC = true
-            AllowWeb = true
+
+    <Listener l>
+            Port = ${toString confOpts.port}
             IPv4 = true
-            IPv6 = false
-            Port = ${if confOpts.useSSL then "+" else ""}${toString confOpts.port}
+            IPv6 = true
             SSL = ${if confOpts.useSSL then "true" else "false"}
     </Listener>
     
     <User ${confOpts.userName}>
+            ${confOpts.passBlock}
             Admin = true
-            Allow = *
-            AltNick = ${confOpts.nick}_
-            AppendTimestamp = false
-            AutoClearChanBuffer = false
-            Buffer = 150
-            ChanModes = +stn
-            DenyLoadMod = false
-            DenySetBindHost = false
-            Ident = ident
-            JoinTries = 10
-            MaxJoins = 0
-            MaxNetworks = 1
-            MultiClients = true
             Nick = ${confOpts.nick}
-            PrependTimestamp = true
-            QuitMsg = Quit
+            AltNick = ${confOpts.nick}_
+            Ident = ${confOpts.nick}
             RealName = ${confOpts.nick}
-            TimestampFormat = [%H:%M:%S]
             ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.userModules}
-            
-            ${confOpts.passBlock}
+
+            ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: net: ''
+              <Network ${name}>
+                  ${concatMapStrings (m: "LoadModule = ${m}\n") net.modules}
+                  Server = ${net.server} ${if net.useSSL then "+" else ""}${toString net.port}
+
+                  ${concatMapStrings (c: "<Chan #${c}>\n</Chan>\n") net.channels}
+              </Network>
+              '') confOpts.networks) }
     </User>
     ${confOpts.extraZncConf}
   '';
@@ -84,6 +66,62 @@ let
       else mkZncConf cfg.confOptions;
   };
 
+  networkOpts = { ... }: {
+    options = {
+      server = mkOption {
+        type = types.str;
+        example = "chat.freenode.net";
+        description = ''
+          IRC server address.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 6697;
+        example = 6697;
+        description = ''
+          IRC server port.
+        '';
+      };
+
+      useSSL = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to use SSL to connect to the IRC server.
+        '';
+      };
+
+      modulePackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = [ "pkgs.zncModules.push" "pkgs.zncModules.fish" ];
+        description = ''
+          External ZNC modules to build.
+        '';
+      };
+
+      modules = mkOption {
+        type = types.listOf types.str;
+        default = [ "simple_away" ];
+        example = literalExample "[ simple_away sasl ]";
+        description = ''
+          ZNC modules to load.
+        '';
+      };
+
+      channels = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "nixos" ];
+        description = ''
+          IRC channels to join.
+        '';
+      };
+    };
+  };
+
 in
 
 {
@@ -111,6 +149,15 @@ in
         '';
       };
 
+      group = mkOption {
+        default = "";
+        example = "users";
+        type = types.string;
+        description = ''
+          Group to own the ZNCserver process.
+        '';
+      };
+
       dataDir = mkOption {
         default = "/var/lib/znc/";
         example = "/home/john/.znc/";
@@ -125,27 +172,16 @@ in
         example = "See: http://wiki.znc.in/Configuration";
         type = types.lines;
         description = ''
-          The contents of the `znc.conf` file to use when creating it.
+          Config file as generated with `znc --makeconf` to use for the whole ZNC configuration.
           If specified, `confOptions` will be ignored, and this value, as-is, will be used.
           If left empty, a conf file with default values will be used.
-          Recommended to generate with `znc --makeconf` command.
         '';
       };
 
-      /* TODO: add to the documentation of the current module:
-
-         Values to use when creating a `znc.conf` file.
-
-           confOptions = {
-             modules = [ "log" ];
-             userName = "john";
-             nick = "johntron";
-           };
-      */
       confOptions = {
         modules = mkOption {
           type = types.listOf types.str;
-          default = [ "partyline" "webadmin" "adminlog" "log" ];
+          default = [ "webadmin" "adminlog" ];
           example = [ "partyline" "webadmin" "adminlog" "log" ];
           description = ''
             A list of modules to include in the `znc.conf` file.
@@ -154,8 +190,8 @@ in
 
         userModules = mkOption {
           type = types.listOf types.str;
-          default = [ ];
-          example = [ "fish" "push" ];
+          default = [ "chansaver" "controlpanel" ];
+          example = [ "chansaver" "controlpanel" "fish" "push" ];
           description = ''
             A list of user modules to include in the `znc.conf` file.
           '';
@@ -166,9 +202,24 @@ in
           example = "johntron";
           type = types.string;
           description = ''
-            The user name to use when generating the `znc.conf` file.
-            This is the user name used by the user logging into the ZNC web admin.
+            The user name used to log in to the ZNC web admin interface.
+          '';
+        };
+
+        networks = mkOption {
+          default = { };
+          type = with types; loaOf (submodule networkOpts);
+          description = ''
+            IRC networks to connect the user to.
           '';
+          example = {
+            "freenode" = {
+              server = "chat.freenode.net";
+              port = 6697;
+              ssl = true;
+              modules = [ "simple_away" ];
+            };
+          };
         };
 
         nick = mkOption {
@@ -176,19 +227,16 @@ in
           example = "john";
           type = types.string;
           description = ''
-            The IRC nick to use when generating the `znc.conf` file.
+            The IRC nick.
           '';
         };
 
         passBlock = mkOption {
-          default = defaultPassBlock;
-          example = "Must be the block generated by the `znc --makepass` command.";
+          example = defaultPassBlock;
           type = types.string;
           description = ''
-            The pass block to use when generating the `znc.conf` file.
-            This is the password used by the user logging into the ZNC web admin.
-            This is the block generated by the `znc --makepass` command.
-            !!! If not specified, please change this after starting the service. !!!
+            Generate with znc --makepass.
+            This is the password used to log in to the ZNC web admin interface.
           '';
         };
 
@@ -206,7 +254,7 @@ in
           example = true;
           type = types.bool;
           description = ''
-            Indicates whether the ZNC server should use SSL when listening on the specified port.
+            Indicates whether the ZNC server should use SSL when listening on the specified port. A self-signed certificate will be generated.
           '';
         };
 
@@ -214,7 +262,7 @@ in
           default = "";
           type = types.lines;
           description = ''
-            Extra config to `znc.conf` file
+            Extra config to `znc.conf` file.
           '';
         };
       };
@@ -265,6 +313,7 @@ in
       after = [ "network.service" ];
       serviceConfig = {
         User = cfg.user;
+        Group = cfg.group;
         Restart = "always";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         ExecStop   = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 368d7ac761a..3041dccfd15 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -75,7 +75,7 @@ let
     '') cfg.listenAddresses}
     Listen /var/run/cups/cups.sock
 
-    SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin
+    SetEnv PATH /var/lib/cups/path/lib/cups/filter:/var/lib/cups/path/bin
 
     DefaultShared ${if cfg.defaultShared then "Yes" else "No"}
 
@@ -310,6 +310,13 @@ in
             for i in *; do
               [ ! -e "/var/lib/cups/$i" ] && ln -s "${rootdir}/etc/cups/$i" "/var/lib/cups/$i"
             done
+
+            #update path reference
+            [ -L /var/lib/cups/path ] && \
+              rm /var/lib/cups/path
+            [ ! -e /var/lib/cups/path ] && \
+              ln -s ${bindir} /var/lib/cups/path
+
             ${optionalString cfg.gutenprint ''
               if [ -d /var/lib/cups/ppd ]; then
                 ${gutenprint}/bin/cups-genppdupdate -p /var/lib/cups/ppd
diff --git a/nixos/modules/services/scheduling/chronos.nix b/nixos/modules/services/scheduling/chronos.nix
index db1f0f5f00c..6c39997fec8 100644
--- a/nixos/modules/services/scheduling/chronos.nix
+++ b/nixos/modules/services/scheduling/chronos.nix
@@ -41,7 +41,7 @@ in {
     systemd.services.chronos = {
       description = "Chronos Service";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" "zookeeper.service" ];
+      after = [ "network.target" "zookeeper.service" ];
 
       serviceConfig = {
         ExecStart = "${pkgs.chronos}/bin/chronos --master ${cfg.master} --zk_hosts ${concatStringsSep "," cfg.zookeeperHosts} --http_port ${toString cfg.httpPort}";
diff --git a/nixos/modules/services/scheduling/marathon.nix b/nixos/modules/services/scheduling/marathon.nix
index 4e837c62dc1..19c9a708f21 100644
--- a/nixos/modules/services/scheduling/marathon.nix
+++ b/nixos/modules/services/scheduling/marathon.nix
@@ -83,7 +83,7 @@ in {
       description = "Marathon Service";
       environment = cfg.environment;
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" "zookeeper.service" "mesos-master.service" "mesos-slave.service" ];
+      after = [ "network.target" "zookeeper.service" "mesos-master.service" "mesos-slave.service" ];
 
       serviceConfig = {
         ExecStart = "${pkgs.marathon}/bin/marathon --master ${cfg.master} --zk zk://${concatStringsSep "," cfg.zookeeperHosts}/marathon --http_port ${toString cfg.httpPort} ${concatStringsSep " " cfg.extraCmdLineOptions}";
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index 9299aaac2f7..574f74d547a 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -129,7 +129,7 @@ in {
     systemd.services.elasticsearch = {
       description = "Elasticsearch Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
diff --git a/nixos/modules/services/search/hound.nix b/nixos/modules/services/search/hound.nix
new file mode 100644
index 00000000000..a94a851e80e
--- /dev/null
+++ b/nixos/modules/services/search/hound.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.hound;
+in {
+  options = {
+    services.hound = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the hound code search daemon.
+        '';
+      };
+
+      user = mkOption {
+        default = "hound";
+        type = types.str;
+        description = ''
+          User the hound daemon should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "hound";
+        type = types.str;
+        description = ''
+          Group the hound daemon should execute under.
+        '';
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "dialout" ];
+        description = ''
+          List of extra groups that the "hound" user should be a part of.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/hound";
+        type = types.path;
+        description = ''
+          The path to use as hound's $HOME. If the default user
+          "hound" is configured then this is the home of the "hound"
+          user.
+        '';
+      };
+
+      package = mkOption {
+        default = pkgs.hound;
+        defaultText = "pkgs.hound";
+        type = types.package;
+        description = ''
+          Package for running hound.
+        '';
+      };
+
+      config = mkOption {
+        type = types.str;
+        description = ''
+          The full configuration of the Hound daemon. Note the dbpath
+          should be an absolute path to a writable location on disk.
+        '';
+        example = ''
+          {
+             "max-concurrent-indexers" : 2,
+             "dbpath" : "''${services.hound.home}/data",
+             "repos" : {
+                "nixpkgs": {
+                   "url" : "https://www.github.com/NixOS/nixpkgs.git"
+                }
+             }
+          }
+        '';
+      };
+
+      listen = mkOption {
+        type = types.str;
+        default = "0.0.0.0:6080";
+        example = "127.0.0.1:6080 or just :6080";
+        description = ''
+          Listen on this IP:port / :port
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups = optional (cfg.group == "hound") {
+      name = "hound";
+      gid = config.ids.gids.hound;
+    };
+
+    users.extraUsers = optional (cfg.user == "hound") {
+      name = "hound";
+      description = "hound code search";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      extraGroups = cfg.extraGroups;
+      uid = config.ids.uids.hound;
+    };
+
+    systemd.services.hound = {
+      description = "Hound Code Search";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        WorkingDirectory = cfg.home;
+        ExecStartPre = "${pkgs.git}/bin/git config --global --replace-all http.sslCAinfo /etc/ssl/certs/ca-certificates.crt";
+        ExecStart = "${cfg.package}/bin/houndd" +
+                    " -addr ${cfg.listen}" +
+                    " -conf ${pkgs.writeText "hound.json" cfg.config}";
+
+      };
+      path = [ pkgs.git pkgs.mercurial pkgs.openssh ];
+    };
+  };
+
+}
diff --git a/nixos/modules/services/search/kibana.nix b/nixos/modules/services/search/kibana.nix
index 033b8139d34..d377a6feeb8 100644
--- a/nixos/modules/services/search/kibana.nix
+++ b/nixos/modules/services/search/kibana.nix
@@ -138,7 +138,7 @@ in {
     systemd.services.kibana = {
       description = "Kibana Service";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" "elasticsearch.service" ];
+      after = [ "network.target" "elasticsearch.service" ];
       environment = { BABEL_CACHE_PATH = "${cfg.dataDir}/.babelcache.json"; };
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/kibana --config ${cfgFile}";
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index e4e5c1253b7..b045e140546 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -3,26 +3,37 @@ with lib;
 let
   clamavUser = "clamav";
   stateDir = "/var/lib/clamav";
-  runDir = "/var/run/clamav";
-  logDir = "/var/log/clamav";
+  runDir = "/run/clamav";
   clamavGroup = clamavUser;
   cfg = config.services.clamav;
+  pkg = pkgs.clamav;
+
   clamdConfigFile = pkgs.writeText "clamd.conf" ''
     DatabaseDirectory ${stateDir}
     LocalSocket ${runDir}/clamd.ctl
-    LogFile ${logDir}/clamav.log
     PidFile ${runDir}/clamd.pid
+    TemporaryDirectory /tmp
     User clamav
+    Foreground yes
 
     ${cfg.daemon.extraConfig}
   '';
-  pkg = pkgs.clamav.override { freshclamConf = cfg.updater.config; };
+
+  freshclamConfigFile = pkgs.writeText "freshclam.conf" ''
+    DatabaseDirectory ${stateDir}
+    Foreground yes
+    Checks ${toString cfg.updater.frequency}
+
+    ${cfg.updater.extraConfig}
+
+    DatabaseMirror database.clamav.net
+  '';
 in
 {
   options = {
     services.clamav = {
       daemon = {
-        enable = mkEnableOption "clamd daemon";
+        enable = mkEnableOption "ClamAV clamd daemon";
 
         extraConfig = mkOption {
           type = types.lines;
@@ -34,16 +45,27 @@ in
         };
       };
       updater = {
-        enable = mkEnableOption "freshclam updater";
+        enable = mkEnableOption "ClamAV freshclam updater";
 
         frequency = mkOption {
+          type = types.int;
           default = 12;
           description = ''
             Number of database checks per day.
           '';
         };
 
-        config = mkOption {
+        interval = mkOption {
+          type = types.str;
+          default = "hourly";
+          description = ''
+            How often freshclam is invoked. See systemd.time(7) for more
+            information about the format.
+          '';
+        };
+
+        extraConfig = mkOption {
+          type = types.lines;
           default = "";
           description = ''
             Extra configuration for freshclam. Contents will be added verbatim to the
@@ -68,50 +90,53 @@ in
       gid = config.ids.gids.clamav;
     };
 
-    services.clamav.updater.config = mkIf cfg.updater.enable ''
-      DatabaseDirectory ${stateDir}
-      Foreground yes
-      Checks ${toString cfg.updater.frequency}
-      DatabaseMirror database.clamav.net
-    '';
+    environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
+    environment.etc."clamav/clamd.conf".source = clamdConfigFile;
 
-    systemd.services.clamd = mkIf cfg.daemon.enable {
+    systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
       description = "ClamAV daemon (clamd)";
-      path = [ pkg ];
-      after = [ "network.target" "freshclam.service" ];
-      requires = [ "freshclam.service" ];
+      after = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
+      requires = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
       wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ clamdConfigFile ];
+
       preStart = ''
-        mkdir -m 0755 -p ${logDir}
         mkdir -m 0755 -p ${runDir}
-        chown ${clamavUser}:${clamavGroup} ${logDir}
         chown ${clamavUser}:${clamavGroup} ${runDir}
       '';
+
       serviceConfig = {
-        ExecStart = "${pkg}/bin/clamd --config-file=${clamdConfigFile}";
-        Type = "forking";
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-        Restart = "on-failure";
-        RestartSec = "10s";
-        StartLimitInterval = "1min";
+        ExecStart = "${pkg}/bin/clamd";
+        ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
+        PrivateTmp = "yes";
+        PrivateDevices = "yes";
+        PrivateNetwork = "yes";
       };
     };
 
-    systemd.services.freshclam = mkIf cfg.updater.enable {
-      description = "ClamAV updater (freshclam)";
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkg ];
+    systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
+      description = "Timer for ClamAV virus database updater (freshclam)";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = cfg.updater.interval;
+        Unit = "clamav-freshclam.service";
+      };
+    };
+
+    systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
+      description = "ClamAV virus database updater (freshclam)";
+      restartTriggers = [ freshclamConfigFile ];
+
       preStart = ''
         mkdir -m 0755 -p ${stateDir}
         chown ${clamavUser}:${clamavGroup} ${stateDir}
       '';
+
       serviceConfig = {
-        ExecStart = "${pkg}/bin/freshclam --daemon --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}";
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-        Restart = "on-failure";
-        RestartSec = "10s";
-        StartLimitInterval = "1min";
+        Type = "oneshot";
+        ExecStart = "${pkg}/bin/freshclam";
+        PrivateTmp = "yes";
+        PrivateDevices = "yes";
       };
     };
   };
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index 22e3bb0066c..716ae7a2d2f 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -143,7 +143,7 @@ in
     services.fail2ban.jails.ssh-iptables =
       ''
         filter   = sshd
-        action   = iptables[name=SSH, port=ssh, protocol=tcp]
+        action   = iptables-multiport[name=SSH, port="${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}", protocol=tcp]
         maxretry = 5
       '';
 
diff --git a/nixos/modules/services/security/haveged.nix b/nixos/modules/services/security/haveged.nix
index 2aa523bf70a..eca52918881 100644
--- a/nixos/modules/services/security/haveged.nix
+++ b/nixos/modules/services/security/haveged.nix
@@ -48,14 +48,18 @@ in
       { description = "Entropy Harvesting Daemon";
         unitConfig.Documentation = "man:haveged(8)";
         wantedBy = [ "multi-user.target" ];
-        
+
         path = [ pkgs.haveged ];
-        
-        serviceConfig = 
-          { Type = "forking";
-            ExecStart = "${pkgs.haveged}/sbin/haveged -w ${toString cfg.refill_threshold} -v 1";
-            PIDFile = "/run/haveged.pid";
-          };
+
+        serviceConfig = {
+          ExecStart = "${pkgs.haveged}/bin/haveged -F -w ${toString cfg.refill_threshold} -v 1";
+          SuccessExitStatus = 143;
+          PrivateTmp = true;
+          PrivateDevices = true;
+          PrivateNetwork = true;
+          ProtectSystem = "full";
+          ProtectHome = true;
+        };
       };
 
   };
diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix
index 4c20392214f..caa7d9d5081 100644
--- a/nixos/modules/services/security/oauth2_proxy.nix
+++ b/nixos/modules/services/security/oauth2_proxy.nix
@@ -510,7 +510,7 @@ in
       description = "OAuth2 Proxy";
       path = [ cfg.package ];
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
 
       serviceConfig = {
         User = "oauth2_proxy";
diff --git a/nixos/modules/services/system/cgmanager.nix b/nixos/modules/services/system/cgmanager.nix
new file mode 100644
index 00000000000..59d3deced86
--- /dev/null
+++ b/nixos/modules/services/system/cgmanager.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cgmanager;
+in {
+  meta.maintainers = [ maintainers.mic92 ];
+
+  ###### interface
+  options.services.cgmanager.enable = mkEnableOption "cgmanager";
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    systemd.services.cgmanager = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "local-fs.target" ];
+      description = "Cgroup management daemon";
+      restartIfChanged = false;
+      serviceConfig = {
+        ExecStart = "${pkgs.cgmanager}/bin/cgmanager -m name=systemd";
+        KillMode = "process";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index df93fcd2bdb..d15d5551e34 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -8,7 +8,7 @@ let
 
   cfg = config.services.dbus;
 
-  homeDir = "/var/run/dbus";
+  homeDir = "/run/dbus";
 
   systemExtraxml = concatStrings (flip concatMap cfg.packages (d: [
     "<servicedir>${d}/share/dbus-1/system-services</servicedir>"
@@ -20,15 +20,23 @@ let
     "<includedir>${d}/etc/dbus-1/session.d</includedir>"
   ]));
 
-  configDir = pkgs.stdenv.mkDerivation {
-    name = "dbus-conf";
+  daemonArgs = "--address=systemd: --nofork --nopidfile --systemd-activation";
 
-    preferLocalBuild = true;
-    allowSubstitutes = false;
-
-    buildCommand = ''
+  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.permissionsWrapperDir}/dbus-daemon-launch-helper,g' \
         -e 's,@extra@,${systemExtraxml},' \
@@ -38,7 +46,6 @@ let
         -e 's,@extra@,${sessionExtraxml},' \
         > "$out/session-local.conf"
     '';
-  };
 
 in
 
@@ -75,16 +82,21 @@ in
         '';
       };
 
+      socketActivated = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Make the user instance socket activated.
+        '';
+      };
     };
-
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.dbus.daemon pkgs.dbus_tools ];
+    environment.systemPackages = [ pkgs.dbus.daemon pkgs.dbus ];
 
     environment.etc = singleton
       { source = configDir;
@@ -104,7 +116,7 @@ in
 
     security.permissionsWrappers.setuid = singleton
       { program = "dbus-daemon-launch-helper";
-        source = "${pkgs.dbus_daemon}/libexec/dbus-daemon-launch-helper";
+        source = "${pkgs.dbus.daemon}/libexec/dbus-daemon-launch-helper";
         owner = "root";
         group = "messagebus";
         setuid = true;
@@ -117,13 +129,29 @@ in
       config.system.path
     ];
 
-    # Don't restart dbus-daemon. Bad things tend to happen if we do.
-    systemd.services.dbus.reloadIfChanged = true;
+    systemd.services.dbus = {
+      # 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.services.dbus.restartTriggers = [ configDir ];
+    systemd.user = {
+      services.dbus = {
+        # 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" ];
+    };
 
     environment.pathsToLink = [ "/etc/dbus-1" "/share/dbus-1" ];
-
   };
-
 }
diff --git a/nixos/modules/services/system/nscd.nix b/nixos/modules/services/system/nscd.nix
index d98ef8a306d..eb4b5281c7c 100644
--- a/nixos/modules/services/system/nscd.nix
+++ b/nixos/modules/services/system/nscd.nix
@@ -9,8 +9,6 @@ let
 
   inherit (lib) singleton;
 
-  cfgFile = pkgs.writeText "nscd.conf" cfg.config;
-
 in
 
 {
@@ -41,6 +39,7 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
+    environment.etc."nscd.conf".text = cfg.config;
 
     users.extraUsers.nscd =
       { isSystemUser = true;
@@ -61,10 +60,14 @@ in
             mkdir -m 0755 -p /var/db/nscd
           '';
 
-        restartTriggers = [ config.environment.etc.hosts.source config.environment.etc."nsswitch.conf".source ];
+        restartTriggers = [
+          config.environment.etc.hosts.source
+          config.environment.etc."nsswitch.conf".source
+          config.environment.etc."nscd.conf".source
+        ];
 
         serviceConfig =
-          { ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd -f ${cfgFile}";
+          { ExecStart = "@${pkgs.glibc.bin}/sbin/nscd nscd";
             Type = "forking";
             PIDFile = "/run/nscd/nscd.pid";
             Restart = "always";
@@ -79,7 +82,7 @@ in
         # its pid. So wait until it's ready.
         postStart =
           ''
-            while ! ${pkgs.glibc.bin}/sbin/nscd -g -f ${cfgFile} > /dev/null; do
+            while ! ${pkgs.glibc.bin}/sbin/nscd -g > /dev/null; do
               sleep 0.2
             done
           '';
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index becd57055d4..a9c08b66eb8 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -5,26 +5,36 @@ with lib;
 let
   cfg = config.services.deluge;
   cfg_web = config.services.deluge.web;
+  openFilesLimit = 4096;
+
 in {
   options = {
-    services.deluge = {
-      enable = mkOption {
-        default = false;
-        example = true;
-        description = ''
-          Start Deluge daemon.
-        ''; 
-      };  
-    };
+    services = {
+      deluge = {
+        enable = mkOption {
+          default = false;
+          example = true;
+          description = "Start the Deluge daemon";
+        };
+
+        openFilesLimit = mkOption {
+          default = openFilesLimit;
+          example = 8192;
+          description = ''
+            Number of files to allow deluged to open.
+          '';
+        };
+      };
 
-    services.deluge.web = {
-      enable = mkOption {
-        default = false;
-        example = true;
-        description = ''
-          Start Deluge Web daemon.
-        ''; 
-      };  
+      deluge.web = {
+        enable = mkOption {
+          default = false;
+          example = true;
+          description = ''
+            Start Deluge Web daemon.
+          '';
+        };
+      };
     };
   };
 
@@ -35,11 +45,14 @@ in {
       description = "Deluge BitTorrent Daemon";
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.pythonPackages.deluge ];
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d";
-      # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it!
-      serviceConfig.Restart = "on-success";
-      serviceConfig.User = "deluge";
-      serviceConfig.Group = "deluge";
+      serviceConfig = {
+        ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d";
+        # To prevent "Quit & shutdown daemon" from working; we want systemd to manage it!
+        Restart = "on-success";
+        User = "deluge";
+        Group = "deluge";
+        LimitNOFILE = cfg.openFilesLimit;
+      };
     };
 
     systemd.services.delugeweb = mkIf cfg_web.enable {
diff --git a/nixos/modules/services/torrent/flexget.nix b/nixos/modules/services/torrent/flexget.nix
index 1252aa1c549..4b9038e3e25 100644
--- a/nixos/modules/services/torrent/flexget.nix
+++ b/nixos/modules/services/torrent/flexget.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.flexget;
-  pkg = pkgs.python27Packages.flexget;
+  pkg = pkgs.flexget;
   ymlFile = pkgs.writeText "flexget.yml" ''
     ${cfg.config}
 
@@ -54,12 +54,12 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.python27Packages.flexget ];
+    environment.systemPackages = [ pkg ];
 
     systemd.services = {
       flexget = {
         description = "FlexGet Daemon";
-        path = [ pkgs.pythonPackages.flexget ];
+        path = [ pkg ];
         serviceConfig = {
           User = cfg.user;
           Environment = "TZ=${config.time.timeZone}";
diff --git a/nixos/modules/services/torrent/opentracker.nix b/nixos/modules/services/torrent/opentracker.nix
new file mode 100644
index 00000000000..74f443381d9
--- /dev/null
+++ b/nixos/modules/services/torrent/opentracker.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.opentracker;
+in {
+  options.services.opentracker = {
+    enable = mkEnableOption "opentracker";
+
+    package = mkOption {
+      type = types.package;
+      description = ''
+        opentracker package to use
+      '';
+      default = pkgs.opentracker;
+      defaultText = "pkgs.opentracker";
+    };
+
+    extraOptions = mkOption {
+      type = types.separatedString " ";
+      description = ''
+        Configuration Arguments for opentracker
+        See https://erdgeist.org/arts/software/opentracker/ for all params
+      '';
+      default = "";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    systemd.services.opentracker = {
+      description = "opentracker server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartIfChanged = true;
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/opentracker ${cfg.extraOptions}";
+        PrivateTmp = true;
+        WorkingDirectory = "/var/empty";
+        # By default opentracker drops all privileges and runs in chroot after starting up as root.
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/torrent/peerflix.nix b/nixos/modules/services/torrent/peerflix.nix
index 38fbd3b226c..2e3dd9902d7 100644
--- a/nixos/modules/services/torrent/peerflix.nix
+++ b/nixos/modules/services/torrent/peerflix.nix
@@ -42,7 +42,7 @@ in {
     systemd.services.peerflix = {
       description = "Peerflix Daemon";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       environment.HOME = cfg.stateDir;
 
       preStart = ''
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index ea7196fc873..051d54e932f 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -80,8 +80,7 @@ in
       };
 
     systemd.services."container-getty@" =
-      { unitConfig.ConditionPathExists = "/dev/pts/%I"; # Work around being respawned when "machinectl login" exits.
-        serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM";
+      { serviceConfig.ExecStart = gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM";
         restartIfChanged = false;
       };
 
diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix
new file mode 100644
index 00000000000..2d9287577de
--- /dev/null
+++ b/nixos/modules/services/web-apps/atlassian/confluence.nix
@@ -0,0 +1,141 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.confluence;
+
+  pkg = pkgs.atlassian-confluence;
+
+in
+
+{
+  options = {
+    services.confluence = {
+      enable = mkEnableOption "Atlassian Confluence service";
+
+      user = mkOption {
+        type = types.str;
+        default = "confluence";
+        description = "User which runs confluence.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "confluence";
+        description = "Group which runs confluence.";
+      };
+
+      home = mkOption {
+        type = types.str;
+        default = "/var/lib/confluence";
+        description = "Home directory of the confluence instance.";
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Address to listen on.";
+      };
+
+      listenPort = mkOption {
+        type = types.int;
+        default = 8090;
+        description = "Port to listen on.";
+      };
+
+      catalinaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-Xms1024m" "-Xmx2048m" "-Dconfluence.disable.peopledirectory.all=true" ];
+        description = "Java options to pass to catalina/tomcat.";
+      };
+
+      proxy = {
+        enable = mkEnableOption "proxy support";
+
+        name = mkOption {
+          type = types.str;
+          example = "confluence.example.com";
+          description = "Virtual hostname at the proxy";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 443;
+          example = 80;
+          description = "Port used at the proxy";
+        };
+
+        scheme = mkOption {
+          type = types.str;
+          default = "https";
+          example = "http";
+          description = "Protocol used at the proxy.";
+        };
+      };
+
+      jrePackage = let
+        jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free;
+      in mkOption {
+        type = types.package;
+        default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre;
+        defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre";
+        example = literalExample "pkgs.openjdk8.jre";
+        description = "Java Runtime to use for Confluence. Note that Atlassian recommends the Oracle JRE.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers."${cfg.user}" = {
+      isSystemUser = true;
+      group = cfg.group;
+    };
+
+    users.extraGroups."${cfg.group}" = {};
+
+    systemd.services.confluence = {
+      description = "Atlassian Confluence";
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "postgresql.service" ];
+
+      path = [ cfg.jrePackage ];
+
+      environment = {
+        CONF_USER = cfg.user;
+        JAVA_HOME = "${cfg.jrePackage}";
+        CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
+      };
+
+      preStart = ''
+        mkdir -p ${cfg.home}/{logs,work,temp,deploy}
+
+        mkdir -p /run/confluence
+        ln -sf ${cfg.home}/{logs,work,temp,server.xml} /run/confluence
+        ln -sf ${cfg.home} /run/confluence/home
+
+        chown -R ${cfg.user} ${cfg.home}
+
+        sed -e 's,port="8090",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \
+        '' + (lib.optionalString cfg.proxy.enable ''
+          -e 's,protocol="org.apache.coyote.http11.Http11NioProtocol",protocol="org.apache.coyote.http11.Http11NioProtocol" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}",' \
+        '') + ''
+          ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml
+      '';
+
+      script = "${pkg}/bin/start-confluence.sh -fg";
+      stopScript  = "${pkg}/bin/stop-confluence.sh";
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PrivateTmp = true;
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixos/modules/services/web-apps/atlassian/crowd.nix
new file mode 100644
index 00000000000..ada26f8057b
--- /dev/null
+++ b/nixos/modules/services/web-apps/atlassian/crowd.nix
@@ -0,0 +1,156 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.crowd;
+
+  pkg = pkgs.atlassian-crowd.override {
+    home = cfg.home;
+    port = cfg.listenPort;
+    proxyUrl = "${cfg.proxy.scheme}://${cfg.proxy.name}:${toString cfg.proxy.port}";
+    openidPassword = cfg.openidPassword;
+  };
+
+in
+
+{
+  options = {
+    services.crowd = {
+      enable = mkEnableOption "Atlassian Crowd service";
+
+      user = mkOption {
+        type = types.str;
+        default = "crowd";
+        description = "User which runs Crowd.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "crowd";
+        description = "Group which runs Crowd.";
+      };
+
+      home = mkOption {
+        type = types.str;
+        default = "/var/lib/crowd";
+        description = "Home directory of the Crowd instance.";
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Address to listen on.";
+      };
+
+      listenPort = mkOption {
+        type = types.int;
+        default = 8092;
+        description = "Port to listen on.";
+      };
+
+      openidPassword = mkOption {
+        type = types.str;
+        description = "Application password for OpenID server.";
+      };
+
+      catalinaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-Xms1024m" "-Xmx2048m" ];
+        description = "Java options to pass to catalina/tomcat.";
+      };
+
+      proxy = {
+        enable = mkEnableOption "reverse proxy support";
+
+        name = mkOption {
+          type = types.str;
+          example = "crowd.example.com";
+          description = "Virtual hostname at the proxy";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 443;
+          example = 80;
+          description = "Port used at the proxy";
+        };
+
+        scheme = mkOption {
+          type = types.str;
+          default = "https";
+          example = "http";
+          description = "Protocol used at the proxy.";
+        };
+
+        secure = mkOption {
+          type = types.bool;
+          default = true;
+          example = false;
+          description = "Whether the connections to the proxy should be considered secure.";
+        };
+      };
+
+      jrePackage = let
+        jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free;
+      in mkOption {
+        type = types.package;
+        default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre;
+        defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre";
+        example = literalExample "pkgs.openjdk8.jre";
+        description = "Java Runtime to use for Crowd. Note that Atlassian recommends the Oracle JRE.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers."${cfg.user}" = {
+      isSystemUser = true;
+      group = cfg.group;
+    };
+
+    users.extraGroups."${cfg.group}" = {};
+
+    systemd.services.atlassian-crowd = {
+      description = "Atlassian Crowd";
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "postgresql.service" ];
+
+      path = [ cfg.jrePackage ];
+
+      environment = {
+        JAVA_HOME = "${cfg.jrePackage}";
+        CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
+        CATALINA_TMPDIR = "/tmp";
+      };
+
+      preStart = ''
+        mkdir -p ${cfg.home}/{logs,work,database}
+
+        mkdir -p /run/atlassian-crowd
+        ln -sf ${cfg.home}/{database,work,server.xml} /run/atlassian-crowd
+
+        chown -R ${cfg.user} ${cfg.home}
+
+        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}",' \
+        '') + ''
+          ${pkg}/apache-tomcat/conf/server.xml.dist > ${cfg.home}/server.xml
+      '';
+
+      script = "${pkg}/start_crowd.sh";
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PrivateTmp = true;
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
new file mode 100644
index 00000000000..6e31d20d068
--- /dev/null
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -0,0 +1,149 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.jira;
+
+  pkg = pkgs.atlassian-jira;
+
+in
+
+{
+  options = {
+    services.jira = {
+      enable = mkEnableOption "Atlassian JIRA service";
+
+      user = mkOption {
+        type = types.str;
+        default = "jira";
+        description = "User which runs JIRA.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "jira";
+        description = "Group which runs JIRA.";
+      };
+
+      home = mkOption {
+        type = types.str;
+        default = "/var/lib/jira";
+        description = "Home directory of the JIRA instance.";
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Address to listen on.";
+      };
+
+      listenPort = mkOption {
+        type = types.int;
+        default = 8091;
+        description = "Port to listen on.";
+      };
+
+      catalinaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "-Xms1024m" "-Xmx2048m" ];
+        description = "Java options to pass to catalina/tomcat.";
+      };
+
+      proxy = {
+        enable = mkEnableOption "reverse proxy support";
+
+        name = mkOption {
+          type = types.str;
+          example = "jira.example.com";
+          description = "Virtual hostname at the proxy";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 443;
+          example = 80;
+          description = "Port used at the proxy";
+        };
+
+        scheme = mkOption {
+          type = types.str;
+          default = "https";
+          example = "http";
+          description = "Protocol used at the proxy.";
+        };
+
+        secure = mkOption {
+          type = types.bool;
+          default = true;
+          example = false;
+          description = "Whether the connections to the proxy should be considered secure.";
+        };
+      };
+
+      jrePackage = let
+        jreSwitch = unfree: free: if config.nixpkgs.config.allowUnfree or false then unfree else free;
+      in mkOption {
+        type = types.package;
+        default = jreSwitch pkgs.oraclejre8 pkgs.openjdk8.jre;
+        defaultText = jreSwitch "pkgs.oraclejre8" "pkgs.openjdk8.jre";
+        example = literalExample "pkgs.openjdk8.jre";
+        description = "Java Runtime to use for JIRA. Note that Atlassian recommends the Oracle JRE.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers."${cfg.user}" = {
+      isSystemUser = true;
+      group = cfg.group;
+    };
+
+    users.extraGroups."${cfg.group}" = {};
+
+    systemd.services.atlassian-jira = {
+      description = "Atlassian JIRA";
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "postgresql.service" ];
+
+      path = [ cfg.jrePackage ];
+
+      environment = {
+        JIRA_USER = cfg.user;
+        JIRA_HOME = cfg.home;
+        JAVA_HOME = "${cfg.jrePackage}";
+        CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
+      };
+
+      preStart = ''
+        mkdir -p ${cfg.home}/{logs,work,temp,deploy}
+
+        mkdir -p /run/atlassian-jira
+        ln -sf ${cfg.home}/{logs,work,temp,server.xml} /run/atlassian-jira
+        ln -sf ${cfg.home} /run/atlassian-jira/home
+
+        chown -R ${cfg.user} ${cfg.home}
+
+        sed -e 's,port="8080",port="${toString cfg.listenPort}" address="${cfg.listenAddress}",' \
+        '' + (lib.optionalString cfg.proxy.enable ''
+          -e 's,protocol="HTTP/1.1",protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \
+        '') + ''
+          ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml
+      '';
+
+      script = "${pkg}/bin/start-jira.sh -fg";
+      stopScript  = "${pkg}/bin/stop-jira.sh";
+
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        PrivateTmp = true;
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix
new file mode 100644
index 00000000000..0592d01bf36
--- /dev/null
+++ b/nixos/modules/services/web-apps/nixbot.nix
@@ -0,0 +1,149 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nixbot;
+  pyramidIni = ''
+    ###
+    # app configuration
+    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html
+    ###
+
+    [app:main]
+    use = egg:nixbot
+
+    nixbot.github_token = ${cfg.githubToken}
+    nixbot.bot_name = ${cfg.botName}
+    nixbot.repo = ${cfg.repo}
+    nixbot.pr_repo = ${cfg.prRepo}
+    nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo}
+    nixbot.github_secret = justnotsorandom
+    nixbot.public_url = ${cfg.publicUrl}
+    nixbot.repo_dir = ${cfg.repoDir}
+
+    pyramid.reload_templates = false
+    pyramid.debug_authorization = false
+    pyramid.debug_notfound = false
+    pyramid.debug_routematch = false
+    pyramid.default_locale_name = en
+
+    # By default, the toolbar only appears for clients from IP addresses
+    # '127.0.0.1' and '::1'.
+    # debugtoolbar.hosts = 127.0.0.1 ::1
+
+    ###
+    # wsgi server configuration
+    ###
+
+    [server:main]
+    use = egg:waitress#main
+    host = 0.0.0.0
+    port = 6543
+
+    ###
+    # logging configuration
+    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html
+    ###
+
+    [loggers]
+    keys = root, nixbot
+
+    [handlers]
+    keys = console
+
+    [formatters]
+    keys = generic
+
+    [logger_root]
+    level = INFO
+    handlers = console
+
+    [logger_nixbot]
+    level = INFO
+    handlers =
+    qualname = nixbot
+
+    [handler_console]
+    class = StreamHandler
+    args = (sys.stderr,)
+    level = NOTSET
+    formatter = generic
+
+    [formatter_generic]
+    format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
+  '';
+in {
+  options = {
+    services.nixbot = {
+      enable = mkEnableOption "nixbot";
+
+      botName = mkOption {
+        type = types.str;
+        description = "The bot's github user account name.";
+        default = "nixbot";
+      };
+
+      githubToken = mkOption {
+        type = types.str;
+        description = "The bot's github user account token.";
+        example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+      };
+
+      repo = mkOption {
+        type = types.str;
+        description = "The github repository to check for PRs.";
+        example = "nixos/nixpkgs";
+      };
+
+      prRepo = mkOption {
+        type = types.str;
+        description = "The github repository to push the testing branches to.";
+        example = "nixos/nixpkgs-pr";
+      };
+
+      hydraJobsetsRepo = mkOption {
+        type = types.str;
+        description = "The github repository to push the hydra jobset definitions to.";
+        example = "nixos/hydra-jobsets";
+      };
+
+      publicUrl = mkOption {
+        type = types.str;
+        description = "The public URL the bot is reachable at (Github hook endpoint).";
+        example = "https://nixbot.nixos.org";
+      };
+
+      repoDir = mkOption {
+        type = types.path;
+        description = "The directory the repositories are stored in.";
+        default = "/var/lib/nixbot";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.nixbot = {
+      createHome = true;
+      home = cfg.repoDir;
+    };
+
+    systemd.services.nixbot = let
+      env = pkgs.python3.buildEnv.override {
+        extraLibs = [ pkgs.nixbot ];
+      };
+    in {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni}
+      '';
+
+      serviceConfig = {
+        User = "nixbot";
+        Group = "nogroup";
+        PermissionsStartOnly = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/quassel-webserver.nix b/nixos/modules/services/web-apps/quassel-webserver.nix
new file mode 100644
index 00000000000..d19e4bc5827
--- /dev/null
+++ b/nixos/modules/services/web-apps/quassel-webserver.nix
@@ -0,0 +1,101 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.quassel-webserver;
+  quassel-webserver = cfg.pkg;
+  settings = ''
+    module.exports = {
+      default: {
+        host: '${cfg.quasselCoreHost}',  // quasselcore host
+        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
+        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
+      prefixpath: '${cfg.prefixPath}'  // Configure this if you use a reverse proxy
+    };
+  '';
+  settingsFile = pkgs.writeText "settings-user.js" settings;
+in {
+  options = {
+    services.quassel-webserver = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the quassel webclient service";
+      };
+      pkg = mkOption {
+        default = pkgs.quassel-webserver;
+        defaultText = "pkgs.quassel-webserver";
+        type = types.package;
+        description = "The quassel-webserver package";
+      };
+      quasselCoreHost = mkOption {
+        default = "";
+        type = types.str;
+        description = "The default host of the quassel core";
+      };
+      quasselCorePort = mkOption {
+        default = 4242;
+        type = types.int;
+        description = "The default quassel core port";
+      };
+      initialBacklogLimit = mkOption {
+        default = 20;
+        type = types.int;
+        description = "Amount of backlogs to fetch per buffer on connection";
+      };
+      backlogLimit = mkOption {
+        default = 100;
+        type = types.int;
+        description = "Amount of backlogs to fetch per buffer after first retrieval";
+      };
+      secureCore = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Connect to the core using SSL";
+      };
+      theme = mkOption {
+        default = "default";
+        type = types.str;
+        description = "default or darksolarized";
+      };
+      prefixPath = mkOption {
+        default = "";
+        type = types.str;
+        description = "Configure this if you use a reverse proxy. Must start with a '/'";
+        example = "/quassel";
+      };
+      port = mkOption {
+        default = 60443;
+        type = types.int;
+        description = "The port the quassel webserver should listen on";
+      };
+      useHttps = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Whether the quassel webserver connection should be a https connection";
+      };
+      forceHostAndPort = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Force the users to use the quasselCoreHost and quasselCorePort defaults";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.quassel-webserver = {
+      description = "A web server/client for Quassel";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${quassel-webserver}/lib/node_modules/quassel-webserver/bin/www -p ${toString cfg.port} -m ${if cfg.useHttps == true then "https" else "http"} -c ${settingsFile}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/selfoss.nix b/nixos/modules/services/web-apps/selfoss.nix
new file mode 100644
index 00000000000..5571f77334c
--- /dev/null
+++ b/nixos/modules/services/web-apps/selfoss.nix
@@ -0,0 +1,166 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.selfoss;
+
+  poolName = "selfoss_pool";
+  phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
+
+  dataDir = "/var/lib/selfoss";
+
+  selfoss-config =
+  let
+    db_type = cfg.database.type;
+    default_port = if (db_type == "mysql") then 3306 else 5342;
+  in
+  pkgs.writeText "selfoss-config.ini" ''
+    [globals]
+    ${lib.optionalString (db_type != "sqlite") ''
+      db_type=${db_type}
+      db_host=${cfg.database.host}
+      db_database=${cfg.database.name}
+      db_username=${cfg.database.user}
+      db_password=${cfg.database.password}
+      db_port=${if (cfg.database.port != null) then cfg.database.port
+                    else default_port}
+    ''
+    }
+    ${cfg.extraConfig}
+  '';
+in
+  {
+    options = {
+      services.selfoss = {
+        enable = mkEnableOption "selfoss";
+
+        user = mkOption {
+          type = types.str;
+          default = "nginx";
+          example = "nginx";
+          description = ''
+            User account under which both the service and the web-application run.
+          '';
+        };
+
+        pool = mkOption {
+          type = types.str;
+          default = "${poolName}";
+          description = ''
+            Name of existing phpfpm pool that is used to run web-application.
+            If not specified a pool will be created automatically with
+            default values.
+          '';
+        };
+
+      database = {
+        type = mkOption {
+          type = types.enum ["pgsql" "mysql" "sqlite"];
+          default = "sqlite";
+          description = ''
+            Database to store feeds. Supported are sqlite, pgsql and mysql.
+          '';
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            Host of the database (has no effect if type is "sqlite").
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "tt_rss";
+          description = ''
+            Name of the existing database (has no effect if type is "sqlite").
+          '';
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "tt_rss";
+          description = ''
+            The database user. The user must exist and has access to
+            the specified database (has no effect if type is "sqlite").
+          '';
+        };
+
+        password = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The database user's password (has no effect if type is "sqlite").
+          '';
+        };
+
+        port = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          description = ''
+            The database's port. If not set, the default ports will be
+            provided (5432 and 3306 for pgsql and mysql respectively)
+            (has no effect if type is "sqlite").
+          '';
+        };
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration added to config.ini
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.phpfpm.poolConfigs = mkIf (cfg.pool == "${poolName}") {
+      "${poolName}" = ''
+        listen = "${phpfpmSocketName}";
+        listen.owner = nginx
+        listen.group = nginx
+        listen.mode = 0600
+        user = nginx
+        pm = dynamic
+        pm.max_children = 75
+        pm.start_servers = 10
+        pm.min_spare_servers = 5
+        pm.max_spare_servers = 20
+        pm.max_requests = 500
+        catch_workers_output = 1
+      '';
+    };
+
+    systemd.services.selfoss-config = {
+      serviceConfig.Type = "oneshot";
+      script = ''
+        mkdir -m 755 -p ${dataDir}
+        cd ${dataDir}
+
+        # Delete all but the "data" folder
+        ls | grep -v data | while read line; do rm -rf $line; done || true
+
+        # Create the files
+        cp -r "${pkgs.selfoss}/"* "${dataDir}"
+        ln -sf "${selfoss-config}" "${dataDir}/config.ini"
+        chown -R "${cfg.user}" "${dataDir}"
+        chmod -R 755 "${dataDir}"
+      '';
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    systemd.services.selfoss-update = {
+      serviceConfig = {
+        ExecStart = "${pkgs.php}/bin/php ${dataDir}/cliupdate.php";
+        User = "${cfg.user}";
+      };
+      startAt = "hourly";
+      after = [ "selfoss-config.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index b08070f1e36..5193814da72 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -18,7 +18,6 @@ let
 
   poolName = "tt-rss";
   phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
-  virtualHostName = "tt-rss";
 
   tt-rss-config = pkgs.writeText "config.php" ''
     <?php
@@ -34,10 +33,10 @@ let
       define('MYSQL_CHARSET', 'UTF8');
 
       define('DB_TYPE', '${cfg.database.type}');
-      define('DB_HOST', '${cfg.database.host}');
+      define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}');
       define('DB_USER', '${cfg.database.user}');
       define('DB_NAME', '${cfg.database.name}');
-      define('DB_PASS', '${escape ["'" "\\"] cfg.database.password}');
+      define('DB_PASS', '${optionalString (cfg.database.password != null) (escape ["'" "\\"] cfg.database.password)}');
       define('DB_PORT', '${toString dbPort}');
 
       define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate});
@@ -91,12 +90,21 @@ let
 
       enable = mkEnableOption "tt-rss";
 
+      root = mkOption {
+        type = types.path;
+        default = "/var/lib/tt-rss";
+        example = "/var/lib/tt-rss";
+        description = ''
+          Root of the application.
+        '';
+      };
+
       user = mkOption {
         type = types.str;
         default = "nginx";
         example = "nginx";
         description = ''
-          User account under which both the service and the web-application run.
+          User account under which both the update daemon and the web-application run.
         '';
       };
 
@@ -110,17 +118,13 @@ let
         '';
       };
 
-      # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged
-
-      # virtualHost = mkOption {
-      #   type = types.str;
-      #   default = "${virtualHostName}";
-      #   description = ''
-      #     Name of existing nginx virtual host that is used to run web-application.
-      #     If not specified a host will be created automatically with
-      #     default values.
-      #   '';
-      # };
+      virtualHost = mkOption {
+        type = types.nullOr types.str;
+        default = "tt-rss";
+        description = ''
+          Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
+        '';
+      };
 
       database = {
         type = mkOption {
@@ -132,10 +136,10 @@ let
         };
 
         host = mkOption {
-          type = types.str;
-          default = "localhost";
+          type = types.nullOr types.str;
+          default = null;
           description = ''
-            Host of the database.
+            Host of the database. Leave null to use Unix domain socket.
           '';
         };
 
@@ -362,7 +366,7 @@ let
 
       singleUserMode = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
 
         description = ''
           Operate in single user mode, disables all functionality related to
@@ -445,17 +449,15 @@ let
 
   ###### implementation
 
-  config = let
-    root = "/var/lib/tt-rss";
-  in mkIf cfg.enable {
+  config = mkIf cfg.enable {
 
-    services.phpfpm.poolConfigs = if cfg.pool == "${poolName}" then {
+    services.phpfpm.poolConfigs = mkIf (cfg.pool == "${poolName}") {
       "${poolName}" = ''
         listen = "${phpfpmSocketName}";
         listen.owner = nginx
         listen.group = nginx
         listen.mode = 0600
-        user = nginx
+        user = ${cfg.user}
         pm = dynamic
         pm.max_children = 75
         pm.start_servers = 10
@@ -464,36 +466,26 @@ let
         pm.max_requests = 500
         catch_workers_output = 1
       '';
-    } else {};
-
-    # TODO: Re-enable after https://github.com/NixOS/nixpkgs/pull/15862 is merged
-
-    # services.nginx.virtualHosts = if cfg.virtualHost == "${virtualHostName}" then {
-    #   "${virtualHostName}" = {
-    #     root = "${root}";
-    #     extraConfig = ''
-    #       access_log  /var/log/nginx-${virtualHostName}-access.log;
-    #       error_log   /var/log/nginx-${virtualHostName}-error.log;
-    #     '';
-
-    #     locations."/" = {
-    #       extraConfig = ''
-    #         index index.php;
-    #       '';
-    #     };
-
-    #     locations."~ \.php$" = {
-    #       extraConfig = ''
-    #         fastcgi_split_path_info ^(.+\.php)(/.+)$;
-    #         fastcgi_pass unix:${phpfpmSocketName};
-    #         fastcgi_index index.php;
-    #         fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name;
-
-    #         include ${pkgs.nginx}/conf/fastcgi_params;
-    #       '';
-    #     };
-    #   };
-    # } else {};
+    };
+
+    services.nginx.virtualHosts = mkIf (cfg.virtualHost != null) {
+      "${cfg.virtualHost}" = {
+        root = "${cfg.root}";
+
+        locations."/" = {
+          index = "index.php";
+        };
+
+        locations."~ \.php$" = {
+          extraConfig = ''
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+            fastcgi_pass unix:${phpfpmSocketName};
+            fastcgi_index index.php;
+            fastcgi_param SCRIPT_FILENAME ${cfg.root}/$fastcgi_script_name;
+          '';
+        };
+      };
+    };
 
 
     systemd.services.tt-rss = let
@@ -503,35 +495,34 @@ let
         description = "Tiny Tiny RSS feeds update daemon";
 
         preStart = let
-          callSql = if cfg.database.type == "pgsql" then (e: ''
-                 ${optionalString (cfg.database.password != null)
-                   "PGPASSWORD=${cfg.database.password}"} ${pkgs.postgresql95}/bin/psql \
-                     -U ${cfg.database.user}                                            \
-                     -h ${cfg.database.host}                                            \
-                     --port ${toString dbPort}                                          \
-                     -c '${e}'                                                          \
-                     ${cfg.database.name}'')
-
-               else if cfg.database.type == "mysql" then (e: ''
-                 echo '${e}' | ${pkgs.mysql}/bin/mysql                  \
-                   ${optionalString (cfg.database.password != null)
-                     "-p${cfg.database.password}"}                      \
-                   -u ${cfg.database.user}                              \
-                   -h ${cfg.database.host}                              \
-                   -P ${toString dbPort}                                \
-                   ${cfg.database.name}'')
-
-               else "";
+          callSql = e:
+              if cfg.database.type == "pgsql" then ''
+                  ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
+                  ${pkgs.postgresql95}/bin/psql \
+                    -U ${cfg.database.user} \
+                    ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
+                    -c '${e}' \
+                    ${cfg.database.name}''
+
+              else if cfg.database.type == "mysql" then ''
+                  echo '${e}' | ${pkgs.mysql}/bin/mysql \
+                    -u ${cfg.database.user} \
+                    ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
+                    ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
+                    ${cfg.database.name}''
+
+              else "";
 
         in ''
-          rm -rf "${root}/*"
-          mkdir -m 755 -p "${root}"
-          cp -r "${pkgs.tt-rss}/"* "${root}"
-          ln -sf "${tt-rss-config}" "${root}/config.php"
-          chown -R "${cfg.user}" "${root}"
-          chmod -R 755 "${root}"
-        '' + (optionalString (cfg.database.type == "pgsql") ''
-
+          rm -rf "${cfg.root}/*"
+          mkdir -m 755 -p "${cfg.root}"
+          cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
+          ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
+          chown -R "${cfg.user}" "${cfg.root}"
+          chmod -R 755 "${cfg.root}"
+        ''
+
+        + (optionalString (cfg.database.type == "pgsql") ''
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -540,8 +531,9 @@ let
           else
             echo 'The database contains some data. Leaving it as it is.'
           fi;
-        '') + (optionalString (cfg.database.type == "mysql") ''
+        '')
 
+        + (optionalString (cfg.database.type == "mysql") ''
           exists=$(${callSql "select count(*) > 0 from information_schema.tables where table_schema = schema()"} \
           | tail -n+2 | sed -e 's/[ \n\t]*//')
 
@@ -554,7 +546,7 @@ let
 
         serviceConfig = {
           User = "${cfg.user}";
-          ExecStart = "${pkgs.php}/bin/php /var/lib/tt-rss/update.php --daemon";
+          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon";
           StandardOutput = "syslog";
           StandardError = "syslog";
           PermissionsStartOnly = true;
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index 397857ea085..dc0ca501a48 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -16,7 +16,19 @@ let
 
   phpMajorVersion = head (splitString "." php.version);
 
-  getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80;
+  mod_perl = pkgs.mod_perl.override { apacheHttpd = httpd; };
+
+  defaultListen = cfg: if cfg.enableSSL
+    then [{ip = "*"; port = 443;}]
+    else [{ip = "*"; port = 80;}];
+
+  getListen = cfg:
+    let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen;
+    in if list == []
+        then defaultListen cfg
+        else list;
+
+  listenToString = l: "${l.ip}:${toString l.port}";
 
   extraModules = attrByPath ["extraModules"] [] mainCfg;
   extraForeignModules = filter isAttrs extraModules;
@@ -25,10 +37,13 @@ let
 
   makeServerInfo = cfg: {
     # Canonical name must not include a trailing slash.
-    canonicalName =
-      (if cfg.enableSSL then "https" else "http") + "://" +
-      cfg.hostName +
-      (if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString (getPort cfg)}" else "");
+    canonicalNames =
+      let defaultPort = (head (defaultListen cfg)).port; in
+      map (port:
+        (if cfg.enableSSL then "https" else "http") + "://" +
+        cfg.hostName +
+        (if port != defaultPort then ":${toString port}" else "")
+        ) (map (x: x.port) (getListen cfg));
 
     # Admin address: inherit from the main server if not specified for
     # a virtual host.
@@ -63,6 +78,7 @@ let
           robotsEntries = "";
           startupScript = "";
           enablePHP = false;
+          enablePerl = false;
           phpOptions = "";
           options = {};
           documentRoot = null;
@@ -224,7 +240,7 @@ let
         ++ (map (svc: svc.robotsEntries) subservices)));
 
   in ''
-    ServerName ${serverInfo.canonicalName}
+    ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
 
     ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
 
@@ -326,9 +342,10 @@ let
     </IfModule>
 
     ${let
-        ports = map getPort allHosts;
-        uniquePorts = uniqList {inputList = ports;};
-      in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts
+        listen = concatMap getListen allHosts;
+        toStr = listen: "Listen ${listenToString listen}\n";
+        uniqueListen = uniqList {inputList = map toStr listen;};
+      in concatStrings uniqueListen
     }
 
     User ${mainCfg.user}
@@ -341,6 +358,7 @@ let
           ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
           ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
           ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
+          ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
           ++ concatMap (svc: svc.extraModules) allSubservices
           ++ extraForeignModules;
       in concatMapStrings load allModules
@@ -382,15 +400,15 @@ let
 
     # Always enable virtual hosts; it doesn't seem to hurt.
     ${let
-        ports = map getPort allHosts;
-        uniquePorts = uniqList {inputList = ports;};
-        directives = concatMapStrings (port: "NameVirtualHost *:${toString port}\n") uniquePorts;
+        listen = concatMap getListen allHosts;
+        uniqueListen = uniqList {inputList = listen;};
+        directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen;
       in optionalString (!version24) directives
     }
 
     ${let
         makeVirtualHost = vhost: ''
-          <VirtualHost *:${toString (getPort vhost)}>
+          <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
               ${perServerConf false vhost}
           </VirtualHost>
         '';
@@ -401,6 +419,8 @@ let
 
   enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
 
+  enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
+
 
   # Generate the PHP configuration file.  Should probably be factored
   # out into a separate module.
@@ -565,6 +585,12 @@ in
         '';
       };
 
+      enablePerl = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Perl module (mod_perl).";
+      };
+
       phpOptions = mkOption {
         type = types.lines;
         default = "";
@@ -628,6 +654,8 @@ in
                      message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
                  ];
 
+    warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port}";}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts);
+
     users.extraUsers = optionalAttrs (mainCfg.user == "wwwrun") (singleton
       { name = "wwwrun";
         group = mainCfg.group;
@@ -681,13 +709,6 @@ in
             ''}
             mkdir -m 0700 -p ${mainCfg.logDir}
 
-            ${optionalString (mainCfg.documentRoot != null)
-            ''
-              # Create the document root directory if does not exists yet
-              mkdir -p ${mainCfg.documentRoot}
-            ''
-            }
-
             # Get rid of old semaphores.  These tend to accumulate across
             # server restarts, eventually preventing it from restarting
             # successfully.
@@ -712,5 +733,4 @@ in
       };
 
   };
-
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
index b4b5a6fdc07..1ed489bcb09 100644
--- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -83,11 +83,11 @@ let
 
   # Unpack Mediawiki and put the config file in its root directory.
   mediawikiRoot = pkgs.stdenv.mkDerivation rec {
-    name= "mediawiki-1.23.13";
+    name= "mediawiki-1.27.1";
 
     src = pkgs.fetchurl {
-      url = "http://download.wikimedia.org/mediawiki/1.23/${name}.tar.gz";
-      sha256 = "168wpf53n4ksj2g5q5r0hxapx6238dvsfng5ff9ixk6axsn0j5d0";
+      url = "http://download.wikimedia.org/mediawiki/1.27/${name}.tar.gz";
+      sha256 = "0sm3ymz93qragbwhzzbwq7f127mbj29inv0afg2z6p32jb1pd9h8";
     };
 
     skins = config.skins;
@@ -288,6 +288,7 @@ in
     };
 
     extraConfig = mkOption {
+      type = types.lines;
       default = "";
       example =
         ''
diff --git a/nixos/modules/services/web-servers/apache-httpd/moodle.nix b/nixos/modules/services/web-servers/apache-httpd/moodle.nix
index 87b1fba5aa1..d525348d5c7 100644
--- a/nixos/modules/services/web-servers/apache-httpd/moodle.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/moodle.nix
@@ -63,6 +63,10 @@ let
         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
@@ -160,6 +164,7 @@ in
 
 
     extraConfig = mkOption {
+      type = types.lines;
       default = "";
       example =
         ''
diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 5abcc5e7490..1d53ce65900 100644
--- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -28,9 +28,30 @@ with lib;
     type = types.int;
     default = 0;
     description = ''
-      Port for the server.  0 means use the default port: 80 for http
-      and 443 for https (i.e. when enableSSL is set).
+      Port for the server. Option will be removed, use <option>listen</option> instead.
+  '';
+  };
+
+  listen = mkOption {
+     type = types.listOf (types.submodule (
+          {
+            options = {
+              port = mkOption {
+                type = types.int;
+                description = "port to listen on";
+              };
+              ip = mkOption {
+                type = types.string;
+                default = "*";
+                description = "Ip to listen on. 0.0.0.0 for ipv4 only, * for all.";
+              };
+            };
+          } ));
+    description = ''
+      List of { /* ip: "*"; */ port = 80;} to listen on
     '';
+
+    default = [];
   };
 
   enableSSL = mkOption {
diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix
index 3196edc2838..35b9ab56087 100644
--- a/nixos/modules/services/web-servers/apache-httpd/trac.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/trac.nix
@@ -99,10 +99,9 @@ in
         makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages"
           [ pkgs.mod_python
             pkgs.pythonPackages.trac
-            pkgs.setuptools
+            pkgs.pythonPackages.setuptools
             pkgs.pythonPackages.genshi
             pkgs.pythonPackages.psycopg2
-            pkgs.python.modules.sqlite3
             subversion
           ];
     };
diff --git a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
index 937b2698ce9..32dd4439675 100644
--- a/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
@@ -5,7 +5,8 @@ with lib;
 
 let
 
-  version = "4.3.1";
+  # 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
@@ -74,7 +75,7 @@ let
       owner = "WordPress";
       repo = "WordPress";
       rev = "${fullversion}";
-      sha256 = "1rk10vcv4z9p04hfzc0wkbilrgx7m9ssyr6c3w6vw3vl1bcgqxza";
+      sha256 = "0n82xgjg1ry2p73hhgpslnkdzrma5n6hxxq76s7qskkzj0qjfvpn";
     };
     installPhase = ''
       mkdir -p $out
@@ -98,7 +99,7 @@ let
       # symlink additional plugin(s)
       ${concatMapStrings (plugin: "ln -s ${plugin} $out/wp-content/plugins/${plugin.name}\n") (config.plugins) }
 
-      # symlink additional translation(s) 
+      # symlink additional translation(s)
       mkdir -p $out/wp-content/languages
       ${concatMapStrings (language: "ln -s ${language}/*.mo ${language}/*.po $out/wp-content/languages/\n") (selectedLanguages) }
     '';
@@ -123,7 +124,7 @@ in
   options = {
     dbHost = mkOption {
       default = "localhost";
-      description = "The location of the database server.";  
+      description = "The location of the database server.";
       example = "localhost";
     };
     dbName = mkOption {
@@ -211,6 +212,7 @@ in
           example = "[ \"en_GB\" \"de_DE\" ];";
     };
     extraConfig = mkOption {
+      type = types.lines;
       default = "";
       example =
         ''
@@ -244,7 +246,6 @@ in
     chown ${serverInfo.serverConfig.user} ${config.wordpressUploads}
 
     # we should use systemd dependencies here
-    #waitForUnit("network-interfaces.target");
     if [ ! -d ${serverInfo.fullConfig.services.mysql.dataDir}/${config.dbName} ]; then
       echo "Need to create the database '${config.dbName}' and grant permissions to user named '${config.dbUser}'."
       # Wait until MySQL is up
@@ -253,7 +254,7 @@ in
       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}";'
-    else 
+    else
       echo "Good, no need to do anything database related."
     fi
   '';
diff --git a/nixos/modules/services/web-servers/fcgiwrap.nix b/nixos/modules/services/web-servers/fcgiwrap.nix
index 2c5e433003c..a64a187255a 100644
--- a/nixos/modules/services/web-servers/fcgiwrap.nix
+++ b/nixos/modules/services/web-servers/fcgiwrap.nix
@@ -21,7 +21,7 @@ in {
       };
 
       socketType = mkOption {
-        type = types.addCheck types.str (t: t == "unix" || t == "tcp" || t == "tcp6");
+        type = types.enum [ "unix" "tcp" "tcp6" ];
         default = "unix";
         description = "Socket type: 'unix', 'tcp' or 'tcp6'.";
       };
diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix
index 43deccb6aef..669e81d0f14 100644
--- a/nixos/modules/services/web-servers/lighttpd/inginious.nix
+++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix
@@ -191,9 +191,8 @@ in
         virtualisation.docker = {
           enable = true;
           # We need docker to listen on port 2375.
-          extraOptions = "-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock";
+          listenOptions = ["127.0.0.1:2375" "/var/run/docker.sock"];
           storageDriver = mkDefault "overlay";
-          socketActivation = false;
         };
 
         users.extraUsers."lighttpd".extraGroups = [ "docker" ];
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 6e62606f323..68a672c42c9 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -10,6 +10,7 @@ let
       sslCertificateKey = "/var/lib/acme/${vhostName}/key.pem";
     })
   ) cfg.virtualHosts;
+  enableIPv6 = config.networking.enableIPv6;
 
   configFile = pkgs.writeText "nginx.conf" ''
     user ${cfg.user} ${cfg.group};
@@ -18,9 +19,13 @@ let
 
     ${cfg.config}
 
-    ${optionalString (cfg.httpConfig == "" && cfg.config == "") ''
-    events {}
+    ${optionalString (cfg.eventsConfig != "" || cfg.config == "") ''
+    events {
+      ${cfg.eventsConfig}
+    }
+    ''}
 
+    ${optionalString (cfg.httpConfig == "" && cfg.config == "") ''
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
@@ -80,7 +85,7 @@ let
       ${optionalString cfg.statusPage ''
         server {
           listen 80;
-          listen [::]:80;
+          ${optionalString enableIPv6 "listen [::]:80;" }
 
           server_name localhost;
 
@@ -88,7 +93,7 @@ let
             stub_status on;
             access_log off;
             allow 127.0.0.1;
-            allow ::1;
+            ${optionalString enableIPv6 "allow ::1;"}
             deny all;
           }
         }
@@ -98,7 +103,6 @@ let
     }''}
 
     ${optionalString (cfg.httpConfig != "") ''
-    events {}
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
@@ -113,35 +117,38 @@ let
         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"
-          + optionalString vhost.default " default";
-        acmeLocation = optionalString vhost.enableACME ''
+          + optionalString vhost.default " default_server";
+        acmeLocation = optionalString vhost.enableACME (''
           location /.well-known/acme-challenge {
-            try_files $uri @acme-fallback;
+            ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
             root ${vhost.acmeRoot};
             auth_basic off;
           }
+        '' + (optionalString (vhost.acmeFallbackHost != null) ''
           location @acme-fallback {
             auth_basic off;
             proxy_pass http://${vhost.acmeFallbackHost};
           }
-        '';
+        ''));
       in ''
         ${optionalString vhost.forceSSL ''
           server {
-            listen 80 ${optionalString vhost.default "default"};
-            listen [::]:80 ${optionalString vhost.default "default"};
+            listen 80 ${optionalString vhost.default "default_server"};
+            ${optionalString enableIPv6
+              ''listen [::]:80 ${optionalString vhost.default "default_server"};''
+            }
 
             server_name ${serverName} ${concatStringsSep " " vhost.serverAliases};
             ${acmeLocation}
             location / {
-              return 301 https://$host${optionalString (port != 443) ":${port}"}$request_uri;
+              return 301 https://$host${optionalString (port != 443) ":${toString port}"}$request_uri;
             }
           }
         ''}
 
         server {
           listen ${listenString};
-          listen [::]:${listenString};
+          ${optionalString enableIPv6 "listen [::]:${listenString};"}
 
           server_name ${serverName} ${concatStringsSep " " vhost.serverAliases};
           ${acmeLocation}
@@ -271,12 +278,20 @@ in
         ";
       };
 
+      eventsConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration lines to be set inside the events block.
+        '';
+      };
+
       appendHttpConfig = mkOption {
         type = types.lines;
         default = "";
         description = "
           Configuration lines to be appended to the generated http block.
-          This is mutually exclusive with using config and httpConfig for 
+          This is mutually exclusive with using config and httpConfig for
           specifying the whole http block verbatim.
         ";
       };
@@ -380,8 +395,13 @@ 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
     );
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index ee3f68bf805..dcebbc9229f 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -39,8 +39,8 @@ with lib;
     };
 
     acmeFallbackHost = mkOption {
-      type = types.str;
-      default = "0.0.0.0";
+      type = types.nullOr types.str;
+      default = null;
       description = ''
         Host which to proxy requests to if acme challenge is not found. Useful
         if you want multiple hosts to be able to verify the same domain name.
diff --git a/nixos/modules/services/web-servers/phpfpm/default.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index 29cfbb8e9a0..ed537e7122a 100644
--- a/nixos/modules/services/web-servers/phpfpm/default.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -7,8 +7,6 @@ let
 
   stateDir = "/run/phpfpm";
 
-  pidFile = "${stateDir}/phpfpm.pid";
-
   mkPool = n: p: ''
     [${n}]
     listen = ${p.listen}
@@ -17,9 +15,8 @@ let
 
   cfgFile = pkgs.writeText "phpfpm.conf" ''
     [global]
-    pid = ${pidFile}
     error_log = syslog
-    daemonize = yes
+    daemonize = no
     ${cfg.extraConfig}
 
     ${concatStringsSep "\n" (mapAttrsToList mkPool cfg.pools)}
@@ -42,8 +39,8 @@ in {
         default = "";
         description = ''
           Extra configuration that should be put in the global section of
-          the PHP FPM configuration file. Do not specify the options
-          <literal>pid</literal>, <literal>error_log</literal> or
+          the PHP-FPM configuration file. Do not specify the options
+          <literal>error_log</literal> or
           <literal>daemonize</literal> here, since they are generated by
           NixOS.
         '';
@@ -54,7 +51,7 @@ in {
         default = pkgs.php;
         defaultText = "pkgs.php";
         description = ''
-          The PHP package to use for running the FPM service.
+          The PHP package to use for running the PHP-FPM service.
         '';
       };
 
@@ -86,7 +83,7 @@ in {
           }
         '';
         description = ''
-          A mapping between PHP FPM pool names and their configurations.
+          A mapping between PHP-FPM pool names and their configurations.
           See the documentation on <literal>php-fpm.conf</literal> for
           details on configuration directives. If no pools are defined,
           the phpfpm service is disabled.
@@ -98,8 +95,24 @@ in {
           inherit lib;
         }));
         default = {};
+        example = literalExample ''
+         {
+           mypool = {
+             listen = "/path/to/unix/socket";
+             extraConfig = '''
+               user = nobody
+               pm = dynamic
+               pm.max_children = 75
+               pm.start_servers = 10
+               pm.min_spare_servers = 5
+               pm.max_spare_servers = 20
+               pm.max_requests = 500
+             ''';
+           }
+         }'';
         description = ''
-          If no pools are defined, the phpfpm service is disabled.
+          PHP-FPM pools. If no pools or poolConfigs are defined, the PHP-FPM
+          service is disabled.
         '';
       };
     };
@@ -113,10 +126,10 @@ in {
         mkdir -p "${stateDir}"
       '';
       serviceConfig = {
+        Type = "notify";
         ExecStart = "${cfg.phpPackage}/bin/php-fpm -y ${cfgFile} -c ${phpIni}";
-        PIDFile = pidFile;
+        ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
       };
     };
-
   };
 }
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index c3be20b41e2..943415e08c6 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -10,6 +10,10 @@ in
 
 {
 
+  meta = {
+    maintainers = with maintainers; [ danbst ];
+  };
+
   ###### interface
 
   options = {
@@ -23,9 +27,9 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.tomcat7;
-        defaultText = "pkgs.tomcat7";
-        example = lib.literalExample "pkgs.tomcat8";
+        default = pkgs.tomcat85;
+        defaultText = "pkgs.tomcat85";
+        example = lib.literalExample "pkgs.tomcatUnstable";
         description = ''
           Which tomcat package to use.
         '';
@@ -74,8 +78,8 @@ in
 
       webapps = mkOption {
         type = types.listOf types.package;
-        default = [ tomcat ];
-        defaultText = "[ tomcat ]";
+        default = [ tomcat.webapps ];
+        defaultText = "[ tomcat.webapps ]";
         description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
       };
 
@@ -135,7 +139,7 @@ in
     systemd.services.tomcat = {
       description = "Apache Tomcat server";
       wantedBy = [ "multi-user.target" ];
-      after = [ "network-interfaces.target" ];
+      after = [ "network.target" ];
       serviceConfig.Type = "oneshot";
       serviceConfig.RemainAfterExit = true;
 
@@ -352,7 +356,7 @@ in
           ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
       '';
 
-      postStop = ''
+      preStop = ''
         echo "Stopping tomcat..."
         CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
       '';
diff --git a/nixos/modules/services/web-servers/uwsgi.nix b/nixos/modules/services/web-servers/uwsgi.nix
index 56f077e62a8..14596bb3add 100644
--- a/nixos/modules/services/web-servers/uwsgi.nix
+++ b/nixos/modules/services/web-servers/uwsgi.nix
@@ -29,7 +29,6 @@ let
 
       pythonPackages = pkgs.pythonPackages.override {
         inherit python;
-        self = pythonPackages;
       };
 
       penv = python.buildEnv.override {
diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix
index 6dab467b35e..064ead5ce4b 100644
--- a/nixos/modules/services/web-servers/winstone.nix
+++ b/nixos/modules/services/web-servers/winstone.nix
@@ -113,8 +113,7 @@ in {
   options = {
     services.winstone = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ winstoneOpts ];
+      type = with types; attrsOf (submodule winstoneOpts);
       description = ''
         Defines independent Winstone services, each serving one WAR-file.
       '';
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index ef3cffd582e..8a453e01557 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 = types.loaOf types.optionSet;
+      type = with types; loaOf (submodule zope2Opts);
       example = literalExample ''
         {
           plone01 = {
@@ -96,7 +96,6 @@ in
         }
       '';
       description = "zope2 instances to be created automaticaly by the system.";
-      options = [ zope2Opts ];
     };
   };
 
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index bda4eec0102..7cbca1dcddf 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -188,6 +188,7 @@ in {
     package = mkOption {
       type = types.package;
       default = pkgs.compton;
+      defaultText = "pkgs.compton";
       example = literalExample "pkgs.compton";
       description = ''
         Compton derivation to use.
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 1ea7b5ccf16..144e4aada27 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -19,7 +19,8 @@ in
   # E.g., if KDE is enabled, it supersedes xterm.
   imports = [
     ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./kde5.nix
-    ./enlightenment.nix ./gnome3.nix ./kodi.nix
+    ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome3.nix
+    ./kodi.nix
   ];
 
   options = {
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 90803ede9d9..9d0ff77c2ae 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -32,10 +32,10 @@ in
       e.efl e.enlightenment
       e.terminology e.econnman
       pkgs.xorg.xauth # used by kdesu
-      pkgs.gtk # To get GTK+'s themes.
+      pkgs.gtk2 # To get GTK+'s themes.
       pkgs.tango-icon-theme
       pkgs.shared_mime_info
-      pkgs.gnome.gnomeicontheme
+      pkgs.gnome2.gnomeicontheme
       pkgs.xorg.xcursorthemes
     ];
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index b3da2544802..17e84b1d9a1 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -25,9 +25,8 @@ let
     '';
   };
 
-  nixos-gsettings-desktop-schemas = pkgs.stdenv.mkDerivation {
-    name = "nixos-gsettings-desktop-schemas";
-    buildCommand = ''
+  nixos-gsettings-desktop-schemas = pkgs.runCommand "nixos-gsettings-desktop-schemas" {}
+    ''
      mkdir -p $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
      cp -rf ${gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
@@ -46,7 +45,6 @@ let
 
      ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/
     '';
-  };
 
 in {
 
@@ -84,7 +82,7 @@ in {
 
     environment.gnome3.packageSet = mkOption {
       default = null;
-      example = literalExample "pkgs.gnome3_20";
+      example = literalExample "pkgs.gnome3_22";
       description = "Which GNOME 3 package set to use.";
       apply = p: if p == null then pkgs.gnome3 else p;
     };
@@ -110,6 +108,7 @@ in {
     services.gnome3.gnome-documents.enable = mkDefault true;
     services.gnome3.gnome-keyring.enable = true;
     services.gnome3.gnome-online-accounts.enable = mkDefault true;
+    services.gnome3.gnome-terminal-server.enable = mkDefault true;
     services.gnome3.gnome-user-share.enable = mkDefault true;
     services.gnome3.gvfs.enable = true;
     services.gnome3.seahorse.enable = mkDefault true;
@@ -124,6 +123,7 @@ in {
     services.packagekit.enable = mkDefault true;
     hardware.bluetooth.enable = mkDefault true;
     services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
+    services.udev.packages = [ pkgs.gnome3.gnome_settings_daemon ];
 
     fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ];
 
diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix
index 88b3c3a1016..31d2ebcdf1a 100644
--- a/nixos/modules/services/x11/desktop-managers/kde4.nix
+++ b/nixos/modules/services/x11/desktop-managers/kde4.nix
@@ -14,7 +14,7 @@ let
   # files), segfault sometimes and consume significant resources.
   # They can be re-enabled in the KDE System Settings under "Desktop
   # Search".
-  nepomukConfig = pkgs.writeTextFile
+  disableNepomuk = pkgs.writeTextFile
     { name = "nepomuk-config";
       destination = "/share/config/nepomukserverrc";
       text =
@@ -70,6 +70,18 @@ in
         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).";
+      };
     };
   };
 
@@ -138,7 +150,6 @@ in
 
           pkgs.kde4.kde_wallpapers # contains kdm's default background
           pkgs.kde4.oxygen_icons
-          pkgs.virtuoso # to enable Nepomuk to find Virtuoso
 
           # Starts KDE's Polkit authentication agent.
           pkgs.kde4.polkit_kde_agent
@@ -149,20 +160,26 @@ in
           xorg.xmessage # so that startkde can show error messages
           xorg.xset # used by startkde, non-essential
           xorg.xauth # used by kdesu
-          pkgs.shared_desktop_ontologies # used by nepomuk
-          pkgs.strigi # used by nepomuk
+        ]
+      ++ optionals cfg.enablePIM
+        [ pkgs.kde4.kdepim_runtime
           pkgs.kde4.akonadi
           pkgs.mysql # used by akonadi
-          pkgs.kde4.kdepim_runtime
         ]
-      ++ lib.optional config.hardware.pulseaudio.enable pkgs.kde4.kmix  # Perhaps this should always be enabled
-      ++ lib.optional config.hardware.bluetooth.enable pkgs.kde4.bluedevil
-      ++ lib.optional config.networking.networkmanager.enable pkgs.kde4.plasma-nm
-      ++ [ nepomukConfig ] ++ phononBackendPackages;
+      ++ (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 (lib.elem "gstreamer" cfg.phononBackends) {
+    environment.profileRelativeEnvVars = mkIf (elem "gstreamer" cfg.phononBackends) {
       GST_PLUGIN_SYSTEM_PATH = [ "/lib/gstreamer-0.10" ];
     };
 
diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix
index 7856ff03f16..3037f949cbf 100644
--- a/nixos/modules/services/x11/desktop-managers/kde5.nix
+++ b/nixos/modules/services/x11/desktop-managers/kde5.nix
@@ -22,207 +22,231 @@ in
         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 = mkIf (xcfg.enable && cfg.enable) {
+  config = mkMerge [
+    (mkIf (cfg.extraPackages != []) {
+      environment.systemPackages = [ (kde5.kdeWrapper cfg.extraPackages) ];
+    })
 
-    warnings = optional config.services.xserver.desktopManager.kde4.enable
-      "KDE 4 should not be enabled at the same time as KDE 5";
+    (mkIf (xcfg.enable && cfg.enable) {
 
-    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"
-        ''}
+      warnings = optional config.services.xserver.desktopManager.kde4.enable
+        "KDE 4 should not be enabled at the same time as KDE 5";
 
-        exec "${kde5.startkde}"
+      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.permissionsWrappers.setuid = [
-      {
-        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
-        kde5.kactivitymanagerd
-        kde5.kde-cli-tools
-        kde5.kdecoration
-        kde5.kdeplasma-addons
-        kde5.kgamma5
-        kde5.khelpcenter
-        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.oxygen
-        kde5.plasma-integration
-        kde5.polkit-kde-agent
-        kde5.systemsettings
-
-        kde5.plasma-desktop
-        kde5.plasma-workspace
-        kde5.plasma-workspace-wallpapers
-
-        kde5.dolphin
-        kde5.dolphin-plugins
-        kde5.ffmpegthumbs
-        kde5.kdegraphics-thumbnailers
-        kde5.kio-extras
-        kde5.konsole
-        kde5.print-manager
-
-        # Oxygen icons moved to KDE Frameworks 5.16 and later.
-        (kde5.oxygen-icons or kde5.oxygen-icons5)
-        pkgs.hicolor_icon_theme
-
-        kde5.kde-gtk-config
-
-        pkgs.phonon-backend-gstreamer
-        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 Breeze icons if available
-      ++ lib.optional (lib.hasAttr "breeze-icons" kde5) kde5.breeze-icons
-
-      # 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
-
-      # 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 ];
-
-    environment.pathsToLink = [ "/share" ];
-
-    environment.etc = singleton {
-      source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
-      target = "X11/xkb";
-    };
+      security.permissionsWrappers.setuid = [
+        {
+          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";
+      };
 
-    # Enable GTK applications to load SVG icons
-    environment.variables =
-      {
-        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
-        (kde5.oxygen-icons or kde5.oxygen-icons5)
+      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
       ];
-    };
 
-    security.pam.services.kde = { allowNullPassword = true; };
+      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/lumina.nix b/nixos/modules/services/x11/desktop-managers/lumina.nix
new file mode 100644
index 00000000000..f0b31a2acb0
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/lumina.nix
@@ -0,0 +1,52 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.lumina;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.lumina.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable the Lumina desktop manager";
+    };
+
+  };
+
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    services.xserver.desktopManager.session = singleton {
+      name = "lumina";
+      start = ''
+        exec ${pkgs.lumina}/bin/start-lumina-desktop
+      '';
+    };
+
+    environment.systemPackages = [
+      pkgs.fluxbox
+      pkgs.kde5.kwindowsystem
+      pkgs.kde5.oxygen-icons5
+      pkgs.lumina
+      pkgs.numlockx
+      pkgs.qt5.qtsvg
+      pkgs.xscreensaver
+    ];
+
+    # Link some extra directories in /run/current-system/software/share
+    environment.pathsToLink = [
+      "/share/desktop-directories"
+      "/share/icons"
+      "/share/lumina"
+      "/share"
+    ];
+
+  };
+}
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
new file mode 100644
index 00000000000..89ad2882363
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  # Remove packages of ys from xs, based on their names
+  removePackagesByName = xs: ys:
+    let
+      pkgName = drv: (builtins.parseDrvName drv.name).name;
+      ysNames = map pkgName ys;
+    in
+      filter (x: !(builtins.elem (pkgName x) ysNames)) xs;
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.lxqt;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.lxqt.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable the LXQt desktop manager";
+    };
+
+    environment.lxqt.excludePackages = mkOption {
+      default = [];
+      example = literalExample "[ pkgs.lxqt.qterminal ]";
+      type = types.listOf types.package;
+      description = "Which LXQt packages to exclude from the default environment";
+    };
+
+  };
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    services.xserver.desktopManager.session = singleton {
+      name = "lxqt";
+      bgSupport = true;
+      start = ''
+        exec ${pkgs.lxqt.lxqt-common}/bin/startlxqt
+      '';
+    };
+
+    environment.systemPackages =
+      pkgs.lxqt.preRequisitePackages ++
+      pkgs.lxqt.corePackages ++
+      (removePackagesByName
+        pkgs.lxqt.optionalPackages
+        config.environment.lxqt.excludePackages);
+
+    # Link some extra directories in /run/current-system/software/share
+    environment.pathsToLink = [
+      "/share/desktop-directories"
+      "/share/icons"
+      "/share/lxqt"
+    ];
+
+    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.gvfs}/lib/gio/modules" ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 634d2a39576..530468be5f9 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -69,7 +69,7 @@ in
     services.xserver.updateDbusEnvironment = true;
 
     environment.systemPackages =
-      [ pkgs.gtk # To get GTK+'s themes.
+      [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache
         pkgs.hicolor_icon_theme
         pkgs.tango-icon-theme
         pkgs.shared_mime_info
@@ -100,6 +100,7 @@ in
         pkgs.xfce.tumbler       # found via dbus
       ]
       ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager
+      ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet
       ++ optionals (!cfg.noDesktop)
          [ pkgs.xfce.xfce4panel
            pkgs.xfce.xfdesktop
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 75d80609f73..c0daf30d04e 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -82,12 +82,12 @@ let
 
       # Speed up application start by 50-150ms according to
       # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html
-      rm -rf $HOME/.compose-cache
-      mkdir $HOME/.compose-cache
+      rm -rf "$HOME/.compose-cache"
+      mkdir "$HOME/.compose-cache"
 
       # Work around KDE errors when a user first logs in and
       # .local/share doesn't exist yet.
-      mkdir -p $HOME/.local/share
+      mkdir -p "$HOME/.local/share"
 
       unset _DID_SYSTEMD_CAT
 
@@ -134,13 +134,8 @@ let
         (*) echo "$0: Desktop manager '$desktopManager' not found.";;
       esac
 
-      # FIXME: gdbus should not be in glib.dev!
-      ${optionalString (cfg.startDbusSession && cfg.updateDbusEnvironment) ''
-        ${pkgs.glib.dev}/bin/gdbus call --session \
-          --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus \
-          --method org.freedesktop.DBus.UpdateActivationEnvironment \
-          "{$(env | ${pkgs.gnused}/bin/sed "s/'/\\\\'/g; s/\([^=]*\)=\(.*\)/'\1':'\2'/" \
-                  | ${pkgs.coreutils}/bin/paste -sd,)}"
+      ${optionalString cfg.updateDbusEnvironment ''
+        ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all
       ''}
 
       test -n "$waitPID" && wait "$waitPID"
@@ -153,7 +148,7 @@ let
       allowSubstitutes = false;
     }
     ''
-      mkdir -p $out
+      mkdir -p "$out"
       ${concatMapStrings (n: ''
         cat - > "$out/${n}.desktop" << EODESKTOP
         [Desktop Entry]
@@ -192,7 +187,6 @@ in
         default = [];
         example = [ "-ac" "-logverbose" "-verbose" "-nolisten tcp" ];
         description = "List of arguments for the X server.";
-        apply = toString;
       };
 
       sessionCommands = mkOption {
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 52847d2f8d2..6c63fede857 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -92,12 +92,16 @@ in
 
     users.extraGroups.gdm.gid = config.ids.gids.gdm;
 
+    # GDM needs different xserverArgs, presumable because using wayland by default.
+    services.xserver.tty = null;
+    services.xserver.display = null;
+
     services.xserver.displayManager.job =
       {
         environment = {
-          GDM_X_SERVER = "${cfg.xserverBin} ${cfg.xserverArgs}";
+          GDM_X_SERVER_EXTRA_ARGS = toString
+            (filter (arg: arg != "-terminate") cfg.xserverArgs);
           GDM_SESSIONS_DIR = "${cfg.session.desktops}";
-          XDG_CONFIG_DIRS = "${gnome3.gnome_settings_daemon}/etc/xdg";
           # Find the mouse
           XCURSOR_PATH = "~/.icons:${config.system.path}/share/icons";
         };
@@ -108,10 +112,12 @@ in
     systemd.services.display-manager.wants = [ "systemd-machined.service" ];
     systemd.services.display-manager.after = [ "systemd-machined.service" ];
 
-    systemd.services.display-manager.path = [ gnome3.gnome_shell gnome3.caribou pkgs.xorg.xhost pkgs.dbus_tools ];
+    systemd.services.display-manager.path = [ gnome3.gnome_session ];
 
     services.dbus.packages = [ gdm ];
 
+    systemd.user.services.dbus.wantedBy = [ "default.target" ];
+
     programs.dconf.profiles.gdm = "${gdm}/share/dconf/profile/gdm";
 
     # Use AutomaticLogin if delay is zero, because it's immediate.
diff --git a/nixos/modules/services/x11/display-managers/kdm.nix b/nixos/modules/services/x11/display-managers/kdm.nix
index d9f7f8f0dfc..04701a1640c 100644
--- a/nixos/modules/services/x11/display-managers/kdm.nix
+++ b/nixos/modules/services/x11/display-managers/kdm.nix
@@ -25,7 +25,7 @@ let
       FailsafeClient=${pkgs.xterm}/bin/xterm
 
       [X-:*-Core]
-      ServerCmd=${dmcfg.xserverBin} ${dmcfg.xserverArgs}
+      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.
@@ -54,19 +54,17 @@ let
       ''}
     '';
 
-  kdmrc = pkgs.stdenv.mkDerivation {
-    name = "kdmrc";
-    config = defaultConfig + cfg.extraConfig;
-    preferLocalBuild = true;
-    buildCommand =
-      ''
-        echo "$config" > $out
+  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
-      '';
-  };
+      # 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
 
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 543dd628ce6..dfda90978b1 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -16,11 +16,9 @@ let
   # The default greeter provided with this expression is the GTK greeter.
   # Again, we need a few things in the environment for the greeter to run with
   # fonts/icons.
-  wrappedGtkGreeter = stdenv.mkDerivation {
-    name = "lightdm-gtk-greeter";
-    buildInputs = [ pkgs.makeWrapper ];
-
-    buildCommand = ''
+  wrappedGtkGreeter = pkgs.runCommand "lightdm-gtk-greeter"
+    { buildInputs = [ pkgs.makeWrapper ]; }
+    ''
       # This wrapper ensures that we actually get themes
       makeWrapper ${pkgs.lightdm_gtk_greeter}/sbin/lightdm-gtk-greeter \
         $out/greeter \
@@ -40,7 +38,6 @@ let
       Type=Application
       EOF
     '';
-  };
 
   gtkGreeterConf = writeText "lightdm-gtk-greeter.conf"
     ''
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 47786f0a432..4afef32aaa4 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -23,7 +23,7 @@ let
       else additionalArgs="-logfile /var/log/X.$display.log"
       fi
 
-      exec ${dmcfg.xserverBin} ${dmcfg.xserverArgs} $additionalArgs "$@"
+      exec ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} $additionalArgs "$@"
     '';
 
   usersConf = writeText "users.conf"
@@ -46,13 +46,15 @@ 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}
     '';
@@ -205,6 +207,9 @@ in
     services.dbus.enable = true;
     services.dbus.packages = [ lightdm ];
 
+    # lightdm uses the accounts daemon to rember language/window-manager per user
+    services.accounts-daemon.enable = true;
+
     security.pam.services.lightdm = {
       allowNullPassword = true;
       startSession = true;
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 16d1e89e8d9..6630b8257e4 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -14,7 +14,7 @@ let
   xserverWrapper = pkgs.writeScript "xserver-wrapper" ''
     #!/bin/sh
     ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
-    exec ${dmcfg.xserverBin} ${dmcfg.xserverArgs} "$@"
+    exec systemd-cat ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
   '';
 
   Xsetup = pkgs.writeScript "Xsetup" ''
@@ -27,7 +27,6 @@ let
     ${cfg.stopScript}
   '';
 
-
   cfgFile = pkgs.writeText "sddm.conf" ''
     [General]
     HaltCommand=${pkgs.systemd}/bin/systemctl poweroff
@@ -46,8 +45,8 @@ let
     HideUsers=${concatStringsSep "," dmcfg.hiddenUsers}
     HideShells=/run/current-system/sw/bin/nologin
 
-    [XDisplay]
-    MinimumVT=${toString xcfg.tty}
+    [X11]
+    MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)}
     ServerPath=${xserverWrapper}
     XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr
     SessionCommand=${dmcfg.session.script}
@@ -86,7 +85,7 @@ in
       };
 
       extraConfig = mkOption {
-        type = types.str;
+        type = types.lines;
         default = "";
         example = ''
           [Autologin]
@@ -100,7 +99,7 @@ in
 
       theme = mkOption {
         type = types.str;
-        default = "maui";
+        default = "";
         description = ''
           Greeter theme to use.
         '';
@@ -254,5 +253,10 @@ in
 
     users.extraGroups.sddm.gid = config.ids.gids.sddm;
 
+    services.dbus.packages = [ sddm.unwrapped ];
+
+    # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
+    services.xserver.tty = null;
+    services.xserver.display = null;
   };
 }
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index ce44c9f54a3..68acde85b5d 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -12,7 +12,7 @@ let
     ''
       xauth_path ${dmcfg.xauthBin}
       default_xserver ${dmcfg.xserverBin}
-      xserver_arguments ${dmcfg.xserverArgs}
+      xserver_arguments ${toString dmcfg.xserverArgs}
       sessiondir ${dmcfg.session.desktops}
       login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session"
       halt_cmd ${config.systemd.package}/sbin/shutdown -h now
@@ -26,15 +26,13 @@ let
   # Unpack the SLiM theme, or use the default.
   slimThemesDir =
     let
-      unpackedTheme = pkgs.stdenv.mkDerivation {
-        name = "slim-theme";
-        buildCommand = ''
+      unpackedTheme = pkgs.runCommand "slim-theme" {}
+        ''
           mkdir -p $out
           cd $out
           unpackFile ${cfg.theme}
           ln -s * default
         '';
-      };
     in if cfg.theme == null then "${pkgs.slim}/share/slim/themes" else unpackedTheme;
 
 in
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index 5c068e89dd7..2a7f4e5cbcd 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -19,7 +19,7 @@ let cfg = config.services.xserver.synaptics;
       Option "TapButton3" "0"
     '';
   pkg = pkgs.xorg.xf86inputsynaptics;
-  etcFile = "X11/xorg.conf.d/50-synaptics.conf";
+  etcFile = "X11/xorg.conf.d/70-synaptics.conf";
 in {
 
   options = {
@@ -172,7 +172,7 @@ in {
     services.xserver.modules = [ pkg.out ];
 
     environment.etc."${etcFile}".source =
-      "${pkg.out}/share/X11/xorg.conf.d/50-synaptics.conf";
+      "${pkg.out}/share/X11/xorg.conf.d/70-synaptics.conf";
 
     environment.systemPackages = [ pkg ];
 
diff --git a/nixos/modules/services/x11/hardware/wacom.nix b/nixos/modules/services/x11/hardware/wacom.nix
index 540ed168b48..a27889c36a7 100644
--- a/nixos/modules/services/x11/hardware/wacom.nix
+++ b/nixos/modules/services/x11/hardware/wacom.nix
@@ -22,7 +22,7 @@ in
           which will make Xorg reconfigure the device ?
 
           If you're not satisfied by the default behaviour you can override
-          <option>environment.etc."X11/xorg.conf.d/50-wacom.conf"</option> in
+          <option>environment.etc."X11/xorg.conf.d/70-wacom.conf"</option> in
           configuration.nix easily.
         '';
       };
@@ -40,7 +40,7 @@ in
 
     services.udev.packages = [ pkgs.xf86_input_wacom ];
 
-    environment.etc."X11/xorg.conf.d/50-wacom.conf".source = "${pkgs.xf86_input_wacom}/share/X11/xorg.conf.d/50-wacom.conf";
+    environment.etc."X11/xorg.conf.d/70-wacom.conf".source = "${pkgs.xf86_input_wacom}/share/X11/xorg.conf.d/70-wacom.conf";
 
   };
 
diff --git a/nixos/modules/services/x11/unclutter-xfixes.nix b/nixos/modules/services/x11/unclutter-xfixes.nix
new file mode 100644
index 00000000000..bd02c5ed989
--- /dev/null
+++ b/nixos/modules/services/x11/unclutter-xfixes.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.unclutter-xfixes;
+
+in {
+  options.services.unclutter-xfixes = {
+
+    enable = mkOption {
+      description = "Enable unclutter-xfixes to hide your mouse cursor when inactive.";
+      type = types.bool;
+      default = false;
+      example = true;
+    };
+
+    package = mkOption {
+      description = "unclutter-xfixes derivation to use.";
+      type = types.package;
+      default = pkgs.unclutter-xfixes;
+      defaultText = "pkgs.unclutter-xfixes";
+    };
+
+    timeout = mkOption {
+      description = "Number of seconds before the cursor is marked inactive.";
+      type = types.int;
+      default = 1;
+    };
+
+    threshold = mkOption {
+      description = "Minimum number of pixels considered cursor movement.";
+      type = types.int;
+      default = 1;
+    };
+
+    extraOptions = mkOption {
+      description = "More arguments to pass to the unclutter-xfixes command.";
+      type = types.listOf types.str;
+      default = [];
+      example = [ "exclude-root" "ignore-scrolling" "fork" ];
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.unclutter-xfixes = {
+      description = "unclutter-xfixes";
+      wantedBy = [ "graphical.target" ];
+      serviceConfig.ExecStart = ''
+        ${cfg.package}/bin/unclutter \
+          --timeout ${toString cfg.timeout} \
+          --jitter ${toString (cfg.threshold - 1)} \
+          ${concatMapStrings (x: " --"+x) cfg.extraOptions} \
+      '';
+      serviceConfig.RestartSec = 3;
+      serviceConfig.Restart = "always";
+    };
+  };
+}
diff --git a/nixos/modules/services/x11/urxvtd.nix b/nixos/modules/services/x11/urxvtd.nix
new file mode 100644
index 00000000000..be36efaa589
--- /dev/null
+++ b/nixos/modules/services/x11/urxvtd.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+# maintainer: siddharthist
+
+with lib;
+
+let
+  cfg = config.services.urxvtd;
+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".
+    '';
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user = {
+      sockets.urxvtd = {
+        description = "socket for urxvtd, the urxvt terminal daemon";
+        after = [ "graphical.target" ];
+        wants = [ "graphical.target" ];
+        wantedBy = [ "sockets.target" ];
+        socketConfig = {
+          ListenStream = "%t/urxvtd-socket";
+        };
+      };
+
+      services.urxvtd = {
+        description = "urxvt terminal daemon";
+        path = [ pkgs.xsel ];
+        serviceConfig = {
+          ExecStart = "${pkgs.rxvt_unicode-with-plugins}/bin/urxvtd -o";
+          Environment = "RXVT_SOCKET=%t/urxvtd-socket";
+          Restart = "on-failure";
+          RestartSec = "5s";
+        };
+      };
+
+    };
+
+    environment.systemPackages = [ pkgs.rxvt_unicode-with-plugins ];
+    environment.variables.RXVT_SOCKET = "/run/user/$(id -u)/urxvtd-socket";
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix
index 455b3568499..eb97449c6bd 100644
--- a/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixos/modules/services/x11/window-managers/awesome.nix
@@ -6,7 +6,7 @@ let
 
   cfg = config.services.xserver.windowManager.awesome;
   awesome = cfg.package;
-
+  inherit (pkgs.luaPackages) getLuaPath getLuaCPath;
 in
 
 {
@@ -46,10 +46,8 @@ in
       { name = "awesome";
         start =
           ''
-            ${concatMapStrings (pkg: ''
-              export LUA_CPATH=$LUA_CPATH''${LUA_CPATH:+;}${pkg}/lib/lua/${awesome.lua.luaversion}/?.so
-              export LUA_PATH=$LUA_PATH''${LUA_PATH:+;}${pkg}/lib/lua/${awesome.lua.luaversion}/?.lua
-            '') cfg.luaModules}
+            export LUA_CPATH="${lib.concatStringsSep ";" (map getLuaCPath cfg.luaModules)}"
+            export LUA_PATH="${lib.concatStringsSep ";" (map getLuaPath cfg.luaModules)}"
 
             ${awesome}/bin/awesome &
             waitPID=$!
@@ -59,5 +57,4 @@ in
     environment.systemPackages = [ awesome ];
 
   };
-
 }
diff --git a/nixos/modules/services/x11/window-managers/bspwm.nix b/nixos/modules/services/x11/window-managers/bspwm.nix
index 03a1b7a72e8..6783ac3479e 100644
--- a/nixos/modules/services/x11/window-managers/bspwm.nix
+++ b/nixos/modules/services/x11/window-managers/bspwm.nix
@@ -9,40 +9,69 @@ in
 {
   options = {
     services.xserver.windowManager.bspwm = {
-        enable = mkEnableOption "bspwm";
-        startThroughSession = mkOption {
-            type = with types; bool;
-            default = false;
-            description = "
-                Start the window manager through the script defined in 
-                sessionScript. Defaults to the the bspwm-session script
-                provided by bspwm
-            ";
-        };
-        sessionScript = mkOption {
-            default = "${pkgs.bspwm}/bin/bspwm-session";
-            defaultText = "(pkgs.bspwm)/bin/bspwm-session";
-            description = "
-                The start-session script to use. Defaults to the
-                provided bspwm-session script from the bspwm package.
+      enable = mkEnableOption "bspwm";
+
+      package = mkOption {
+        type        = types.package;
+        default     = pkgs.bspwm;
+        defaultText = "pkgs.bspwm";
+        example     = "pkgs.bspwm-unstable";
+        description = ''
+          bspwm package to use.
+        '';
+      };
+      configFile = mkOption {
+        type        = with types; nullOr path;
+        example     = "${pkgs.bspwm}/share/doc/bspwm/examples/bspwmrc";
+        default     = null;
+        description = ''
+          Path to the bspwm configuration file.
+          If null, $HOME/.config/bspwm/bspwmrc will be used.
+        '';
+      };
 
-                Does nothing unless `bspwm.startThroughSession` is enabled
-            ";
+      sxhkd = {
+        package = mkOption {
+          type        = types.package;
+          default     = pkgs.sxhkd;
+          defaultText = "pkgs.sxhkd";
+          example     = "pkgs.sxhkd-unstable";
+          description = ''
+            sxhkd package to use.
+          '';
         };
+        configFile = mkOption {
+          type        = with types; nullOr path;
+          example     = "${pkgs.bspwm}/share/doc/bspwm/examples/sxhkdrc";
+          default     = null;
+          description = ''
+            Path to the sxhkd configuration file.
+            If null, $HOME/.config/sxhkd/sxhkdrc will be used.
+          '';
+        };
+      };
     };
   };
 
   config = mkIf cfg.enable {
     services.xserver.windowManager.session = singleton {
-      name = "bspwm";
-      start = if cfg.startThroughSession
-        then cfg.sessionScript
-        else ''
-            export _JAVA_AWT_WM_NONREPARENTING=1
-            SXHKD_SHELL=/bin/sh ${pkgs.sxhkd}/bin/sxhkd -f 100 &
-            ${pkgs.bspwm}/bin/bspwm
-        '';
+      name  = "bspwm";
+      start = ''
+        export _JAVA_AWT_WM_NONREPARENTING=1
+        SXHKD_SHELL=/bin/sh ${cfg.sxhkd.package}/bin/sxhkd ${optionalString (cfg.sxhkd.configFile != null) "-c \"${cfg.sxhkd.configFile}\""} &
+        ${cfg.package}/bin/bspwm ${optionalString (cfg.configFile != null) "-c \"${cfg.configFile}\""}
+        waitPID=$!
+      '';
     };
-    environment.systemPackages = [ pkgs.bspwm ];
+    environment.systemPackages = [ cfg.package ];
   };
+
+  imports = [
+   (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm-unstable" "enable" ]
+     "Use services.xserver.windowManager.bspwm.enable and set services.xserver.windowManager.bspwm.package to pkgs.bspwm-unstable to use the unstable version of bspwm.")
+   (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm" "startThroughSession" ]
+     "bspwm package does not provide bspwm-session anymore.")
+   (mkRemovedOptionModule [ "services" "xserver" "windowManager" "bspwm" "sessionScript" ]
+     "bspwm package does not provide bspwm-session anymore.")
+  ];
 }
diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix
index cfe9439b688..f9c75e80db4 100644
--- a/nixos/modules/services/x11/window-managers/i3.nix
+++ b/nixos/modules/services/x11/window-managers/i3.nix
@@ -3,52 +3,58 @@
 with lib;
 
 let
-  wmCfg = config.services.xserver.windowManager;
+  cfg = config.services.xserver.windowManager.i3;
+in
+
+{
+  options.services.xserver.windowManager.i3 = {
+    enable = mkEnableOption "i3 window manager";
 
-  i3option = name: {
-    enable = mkEnableOption name;
     configFile = mkOption {
-      default = null;
-      type = types.nullOr types.path;
+      default     = null;
+      type        = with types; nullOr path;
       description = ''
         Path to the i3 configuration file.
         If left at the default value, $HOME/.i3/config will be used.
       '';
     };
+
     extraSessionCommands = mkOption {
-      default = "";
-      type = types.lines;
+      default     = "";
+      type        = types.lines;
       description = ''
         Shell commands executed just before i3 is started.
       '';
     };
+
+    package = mkOption {
+      type        = types.package;
+      default     = pkgs.i3;
+      defaultText = "pkgs.i3";
+      example     = "pkgs.i3-gaps";
+      description = ''
+        i3 package to use.
+      '';
+    };
   };
 
-  i3config = name: pkg: cfg: {
+  config = mkIf cfg.enable {
     services.xserver.windowManager.session = [{
-      inherit name;
+      name  = "i3";
       start = ''
         ${cfg.extraSessionCommands}
 
-        ${pkg}/bin/i3 ${optionalString (cfg.configFile != null)
+        ${cfg.package}/bin/i3 ${optionalString (cfg.configFile != null)
           "-c \"${cfg.configFile}\""
         } &
         waitPID=$!
       '';
     }];
-    environment.systemPackages = [ pkg ];
-  };
-
-in
-
-{
-  options.services.xserver.windowManager = {
-    i3 = i3option "i3";
-    i3-gaps = i3option "i3-gaps";
+    environment.systemPackages = [ cfg.package ];
   };
 
-  config = mkMerge [
-    (mkIf wmCfg.i3.enable (i3config "i3" pkgs.i3 wmCfg.i3))
-    (mkIf wmCfg.i3-gaps.enable (i3config "i3-gaps" pkgs.i3-gaps wmCfg.i3-gaps))
+  imports = [
+    (mkRemovedOptionModule [ "services" "xserver" "windowManager" "i3-gaps" "enable" ]
+      "Use services.xserver.windowManager.i3.enable and set services.xserver.windowManager.i3.package to pkgs.i3-gaps to use i3-gaps.")
   ];
 }
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index b03f70385b1..f5ed5233818 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -71,15 +71,11 @@ let
     monitors = reverseList (foldl mkMonitor [] xrandrHeads);
   in concatMapStrings (getAttr "value") monitors;
 
-  configFile = pkgs.stdenv.mkDerivation {
-    name = "xserver.conf";
-
-    xfs = optionalString (cfg.useXFS != false)
-      ''FontPath "${toString cfg.useXFS}"'';
-
-    inherit (cfg) config;
-
-    buildCommand =
+  configFile = pkgs.runCommand "xserver.conf"
+    { xfs = optionalString (cfg.useXFS != false)
+        ''FontPath "${toString cfg.useXFS}"'';
+      inherit (cfg) config;
+    }
       ''
         echo 'Section "Files"' >> $out
         echo $xfs >> $out
@@ -102,7 +98,6 @@ let
 
         echo "$config" >> $out
       ''; # */
-  };
 
 in
 
@@ -154,6 +149,22 @@ in
         '';
       };
 
+      autoRepeatDelay = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Sets the autorepeat delay (length of time in milliseconds that a key must be depressed before autorepeat starts).
+        '';
+      };
+
+      autoRepeatInterval = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Sets the autorepeat interval (length of time in milliseconds that should elapse between autorepeat-generated keystrokes).
+        '';
+      };
+
       inputClassSections = mkOption {
         type = types.listOf types.lines;
         default = [];
@@ -504,12 +515,12 @@ in
       { description = "X11 Server";
 
         after = [ "systemd-udev-settle.service" "local-fs.target" "acpid.service" "systemd-logind.service" ];
+        wants = [ "systemd-udev-settle.service" ];
 
         restartIfChanged = false;
 
         environment =
           {
-            XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension.
             XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
             LD_LIBRARY_PATH = concatStringsSep ":" (
               [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ]
@@ -529,6 +540,10 @@ in
           Restart = "always";
           RestartSec = "200ms";
           SyslogIdentifier = "display-manager";
+          # Stop restarting if the display manager stops (crashes) 2 times
+          # in one minute. Starting X typically takes 3-4s.
+          StartLimitInterval = "30s";
+          StartLimitBurst = "3";
         };
       };
 
@@ -541,7 +556,9 @@ in
       ] ++ optional (cfg.display != null) ":${toString cfg.display}"
         ++ optional (cfg.tty     != null) "vt${toString cfg.tty}"
         ++ optional (cfg.dpi     != null) "-dpi ${toString cfg.dpi}"
-        ++ optional (!cfg.enableTCP) "-nolisten tcp";
+        ++ optional (!cfg.enableTCP) "-nolisten tcp"
+        ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}"
+        ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}";
 
     services.xserver.modules =
       concatLists (catAttrs "modules" cfg.drivers) ++
@@ -654,6 +671,8 @@ in
         ${xrandrMonitorSections}
       '';
 
+    fonts.enableDefaultFonts = mkDefault true;
+
   };
 
 }
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 1c587413121..dcf105eb784 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -12,11 +12,13 @@ let
     '';
   });
 
-  path = map getBin
-    [ pkgs.coreutils pkgs.gnugrep pkgs.findutils
-      pkgs.glibc # needed for getent
-      pkgs.shadow
-      pkgs.nettools # needed for hostname
+  path = with pkgs; map getBin
+    [ coreutils
+      gnugrep
+      findutils
+      glibc # needed for getent
+      shadow
+      nettools # needed for hostname
     ];
 
 in
@@ -137,8 +139,14 @@ in
 
         mkdir -m 1777 -p /var/tmp
 
-        # Empty, read-only home directory of many system accounts.
-        mkdir -m 0555 -p /var/empty
+        # Empty, immutable home directory of many system accounts.
+        mkdir -p /var/empty
+        # Make sure it's really empty
+        ${pkgs.e2fsprogs}/bin/chattr -f -i /var/empty || true
+        find /var/empty -mindepth 1 -delete
+        chmod 0555 /var/empty
+        chown root:root /var/empty
+        ${pkgs.e2fsprogs}/bin/chattr -f +i /var/empty || true
       '';
 
     system.activationScripts.usrbinenv = if config.environment.usrbinenv != null
@@ -152,7 +160,7 @@ in
         rmdir --ignore-fail-on-non-empty /usr/bin /usr
       '';
 
-    system.activationScripts.tmpfs =
+    system.activationScripts.specialfs =
       ''
         specialMount() {
           local device="$1"
@@ -160,7 +168,12 @@ in
           local options="$3"
           local fsType="$4"
 
-          ${pkgs.utillinux}/bin/mount -t "$fsType" -o "remount,$options" "$device" "$mountPoint"
+          if ${pkgs.utillinux}/bin/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"
         }
         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 bb97d0c53a6..8747c1e3d4a 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -213,33 +213,30 @@ while (my ($unit, $state) = each %{$activePrev}) {
                 elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes") || boolIsTrue($unitInfo->{'RefuseManualStop'} // "no") ) {
                     $unitsToSkip{$unit} = 1;
                 } else {
-                    # If this unit is socket-activated, then stop the
-                    # socket unit(s) as well, and restart the
-                    # socket(s) instead of the service.
-                    my $socketActivated = 0;
-                    if ($unit =~ /\.service$/) {
-                        my @sockets = split / /, ($unitInfo->{Sockets} // "");
-                        if (scalar @sockets == 0) {
-                            @sockets = ("$baseName.socket");
-                        }
-                        foreach my $socket (@sockets) {
-                            if (defined $activePrev->{$socket}) {
-                                $unitsToStop{$unit} = 1;
-                                $unitsToStart{$unit} = 1;
-                                recordUnit($startListFile, $socket);
-                                $socketActivated = 1;
-                            }
-                        }
-                    }
-
                     if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes")) {
-
                         # This unit should be restarted instead of
                         # stopped and started.
                         $unitsToRestart{$unit} = 1;
                         recordUnit($restartListFile, $unit);
-
                     } else {
+                        # If this unit is socket-activated, then stop the
+                        # socket unit(s) as well, and restart the
+                        # socket(s) instead of the service.
+                        my $socketActivated = 0;
+                        if ($unit =~ /\.service$/) {
+                            my @sockets = split / /, ($unitInfo->{Sockets} // "");
+                            if (scalar @sockets == 0) {
+                                @sockets = ("$baseName.socket");
+                            }
+                            foreach my $socket (@sockets) {
+                                if (defined $activePrev->{$socket}) {
+                                    $unitsToStop{$socket} = 1;
+                                    $unitsToStart{$socket} = 1;
+                                    recordUnit($startListFile, $socket);
+                                    $socketActivated = 1;
+                                }
+                            }
+                        }
 
                         # If the unit is not socket-activated, record
                         # that this unit needs to be started below.
@@ -251,7 +248,6 @@ while (my ($unit, $state) = each %{$activePrev}) {
                         }
 
                         $unitsToStop{$unit} = 1;
-
                     }
                 }
             }
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 2d1b0ffb54c..0c08375da64 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -98,7 +98,7 @@ let
   # `switch-to-configuration' that activates the configuration and
   # makes it bootable.
   baseSystem = showWarnings (
-    if [] == failed then pkgs.stdenv.mkDerivation {
+    if [] == failed then pkgs.stdenvNoCC.mkDerivation {
       name = let hn = config.networking.hostName;
                  nn = if (hn != "") then hn else "unnamed";
           in "nixos-system-${nn}-${config.system.nixosLabel}";
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index bc899984c57..59ecaf8d5a6 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -85,10 +85,14 @@ in
   };
 
   config = mkIf (config.boot.initrd.network.enable && cfg.enable) {
-    assertions = [ {
-      assertion = cfg.hostRSAKey != null || cfg.hostDSSKey != null || cfg.hostECDSAKey != null;
-      message = "You should specify at least one host key for initrd SSH";
-    } ];
+    assertions = [
+      { assertion = cfg.hostRSAKey != null || cfg.hostDSSKey != null || cfg.hostECDSAKey != null;
+        message = "You should specify at least one host key for initrd SSH";
+      }
+      { assertion = cfg.authorizedKeys != [];
+        message = "You should specify at least one authorized key for initrd SSH";
+      }
+    ];
 
     boot.initrd.extraUtilsCommands = ''
       copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear
@@ -118,7 +122,7 @@ in
 
       mkdir -p /root/.ssh
       ${concatStrings (map (key: ''
-        echo -n ${escapeShellArg key} >> /root/.ssh/authorized_keys
+        echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
       '') cfg.authorizedKeys)}
 
       dropbear -s -j -k -E -m -p ${toString cfg.port}
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 0f342f44fe7..e751ff141f7 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -4,7 +4,9 @@ with lib;
 
 let
 
-  kernel = config.boot.kernelPackages.kernel;
+  inherit (config.boot) kernelPatches;
+
+  inherit (config.boot.kernelPackages) kernel;
 
   kernelModulesConf = pkgs.writeText "nixos.conf"
     ''
@@ -21,6 +23,11 @@ in
 
     boot.kernelPackages = mkOption {
       default = pkgs.linuxPackages;
+      apply = kernelPackages: kernelPackages.extend (self: super: {
+        kernel = super.kernel.override {
+          kernelPatches = super.kernel.kernelPatches ++ kernelPatches;
+        };
+      });
       # We don't want to evaluate all of linuxPackages for the manual
       # - some of it might not even evaluate correctly.
       defaultText = "pkgs.linuxPackages";
@@ -39,6 +46,13 @@ in
       '';
     };
 
+    boot.kernelPatches = mkOption {
+      type = types.listOf types.attrs;
+      default = [];
+      example = literalExample "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]";
+      description = "A list of additional patches to apply to the kernel.";
+    };
+
     boot.kernelParams = mkOption {
       type = types.listOf types.str;
       default = [ ];
@@ -200,8 +214,14 @@ in
         "hid_generic" "hid_lenovo"
         "hid_apple" "hid_logitech_dj" "hid_lenovo_tpkbd" "hid_roccat"
 
-        # Misc. stuff.
-        "pcips2" "atkbd"
+        # Misc. keyboard stuff.
+        "pcips2" "atkbd" "i8042"
+
+        # Temporary fix for https://github.com/NixOS/nixpkgs/issues/18451
+        # Remove as soon as upstream gets fixed - marking it:
+        # TODO
+        # FIXME
+        "i8042"
 
         # To wait for SCSI devices to appear.
         "scsi_wait_scan"
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
index 6153578612c..f8a00784034 100644
--- a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -44,10 +44,10 @@ in
       copyKernels = mkOption {
         default = false;
         type = types.bool;
-        description = "
+        description = ''
           Whether copy the necessary boot files into /boot, so
           /nix/store is not needed by the boot loader.
-        ";
+        '';
       };
 
     };
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 61c34cc2f03..294fc1988e9 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -53,9 +53,9 @@ let
       inherit (args) devices;
       inherit (efi) canTouchEfiVariables;
       inherit (cfg)
-        version extraConfig extraPerEntryConfig extraEntries
+        version extraConfig extraPerEntryConfig extraEntries forceInstall 
         extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels
-        default fsIdentifier efiSupport gfxmodeEfi gfxmodeBios;
+        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 [])
@@ -131,51 +131,51 @@ in
           to the respective devices corresponding to those partitions.
         '';
 
-        type = types.listOf types.optionSet;
+        type = with types; listOf (submodule {
+          options = {
+
+            path = mkOption {
+              example = "/boot1";
+              type = types.str;
+              description = ''
+                The path to the boot directory where GRUB will be written. Generally
+                this boot path should double as an EFI path.
+              '';
+            };
+
+            efiSysMountPoint = mkOption {
+              default = null;
+              example = "/boot1/efi";
+              type = types.nullOr types.str;
+              description = ''
+                The path to the efi system mount point. Usually this is the same
+                partition as the above path and can be left as null.
+              '';
+            };
+
+            efiBootloaderId = mkOption {
+              default = null;
+              example = "NixOS-fsid";
+              type = types.nullOr types.str;
+              description = ''
+                The id of the bootloader to store in efi nvram.
+                The default is to name it NixOS and append the path or efiSysMountPoint.
+                This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
+              '';
+            };
+
+            devices = mkOption {
+              default = [ ];
+              example = [ "/dev/sda" "/dev/sdb" ];
+              type = types.listOf types.str;
+              description = ''
+                The path to the devices which will have the GRUB MBR written.
+                Note these are typically device paths and not paths to partitions.
+              '';
+            };
 
-        options = {
-
-          path = mkOption {
-            example = "/boot1";
-            type = types.str;
-            description = ''
-              The path to the boot directory where GRUB will be written. Generally
-              this boot path should double as an EFI path.
-            '';
-          };
-
-          efiSysMountPoint = mkOption {
-            default = null;
-            example = "/boot1/efi";
-            type = types.nullOr types.str;
-            description = ''
-              The path to the efi system mount point. Usually this is the same
-              partition as the above path and can be left as null.
-            '';
-          };
-
-          efiBootloaderId = mkOption {
-            default = null;
-            example = "NixOS-fsid";
-            type = types.nullOr types.str;
-            description = ''
-              The id of the bootloader to store in efi nvram.
-              The default is to name it NixOS and append the path or efiSysMountPoint.
-              This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
-            '';
-          };
-
-          devices = mkOption {
-            default = [ ];
-            example = [ "/dev/sda" "/dev/sdb" ];
-            type = types.listOf types.str;
-            description = ''
-              The path to the devices which will have the GRUB MBR written.
-              Note these are typically device paths and not paths to partitions.
-            '';
           };
-
-        };
+        });
       };
 
       configurationName = mkOption {
@@ -324,8 +324,7 @@ in
 
       fsIdentifier = mkOption {
         default = "uuid";
-        type = types.addCheck types.str
-          (type: type == "uuid" || type == "label" || type == "provided");
+        type = types.enum [ "uuid" "label" "provided" ];
         description = ''
           Determines how GRUB will identify devices when generating the
           configuration file. A value of uuid / label signifies that grub
@@ -357,6 +356,44 @@ in
         '';
       };
 
+      efiInstallAsRemovable = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Whether to invoke <literal>grub-install</literal> with
+          <literal>--removable</literal>.</para>
+
+          <para>Unless you turn this on, GRUB will install itself somewhere in
+          <literal>boot.loader.efi.efiSysMountPoint</literal> (exactly where
+          depends on other config variables). If you've set
+          <literal>boot.loader.efi.canTouchEfiVariables</literal> *AND* you
+          are currently booted in UEFI mode, then GRUB will use
+          <literal>efibootmgr</literal> to modify the boot order in the
+          EFI variables of your firmware to include this location. If you are
+          *not* booted in UEFI mode at the time GRUB is being installed, the
+          NVRAM will not be modified, and your system will not find GRUB at
+          boot time. However, GRUB will still return success so you may miss
+          the warning that gets printed ("<literal>efibootmgr: EFI variables
+          are not supported on this system.</literal>").</para>
+
+          <para>If you turn this feature on, GRUB will install itself in a
+          special location within <literal>efiSysMountPoint</literal> (namely
+          <literal>EFI/boot/boot$arch.efi</literal>) which the firmwares
+          are hardcoded to try first, regardless of NVRAM EFI variables.</para>
+
+          <para>To summarize, turn this on if:
+          <itemizedlist>
+            <listitem><para>You are installing NixOS and want it to boot in UEFI mode,
+            but you are currently booted in legacy mode</para></listitem>
+            <listitem><para>You want to make a drive that will boot regardless of
+            the NVRAM state of the computer (like a USB "removable" drive)</para></listitem>
+            <listitem><para>You simply dislike the idea of depending on NVRAM
+            state to make your drive bootable</para></listitem>
+          </itemizedlist>
+        '';
+      };
+
       enableCryptodisk = mkOption {
         default = false;
         type = types.bool;
@@ -366,6 +403,16 @@ in
         '';
       };
 
+      forceInstall = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to try and forcibly install GRUB even if problems are
+          detected. It is not recommended to enable this unless you know what
+          you are doing.
+        '';
+      };
+
       trustedBoot = {
 
         enable = mkOption {
@@ -465,7 +512,7 @@ in
             + "'boot.loader.grub.mirroredBoots' to make the system bootable.";
         }
         {
-          assertion = all (c: c < 2) (mapAttrsToList (_: c: c) bootDeviceCounters);
+          assertion = cfg.efiSupport || all (c: c < 2) (mapAttrsToList (_: c: c) bootDeviceCounters);
           message = "You cannot have duplicated devices in mirroredBoots";
         }
         {
@@ -484,6 +531,14 @@ in
           assertion = !cfg.trustedBoot.enable || cfg.trustedBoot.systemHasTPM == "YES_TPM_is_activated";
           message = "Trusted GRUB can break the system! Confirm that the system has an activated TPM by setting 'systemHasTPM'.";
         }
+        {
+          assertion = cfg.efiInstallAsRemovable -> cfg.efiSupport;
+          message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn on boot.loader.grub.efiSupport";
+        }
+        {
+          assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables;
+          message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables";
+        }
       ] ++ flip concatMap cfg.mirroredBoots (args: [
         {
           assertion = args.devices != [ ];
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index 06eece5025f..24442ca12a3 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -60,10 +60,12 @@ my $grubTargetEfi = get("grubTargetEfi");
 my $bootPath = get("bootPath");
 my $storePath = get("storePath");
 my $canTouchEfiVariables = get("canTouchEfiVariables");
+my $efiInstallAsRemovable = get("efiInstallAsRemovable");
 my $efiSysMountPoint = get("efiSysMountPoint");
 my $gfxmodeEfi = get("gfxmodeEfi");
 my $gfxmodeBios = get("gfxmodeBios");
 my $bootloaderId = get("bootloaderId");
+my $forceInstall = get("forceInstall");
 $ENV{'PATH'} = get("path");
 
 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
@@ -530,13 +532,14 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
     foreach my $dev (@deviceTargets) {
         next if $dev eq "nodev";
         print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
-        if ($grubTarget eq "") {
-            system("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev)) == 0
-                or die "$0: installation of GRUB on $dev failed\n";
-        } else {
-            system("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", "--target=$grubTarget", Cwd::abs_path($dev)) == 0
-                or die "$0: installation of GRUB on $dev failed\n";
+        my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev));
+        if ($forceInstall eq "true") {
+            push @command, "--force";
+        }
+        if ($grubTarget ne "") {
+            push @command, "--target=$grubTarget";
         }
+        (system @command) == 0 or die "$0: installation of GRUB on $dev failed\n";
     }
 }
 
@@ -544,13 +547,18 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
 # install EFI GRUB
 if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
     print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
+    my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint");
+    if ($forceInstall eq "true") {
+        push @command, "--force";
+    }
     if ($canTouchEfiVariables eq "true") {
-        system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", "--bootloader-id=$bootloaderId") == 0
-                or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
+        push @command, "--bootloader-id=$bootloaderId";
     } else {
-        system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", "--no-nvram") == 0
-                or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
+        push @command, "--no-nvram";
+        push @command, "--removable" if $efiInstallAsRemovable eq "true";
     }
+
+    (system @command) == 0 or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
 }
 
 
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index b7400e333e2..eb8ea613097 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.int;
+      type = types.enum [ 1 2 ];
       description = ''
       '';
     };
@@ -44,10 +44,5 @@ in
     system.build.installBootLoader = builder;
     system.boot.loader.id = "raspberrypi";
     system.boot.loader.kernelFile = platform.kernelTarget;
-    assertions = [
-      { assertion = (cfg.version == 1 || cfg.version == 2);
-        message = "loader.raspberryPi.version should be 1 or 2";
-      }
-    ];
   };
 }
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 c703a3e083b..515136c904c 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 @@
-#! @python@/bin/python
+#! @python3@/bin/python3
 import argparse
 import shutil
 import os
@@ -13,29 +13,21 @@ def copy_if_not_exists(source, dest):
     if not os.path.exists(dest):
         shutil.copyfile(source, dest)
 
-system_dir = lambda generation: "/nix/var/nix/profiles/system-%d-link" % (generation)
+def system_dir(generation):
+    return "/nix/var/nix/profiles/system-%d-link" % (generation)
 
-def write_entry(generation, kernel, initrd):
-    entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
-    generation_dir = os.readlink(system_dir(generation))
-    tmp_path = "%s.tmp" % (entry_file)
-    kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
-    with open("%s/kernel-params" % (generation_dir)) as params_file:
-        kernel_params = kernel_params + params_file.read()
-    with open(tmp_path, 'w') as f:
-        print >> f, "title NixOS"
-        print >> f, "version Generation %d" % (generation)
-        if machine_id is not None: print >> f, "machine-id %s" % (machine_id)
-        print >> f, "linux %s" % (kernel)
-        print >> f, "initrd %s" % (initrd)
-        print >> f, "options %s" % (kernel_params)
-    os.rename(tmp_path, entry_file)
+BOOT_ENTRY = """title NixOS
+version Generation {generation}
+linux {kernel}
+initrd {initrd}
+options {kernel_params}
+"""
 
 def write_loader_conf(generation):
     with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
         if "@timeout@" != "":
-            print >> f, "timeout @timeout@"
-        print >> f, "default nixos-generation-%d" % (generation)
+            f.write("timeout @timeout@\n")
+        f.write("default nixos-generation-%d\n" % generation)
     os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
 
 def copy_from_profile(generation, name, dry_run=False):
@@ -47,10 +39,23 @@ def copy_from_profile(generation, name, dry_run=False):
         copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
     return efi_file_path
 
-def add_entry(generation):
-    efi_kernel_path = copy_from_profile(generation, "kernel")
-    efi_initrd_path = copy_from_profile(generation, "initrd")
-    write_entry(generation, efi_kernel_path, efi_initrd_path)
+def write_entry(generation, machine_id):
+    kernel = copy_from_profile(generation, "kernel")
+    initrd = copy_from_profile(generation, "initrd")
+    entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
+    generation_dir = os.readlink(system_dir(generation))
+    tmp_path = "%s.tmp" % (entry_file)
+    kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
+    with open("%s/kernel-params" % (generation_dir)) as params_file:
+        kernel_params = kernel_params + params_file.read()
+    with open(tmp_path, 'w') as f:
+        f.write(BOOT_ENTRY.format(generation=generation,
+                    kernel=kernel,
+                    initrd=initrd,
+                    kernel_params=kernel_params))
+        if machine_id is not None:
+            f.write("machine-id %s\n" % machine_id)
+    os.rename(tmp_path, entry_file)
 
 def mkdir_p(path):
     try:
@@ -65,8 +70,8 @@ def get_generations(profile):
         "--list-generations",
         "-p",
         "/nix/var/nix/profiles/%s" % (profile),
-        "--option", "build-users-group", ""
-        ])
+        "--option", "build-users-group", ""],
+        universal_newlines=True)
     gen_lines = gen_list.split('\n')
     gen_lines.pop()
     return [ int(line.split()[0]) for line in gen_lines ]
@@ -89,33 +94,37 @@ def remove_old_entries(gens):
         if not path in known_paths:
             os.unlink(path)
 
-parser = argparse.ArgumentParser(description='Update NixOS-related systemd-boot files')
-parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
-args = parser.parse_args()
+def main():
+    parser = argparse.ArgumentParser(description='Update NixOS-related systemd-boot files')
+    parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
+    args = parser.parse_args()
 
-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_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":
-    if "@canTouchEfiVariables@" == "1":
-        subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
-    else:
-        subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "--no-variables", "install"])
+    if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
+        if "@canTouchEfiVariables@" == "1":
+            subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
+        else:
+            subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "--no-variables", "install"])
 
-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
+    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)
+    for gen in gens:
+        write_entry(gen, machine_id)
+        if os.readlink(system_dir(gen)) == args.default_config:
+            write_loader_conf(gen)
 
-gens = get_generations("system")
-remove_old_entries(gens)
-for gen in gens:
-    add_entry(gen)
-    if os.readlink(system_dir(gen)) == args.default_config:
-        write_loader_conf(gen)
+if __name__ == '__main__':
+    main()
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 a778a4f539c..cc43fb8bab4 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -12,7 +12,7 @@ let
 
     isExecutable = true;
 
-    inherit (pkgs) python;
+    inherit (pkgs) python3;
 
     systemd = config.systemd.package;
 
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index f2755b49f88..1f412fe2d8f 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -236,165 +236,165 @@ in
         <filename>/dev/mapper/<replaceable>name</replaceable></filename>.
       '';
 
-      type = types.loaOf types.optionSet;
-
-      options = { name, ... }: { options = {
-
-        name = mkOption {
-          visible = false;
-          default = name;
-          example = "luksroot";
-          type = types.str;
-          description = "Name of the unencrypted device in <filename>/dev/mapper</filename>.";
-        };
-
-        device = mkOption {
-          example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
-          type = types.str;
-          description = "Path of the underlying encrypted block device.";
-        };
-
-        header = mkOption {
-          default = null;
-          example = "/root/header.img";
-          type = types.nullOr types.str;
-          description = ''
-            The name of the file or block device that
-            should be used as header for the encrypted device.
-          '';
-        };
-
-        keyFile = mkOption {
-          default = null;
-          example = "/dev/sdb1";
-          type = types.nullOr types.str;
-          description = ''
-            The name of the file (can be a raw device or a partition) that
-            should be used as the decryption key for the encrypted device. If
-            not specified, you will be prompted for a passphrase instead.
-          '';
-        };
-
-        keyFileSize = mkOption {
-          default = null;
-          example = 4096;
-          type = types.nullOr types.int;
-          description = ''
-            The size of the key file. Use this if only the beginning of the
-            key file should be used as a key (often the case if a raw device
-            or partition is used as key file). If not specified, the whole
-            <literal>keyFile</literal> will be used decryption, instead of just
-            the first <literal>keyFileSize</literal> bytes.
-          '';
-        };
-
-        # FIXME: get rid of this option.
-        preLVM = mkOption {
-          default = true;
-          type = types.bool;
-          description = "Whether the luksOpen will be attempted before LVM scan or after it.";
-        };
-
-        allowDiscards = mkOption {
-          default = false;
-          type = types.bool;
-          description = ''
-            Whether to allow TRIM requests to the underlying device. This option
-            has security implications; please read the LUKS documentation before
-            activating it.
-          '';
-        };
-
-        yubikey = mkOption {
-          default = null;
-          type = types.nullOr types.optionSet;
-          description = ''
-            The options to use for this LUKS device in Yubikey-PBA.
-            If null (the default), Yubikey-PBA will be disabled for this device.
-          '';
+      type = with types; loaOf (submodule (
+        { name, ... }: { options = {
+
+          name = mkOption {
+            visible = false;
+            default = name;
+            example = "luksroot";
+            type = types.str;
+            description = "Name of the unencrypted device in <filename>/dev/mapper</filename>.";
+          };
 
-          options = {
-            twoFactor = mkOption {
-              default = true;
-              type = types.bool;
-              description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
-            };
-
-            slot = mkOption {
-              default = 2;
-              type = types.int;
-              description = "Which slot on the Yubikey to challenge.";
-            };
-
-            saltLength = mkOption {
-              default = 16;
-              type = types.int;
-              description = "Length of the new salt in byte (64 is the effective maximum).";
-            };
-
-            keyLength = mkOption {
-              default = 64;
-              type = types.int;
-              description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
-            };
-
-            iterationStep = mkOption {
-              default = 0;
-              type = types.int;
-              description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
-            };
-
-            gracePeriod = mkOption {
-              default = 2;
-              type = types.int;
-              description = "Time in seconds to wait before attempting to find the Yubikey.";
-            };
-
-            ramfsMountPoint = mkOption {
-              default = "/crypt-ramfs";
-              type = types.str;
-              description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
-            };
-
-            /* TODO: Add to the documentation of the current module:
-
-               Options related to the storing the salt.
-            */
-            storage = {
-              device = mkOption {
-                default = "/dev/sda1";
-                type = types.path;
-                description = ''
-                  An unencrypted device that will temporarily be mounted in stage-1.
-                  Must contain the current salt to create the challenge for this LUKS device.
-                '';
-              };
+          device = mkOption {
+            example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08";
+            type = types.str;
+            description = "Path of the underlying encrypted block device.";
+          };
 
-              fsType = mkOption {
-                default = "vfat";
-                type = types.str;
-                description = "The filesystem of the unencrypted device.";
-              };
+          header = mkOption {
+            default = null;
+            example = "/root/header.img";
+            type = types.nullOr types.str;
+            description = ''
+              The name of the file or block device that
+              should be used as header for the encrypted device.
+            '';
+          };
 
-              mountPoint = mkOption {
-                default = "/crypt-storage";
-                type = types.str;
-                description = "Path where the unencrypted device will be mounted during early boot.";
-              };
+          keyFile = mkOption {
+            default = null;
+            example = "/dev/sdb1";
+            type = types.nullOr types.str;
+            description = ''
+              The name of the file (can be a raw device or a partition) that
+              should be used as the decryption key for the encrypted device. If
+              not specified, you will be prompted for a passphrase instead.
+            '';
+          };
+
+          keyFileSize = mkOption {
+            default = null;
+            example = 4096;
+            type = types.nullOr types.int;
+            description = ''
+              The size of the key file. Use this if only the beginning of the
+              key file should be used as a key (often the case if a raw device
+              or partition is used as key file). If not specified, the whole
+              <literal>keyFile</literal> will be used decryption, instead of just
+              the first <literal>keyFileSize</literal> bytes.
+            '';
+          };
+
+          # FIXME: get rid of this option.
+          preLVM = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Whether the luksOpen will be attempted before LVM scan or after it.";
+          };
+
+          allowDiscards = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Whether to allow TRIM requests to the underlying device. This option
+              has security implications; please read the LUKS documentation before
+              activating it.
+            '';
+          };
 
-              path = mkOption {
-                default = "/crypt-storage/default";
-                type = types.str;
-                description = ''
-                  Absolute path of the salt on the unencrypted device with
-                  that device's root directory as "/".
-                '';
+          yubikey = mkOption {
+            default = null;
+            description = ''
+              The options to use for this LUKS device in Yubikey-PBA.
+              If null (the default), Yubikey-PBA will be disabled for this device.
+            '';
+
+            type = with types; nullOr (submodule {
+              options = {
+                twoFactor = mkOption {
+                  default = true;
+                  type = types.bool;
+                  description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
+                };
+
+                slot = mkOption {
+                  default = 2;
+                  type = types.int;
+                  description = "Which slot on the Yubikey to challenge.";
+                };
+
+                saltLength = mkOption {
+                  default = 16;
+                  type = types.int;
+                  description = "Length of the new salt in byte (64 is the effective maximum).";
+                };
+
+                keyLength = mkOption {
+                  default = 64;
+                  type = types.int;
+                  description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
+                };
+
+                iterationStep = mkOption {
+                  default = 0;
+                  type = types.int;
+                  description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
+                };
+
+                gracePeriod = mkOption {
+                  default = 2;
+                  type = types.int;
+                  description = "Time in seconds to wait before attempting to find the Yubikey.";
+                };
+
+                ramfsMountPoint = mkOption {
+                  default = "/crypt-ramfs";
+                  type = types.str;
+                  description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
+                };
+
+                /* TODO: Add to the documentation of the current module:
+
+                   Options related to the storing the salt.
+                */
+                storage = {
+                  device = mkOption {
+                    default = "/dev/sda1";
+                    type = types.path;
+                    description = ''
+                      An unencrypted device that will temporarily be mounted in stage-1.
+                      Must contain the current salt to create the challenge for this LUKS device.
+                    '';
+                  };
+
+                  fsType = mkOption {
+                    default = "vfat";
+                    type = types.str;
+                    description = "The filesystem of the unencrypted device.";
+                  };
+
+                  mountPoint = mkOption {
+                    default = "/crypt-storage";
+                    type = types.str;
+                    description = "Path where the unencrypted device will be mounted during early boot.";
+                  };
+
+                  path = mkOption {
+                    default = "/crypt-storage/default";
+                    type = types.str;
+                    description = ''
+                      Absolute path of the salt on the unencrypted device with
+                      that device's root directory as "/".
+                    '';
+                  };
+                };
               };
-            };
+            });
           };
-        };
 
-      }; };
+        }; }));
     };
 
     boot.initrd.luks.yubikeySupport = mkOption {
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index ab748550026..b828ad53dc5 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -165,6 +165,11 @@ let
       '';
     };
 
+    extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      description = "Extra configuration append to unit";
+    };
   };
 
   linkOptions = commonNetworkOptions // {
@@ -296,35 +301,35 @@ let
   };
 
   addressOptions = {
-
-    addressConfig = mkOption {
-      default = {};
-      example = { Address = "192.168.0.100/24"; };
-      type = types.addCheck (types.attrsOf unitOption) checkAddress;
-      description = ''
-        Each attribute in this set specifies an option in the
-        <literal>[Address]</literal> section of the unit.  See
-        <citerefentry><refentrytitle>systemd.network</refentrytitle>
-        <manvolnum>5</manvolnum></citerefentry> for details.
-      '';
+    options = {
+      addressConfig = mkOption {
+        default = {};
+        example = { Address = "192.168.0.100/24"; };
+        type = types.addCheck (types.attrsOf unitOption) checkAddress;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[Address]</literal> section of the unit.  See
+          <citerefentry><refentrytitle>systemd.network</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
     };
-
   };
 
   routeOptions = {
-
-    routeConfig = mkOption {
-      default = {};
-      example = { Gateway = "192.168.0.1"; };
-      type = types.addCheck (types.attrsOf unitOption) checkRoute;
-      description = ''
-        Each attribute in this set specifies an option in the
-        <literal>[Route]</literal> section of the unit.  See
-        <citerefentry><refentrytitle>systemd.network</refentrytitle>
-        <manvolnum>5</manvolnum></citerefentry> for details.
-      '';
+    options = {
+      routeConfig = mkOption {
+        default = {};
+        example = { Gateway = "192.168.0.1"; };
+        type = types.addCheck (types.attrsOf unitOption) checkRoute;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[Route]</literal> section of the unit.  See
+          <citerefentry><refentrytitle>systemd.network</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
     };
-
   };
 
   networkOptions = commonNetworkOptions // {
@@ -471,8 +476,7 @@ let
 
     addresses = mkOption {
       default = [ ];
-      type = types.listOf types.optionSet;
-      options = [ addressOptions ];
+      type = with types; listOf (submodule addressOptions);
       description = ''
         A list of address sections to be added to the unit.  See
         <citerefentry><refentrytitle>systemd.network</refentrytitle>
@@ -482,8 +486,7 @@ let
 
     routes = mkOption {
       default = [ ];
-      type = types.listOf types.optionSet;
-      options = [ routeOptions ];
+      type = with types; listOf (submodule routeOptions);
       description = ''
         A list of route sections to be added to the unit.  See
         <citerefentry><refentrytitle>systemd.network</refentrytitle>
@@ -517,6 +520,8 @@ let
         ''
           [Link]
           ${attrsToSection def.linkConfig}
+
+          ${def.extraConfig}
         '';
     };
 
@@ -567,6 +572,7 @@ let
             ${attrsToSection def.bondConfig}
 
           ''}
+          ${def.extraConfig}
         '';
     };
 
@@ -605,9 +611,14 @@ let
             ${attrsToSection x.routeConfig}
 
           '')}
+          ${def.extraConfig}
         '';
     };
 
+  unitFiles = map (name: {
+    target = "systemd/network/${name}";
+    source = "${cfg.units.${name}.unit}/${name}";
+  }) (attrNames cfg.units);
 in
 
 {
@@ -624,35 +635,32 @@ in
 
     systemd.network.links = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ linkOptions ];
+      type = with types; attrsOf (submodule [ { options = linkOptions; } ]);
       description = "Definition of systemd network links.";
     };
 
     systemd.network.netdevs = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ netdevOptions ];
+      type = with types; attrsOf (submodule [ { options = netdevOptions; } ]);
       description = "Definition of systemd network devices.";
     };
 
     systemd.network.networks = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ networkOptions networkConfig ];
+      type = with types; attrsOf (submodule [ { options = networkOptions; } networkConfig ]);
       description = "Definition of systemd networks.";
     };
 
     systemd.network.units = mkOption {
       description = "Definition of networkd units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
   };
@@ -662,23 +670,19 @@ in
     systemd.additionalUpstreamSystemUnits =
       [ "systemd-networkd.service" "systemd-networkd-wait-online.service" ];
 
-    systemd.network.units =
-      mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links
+    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
       // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
 
-    environment.etc."systemd/network".source =
-      generateUnits "network" cfg.units [] [];
+    environment.etc = unitFiles;
 
     systemd.services.systemd-networkd = {
       wantedBy = [ "multi-user.target" ];
-      before = [ "network-interfaces.target" ];
-      restartTriggers = [ config.environment.etc."systemd/network".source ];
+      restartTriggers = map (f: f.source) (unitFiles);
     };
 
     systemd.services.systemd-networkd-wait-online = {
-      before = [ "network-online.target" "ip-up.target" ];
-      wantedBy = [ "network-online.target" "ip-up.target" ];
+      wantedBy = [ "network-online.target" ];
     };
 
     systemd.services."systemd-network-wait-online@" = {
@@ -694,8 +698,5 @@ in
     };
 
     services.resolved.enable = mkDefault true;
-    services.timesyncd.enable = mkDefault config.services.ntp.enable;
-
   };
-
 }
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index 60a587af8e9..d45b1686c1e 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -51,6 +51,10 @@ in
           url = "https://nixos.org/logo/nixos-hires.png";
           sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
         };
+        defaultText = ''pkgs.fetchurl {
+          url = "https://nixos.org/logo/nixos-hires.png";
+          sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
+        }'';
         description = ''
           Logo which is displayed on the splash screen.
         '';
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index abab5f20baa..f0699ad9832 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -318,6 +318,8 @@ mountFS() {
     [ "$mountPoint" == "/" ] &&
         [ -f "/mnt-root/etc/NIXOS_LUSTRATE" ] &&
         lustrateRoot "/mnt-root"
+
+    true
 }
 
 lustrateRoot () {
@@ -498,8 +500,7 @@ eval "exec $logOutFd>&- $logErrFd>&-"
 #
 # Storage daemons are distinguished by an @ in front of their command line:
 # https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
-local pidsToKill="$(pgrep -v -f '^@')"
-for pid in $pidsToKill; do
+for pid in $(pgrep -v -f '^@'); do
     # Make sure we don't kill kernel processes, see #15226 and:
     # http://stackoverflow.com/questions/12213445/identifying-kernel-threads
     readlink "/proc/$pid/exe" &> /dev/null || continue
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 513c121347b..61def24efd8 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -34,7 +34,7 @@ let
   # copy what we need.  Instead of using statically linked binaries,
   # we just copy what we need from Glibc and use patchelf to make it
   # work.
-  extraUtils = pkgs.runCommand "extra-utils"
+  extraUtils = pkgs.runCommandCC "extra-utils"
     { buildInputs = [pkgs.nukeReferences];
       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
     }
@@ -134,10 +134,9 @@ let
     ''; # */
 
 
-  udevRules = pkgs.stdenv.mkDerivation {
-    name = "udev-rules";
-    allowedReferences = [ extraUtils ];
-    buildCommand = ''
+  udevRules = pkgs.runCommand "udev-rules"
+    { allowedReferences = [ extraUtils ]; }
+    ''
       mkdir -p $out
 
       echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
@@ -176,7 +175,6 @@ let
       substituteInPlace $out/60-persistent-storage.rules \
         --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
     ''; # */
-  };
 
 
   # The init script of boot stage 1 (loading kernel modules for
@@ -198,9 +196,10 @@ let
       preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules;
 
     resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
-                    (filter (sd: (sd ? label || hasPrefix "/dev/" sd.device) && !sd.randomEncryption 
-                    # Don't include zram devices
-                    && !(hasPrefix "/dev/zram" sd.device)) config.swapDevices);
+                    (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption
+                             # Don't include zram devices
+                             && !(hasPrefix "/dev/zram" sd.device)
+                            ) config.swapDevices);
 
     fsInfo =
       let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ];
@@ -229,16 +228,12 @@ let
         { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
           symlink = "/etc/mdadm.conf";
         }
-        { object = pkgs.stdenv.mkDerivation {
-            name = "initrd-kmod-blacklist-ubuntu";
-            builder = pkgs.writeText "builder.sh" ''
-              source $stdenv/setup
+        { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu"
+            { src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; }
+            ''
               target=$out
-
               ${pkgs.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
             '';
-            src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
-          };
           symlink = "/etc/modprobe.d/ubuntu.conf";
         }
         { object = pkgs.kmod-debian-aliases;
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 946897b1c43..86f552cd3ca 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -29,7 +29,7 @@ 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.
-if [ "$container" != systemd-nspawn ]; then
+if [ -z "$container" ]; then
     mount -n -o remount,rw none /
 fi
 
@@ -111,16 +111,6 @@ rm -f /etc/{group,passwd,shadow}.lock
 rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
 
 
-# Create a ramfs on /run/keys to hold secrets that shouldn't be
-# written to disk (generally used for NixOps, harmless elsewhere).
-if ! mountpoint -q /run/keys; then
-    rm -rf /run/keys
-    mkdir /run/keys
-    mount -t ramfs ramfs /run/keys
-    chown 0:96 /run/keys
-    chmod 0750 /run/keys
-fi
-
 mkdir -m 0755 -p /run/lock
 
 
diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix
index 2e93693cbfc..997770b8bec 100644
--- a/nixos/modules/system/boot/systemd-lib.nix
+++ b/nixos/modules/system/boot/systemd-lib.nix
@@ -182,7 +182,7 @@ rec {
         mkdir -p $out/getty.target.wants/
         ln -s ../autovt@tty1.service $out/getty.target.wants/
 
-        ln -s ../local-fs.target ../remote-fs.target ../network.target \
+        ln -s ../local-fs.target ../remote-fs.target \
         ../nss-lookup.target ../nss-user-lookup.target ../swap.target \
         $out/multi-user.target.wants/
       ''}
diff --git a/nixos/modules/system/boot/systemd-nspawn.nix b/nixos/modules/system/boot/systemd-nspawn.nix
new file mode 100644
index 00000000000..f765db275e7
--- /dev/null
+++ b/nixos/modules/system/boot/systemd-nspawn.nix
@@ -0,0 +1,122 @@
+{ config, lib , pkgs, ...}:
+
+with lib;
+with import ./systemd-unit-options.nix { inherit config lib; };
+with import ./systemd-lib.nix { inherit config lib pkgs; };
+
+let
+  cfg = config.systemd.nspawn;
+  assertions = [
+    # boot = true -> processtwo != true
+  ];
+
+  checkExec = checkUnitConfig "Exec" [
+    (assertOnlyFields [
+      "Boot" "ProcessTwo" "Parameters" "Environment" "User" "WorkingDirectory"
+      "Capability" "DropCapability" "KillSignal" "Personality" "MachineId"
+      "PrivateUsers"
+    ])
+    (assertValueOneOf "Boot" boolValues)
+    (assertValueOneOf "ProcessTwo" boolValues)
+    (assertValueOneOf "PrivateUsers" (boolValues ++ [ "pick" ]))
+  ];
+
+  checkFiles = checkUnitConfig "Files" [
+    (assertOnlyFields [
+      "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystems"
+      "PrivateUsersChown"
+    ])
+    (assertValueOneOf "ReadOnly" boolValues)
+    (assertValueOneOf "Volatile" (boolValues ++ [ "state" ]))
+    (assertValueOneOf "PrivateUsersChown" boolValues)
+  ];
+
+  checkNetwork = checkUnitConfig "Network" [
+    (assertOnlyFields [
+      "Private" "VirtualEthernet" "VirtualEthernetExtra" "Interface" "MACVLAN"
+      "IPVLAN" "Bridge" "Zone" "Port"
+    ])
+    (assertValueOneOf "Private" boolValues)
+    (assertValueOneOf "VirtualEthernet" boolValues)
+  ];
+
+  instanceOptions = {
+    options = {
+
+      execConfig = mkOption {
+        default = {};
+        example = { Parameters = "/bin/sh"; };
+        type = types.addCheck (types.attrsOf unitOption) checkExec;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[Exec]</literal> section of this unit. See
+          <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
+
+      filesConfig = mkOption {
+        default = {};
+        example = { Bind = [ "/home/alice" ]; };
+        type = types.addCheck (types.attrsOf unitOption) checkFiles;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[Files]</literal> section of this unit. See
+          <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
+
+      networkConfig = mkOption {
+        default = {};
+        example = { Private = false; };
+        type = types.addCheck (types.attrsOf unitOption) checkNetwork;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[Network]</literal> section of this unit. See
+          <citerefentry><refentrytitle>systemd.nspawn</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
+    };
+
+  };
+
+  instanceToUnit = name: def: 
+    { text = ''
+      [Exec]
+      ${attrsToSection def.execConfig}
+
+      [Files]
+      ${attrsToSection def.filesConfig}
+
+      [Network]
+      ${attrsToSection def.networkConfig}
+    '';
+    };
+
+in {
+
+  options = {
+
+    systemd.nspawn = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule instanceOptions);
+      description = "Definition of systemd-nspawn configurations.";
+    };
+
+  };
+
+  config =
+    let
+      units = mapAttrs' (n: v: nameValuePair "${n}.nspawn" (instanceToUnit n v)) cfg.instances;
+    in mkIf (cfg != {}) {
+
+      environment.etc."systemd/nspawn".source = generateUnits "nspawn" units [] [];
+
+      systemd.services."systemd-nspawn@" = {
+        wantedBy = [ "machine.target" ];
+      };
+  };
+
+}
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index f4892244de4..69af2398148 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -77,6 +77,12 @@ in rec {
       description = "Description of this unit used in systemd messages and progress indicators.";
     };
 
+    documentation = mkOption {
+      default = [];
+      type = types.listOf types.str;
+      description = "A list of URIs referencing documentation for this unit or its configuration.";
+    };
+
     requires = mkOption {
       default = [];
       type = types.listOf types.str;
@@ -310,7 +316,7 @@ in rec {
 
     startAt = mkOption {
       type = with types; either str (listOf str);
-      default = "";
+      default = [];
       example = "Sun 14:00:00";
       description = ''
         Automatically start this unit at the given date/time, which
@@ -320,6 +326,7 @@ in rec {
         to adding a corresponding timer unit with
         <option>OnCalendar</option> set to the value given here.
       '';
+      apply = v: if isList v then v else [ v ];
     };
 
   };
@@ -458,4 +465,20 @@ in rec {
 
   targetOptions = commonUnitOptions;
 
+  sliceOptions = commonUnitOptions // {
+
+    sliceConfig = mkOption {
+      default = {};
+      example = { MemoryMax = "2G"; };
+      type = types.attrsOf unitOption;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Slice]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.slice</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
 }
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index df72be1b4be..a2ee5166971 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -14,6 +14,7 @@ let
   upstreamSystemUnits =
     [ # Targets.
       "basic.target"
+      "busnames.target"
       "sysinit.target"
       "sockets.target"
       "graphical.target"
@@ -140,6 +141,7 @@ let
       "user.slice"
       "machine.slice"
       "systemd-machined.service"
+      "systemd-nspawn@.service"
 
       # Temporary file creation / cleanup.
       "systemd-tmpfiles-clean.service"
@@ -183,6 +185,8 @@ let
       "timers.target"
     ];
 
+  boolToString = value: if value then "yes" else "no";
+
   makeJobScript = name: text:
     let mkScriptName =  s: (replaceChars [ "\\" ] [ "-" ] (shellEscape s) );
         x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${mkScriptName name}"; inherit text; };
@@ -210,8 +214,10 @@ let
         // optionalAttrs (config.restartTriggers != [])
           { X-Restart-Triggers = toString config.restartTriggers; }
         // optionalAttrs (config.description != "") {
-          Description = config.description;
-        } // optionalAttrs (config.onFailure != []) {
+          Description = config.description; }
+        // optionalAttrs (config.documentation != []) {
+          Documentation = toString config.documentation; }
+        // optionalAttrs (config.onFailure != []) {
           OnFailure = toString config.onFailure;
         };
     };
@@ -369,6 +375,15 @@ let
         '';
     };
 
+  sliceToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text = commonUnitText def +
+        ''
+          [Slice]
+          ${attrsToSection def.sliceConfig}
+        '';
+    };
+
 in
 
 {
@@ -387,13 +402,13 @@ in
     systemd.units = mkOption {
       description = "Definition of systemd units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
     systemd.packages = mkOption {
@@ -404,43 +419,37 @@ in
 
     systemd.targets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ targetOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
       description = "Definition of systemd target units.";
     };
 
     systemd.services = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ serviceOptions unitConfig serviceConfig ];
+      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
       description = "Definition of systemd service units.";
     };
 
     systemd.sockets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ socketOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
       description = "Definition of systemd socket units.";
     };
 
     systemd.timers = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ timerOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
       description = "Definition of systemd timer units.";
     };
 
     systemd.paths = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ pathOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
       description = "Definition of systemd path units.";
     };
 
     systemd.mounts = mkOption {
       default = [];
-      type = types.listOf types.optionSet;
-      options = [ mountOptions unitConfig mountConfig ];
+      type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
       description = ''
         Definition of systemd mount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -450,8 +459,7 @@ in
 
     systemd.automounts = mkOption {
       default = [];
-      type = types.listOf types.optionSet;
-      options = [ automountOptions unitConfig automountConfig ];
+      type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
       description = ''
         Definition of systemd automount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -459,6 +467,12 @@ in
       '';
     };
 
+    systemd.slices = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] );
+      description = "Definition of slice configurations.";
+    };
+
     systemd.generators = mkOption {
       type = types.attrsOf types.path;
       default = {};
@@ -569,6 +583,16 @@ in
       '';
     };
 
+    systemd.user.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      example = "DefaultCPUAccounting=yes";
+      description = ''
+        Extra config options for systemd user instances. See man systemd-user.conf for
+        available options.
+      '';
+    };
+
     systemd.tmpfiles.rules = mkOption {
       type = types.listOf types.str;
       default = [];
@@ -588,36 +612,39 @@ in
     systemd.user.units = mkOption {
       description = "Definition of systemd per-user units.";
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = { name, config, ... }:
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
         { options = concreteUnitOptions;
           config = {
             unit = mkDefault (makeUnit name config);
           };
-        };
+        }));
     };
 
     systemd.user.services = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ serviceOptions unitConfig serviceConfig ];
+      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
       description = "Definition of systemd per-user service units.";
     };
 
     systemd.user.timers = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ timerOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
       description = "Definition of systemd per-user timer units.";
     };
 
     systemd.user.sockets = mkOption {
       default = {};
-      type = types.attrsOf types.optionSet;
-      options = [ socketOptions unitConfig ];
+      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
       description = "Definition of systemd per-user socket units.";
     };
 
+    systemd.user.targets = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+      description = "Definition of systemd per-user target units.";
+    };
+
     systemd.additionalUpstreamSystemUnits = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -663,6 +690,11 @@ in
         ${config.systemd.extraConfig}
       '';
 
+      "systemd/user.conf".text = ''
+        [Manager]
+        ${config.systemd.user.extraConfig}
+      '';
+
       "systemd/journald.conf".text = ''
         [Journal]
         RateLimitInterval=${config.services.journald.rateLimitInterval}
@@ -725,24 +757,13 @@ in
         unitConfig.X-StopOnReconfiguration = true;
       };
 
-    systemd.targets.network-online.after = [ "ip-up.target" ];
-
-    systemd.targets.network-pre = {
-      wantedBy = [ "network.target" ];
-      before = [ "network.target" ];
-    };
-
-    systemd.targets.remote-fs-pre = {
-      wantedBy = [ "remote-fs.target" ];
-      before = [ "remote-fs.target" ];
-    };
-
     systemd.units =
       mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
       // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
       // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
       // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
       // mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
+      // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
       // listToAttrs (map
                    (v: let n = escapeSystemdPath v.where;
                        in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
@@ -753,6 +774,7 @@ in
     systemd.user.units =
          mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
       // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.user.sockets
+      // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.user.targets
       // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.user.timers;
 
     system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
@@ -771,7 +793,7 @@ in
         { wantedBy = [ "timers.target" ];
           timerConfig.OnCalendar = service.startAt;
         })
-        (filterAttrs (name: service: service.enable && service.startAt != "") cfg.services);
+        (filterAttrs (name: service: service.enable && service.startAt != []) cfg.services);
 
     # Generate timer units for all services that have a ‘startAt’ value.
     systemd.user.timers =
@@ -779,7 +801,7 @@ in
         { wantedBy = [ "timers.target" ];
           timerConfig.OnCalendar = service.startAt;
         })
-        (filterAttrs (name: service: service.startAt != "") cfg.user.services);
+        (filterAttrs (name: service: service.startAt != []) cfg.user.services);
 
     systemd.sockets.systemd-journal-gatewayd.wantedBy =
       optional config.services.journald.enableHttpGateway "sockets.target";
@@ -803,6 +825,8 @@ in
     systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
     systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
     systemd.services.systemd-logind.stopIfChanged = false;
+    systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
+    systemd.services.systemd-journald.stopIfChanged = false;
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ];
diff --git a/nixos/modules/system/boot/timesyncd.nix b/nixos/modules/system/boot/timesyncd.nix
index cba965b1cd2..f643723ab14 100644
--- a/nixos/modules/system/boot/timesyncd.nix
+++ b/nixos/modules/system/boot/timesyncd.nix
@@ -6,14 +6,21 @@ with lib;
 
   options = {
 
-    services.timesyncd.enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Enables the systemd NTP client daemon.
-      '';
+    services.timesyncd = {
+      enable = mkOption {
+        default = !config.boot.isContainer;
+        type = types.bool;
+        description = ''
+          Enables the systemd NTP client daemon.
+        '';
+      };
+      servers = mkOption {
+        default = config.networking.timeServers;
+        description = ''
+          The set of NTP servers from which to synchronise.
+        '';
+      };
     };
-
   };
 
   config = mkIf config.services.timesyncd.enable {
@@ -30,8 +37,6 @@ with lib;
       NTP=${concatStringsSep " " config.services.ntp.servers}
     '';
 
-    systemd.services.ntpd.enable = false;
-
     users.extraUsers.systemd-timesync.uid = config.ids.uids.systemd-timesync;
     users.extraGroups.systemd-timesync.gid = config.ids.gids.systemd-timesync;
 
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 163f4f4106e..fd6e58cd5b4 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -8,7 +8,7 @@ let
 
   etc' = filter (f: f.enable) (attrValues config.environment.etc);
 
-  etc = pkgs.stdenv.mkDerivation {
+  etc = pkgs.stdenvNoCC.mkDerivation {
     name = "etc";
 
     builder = ./make-etc.sh;
@@ -33,7 +33,6 @@ in
   options = {
 
     environment.etc = mkOption {
-      type = types.loaOf types.optionSet;
       default = {};
       example = literalExample ''
         { example-configuration-file =
@@ -47,7 +46,8 @@ in
         Set of files that have to be linked in <filename>/etc</filename>.
       '';
 
-      options = singleton ({ name, config, ... }:
+      type = with types; loaOf (submodule (
+        { name, config, ... }:
         { options = {
 
             enable = mkOption {
@@ -117,7 +117,7 @@ in
               in mkDefault (pkgs.writeText name' config.text));
           };
 
-        });
+        }));
 
     };
 
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 9ab1baeacb9..49ba66ad50a 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -18,7 +18,7 @@ let
 
   prioOption = prio: optionalString (prio != null) " pri=${toString prio}";
 
-  specialFSTypes = [ "proc" "sysfs" "tmpfs" "devtmpfs" "devpts" ];
+  specialFSTypes = [ "proc" "sysfs" "tmpfs" "ramfs" "devtmpfs" "devpts" ];
 
   coreFileSystemOpts = { name, config, ... }: {
 
@@ -258,7 +258,7 @@ in
           let
             mountPoint' = "${escapeSystemdPath fs.mountPoint}.mount";
             device'  = escapeSystemdPath fs.device;
-            device'' = "${device}.device";
+            device'' = "${device'}.device";
           in nameValuePair "mkfs-${device'}"
           { description = "Initialisation of Filesystem ${fs.device}";
             wantedBy = [ mountPoint' ];
@@ -286,11 +286,18 @@ in
     # Sync mount options with systemd's src/core/mount-setup.c: mount_table.
     boot.specialFileSystems = {
       "/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; };
-      "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };
-      "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
+      "/run" = { fsType = "tmpfs"; options = [ "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}" ]; };
+
+      # To hold secrets that shouldn't be written to disk (generally used for NixOps, harmless elsewhere)
+      "/run/keys" = { fsType = "ramfs"; options = [ "nosuid" "nodev" "mode=750" "gid=${toString config.ids.gids.keys}" ]; };
+    } // optionalAttrs (!config.boot.isContainer) {
+      # systemd-nspawn populates /sys by itself, and remounting it causes all
+      # kinds of weird issues (most noticeably, waiting for host disk device
+      # nodes).
+      "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };
     };
 
   };
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index e454eca3a0e..e9a7ccc721a 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -38,15 +38,17 @@ in
         default = null;
         example = 4000;
         description = ''
-          Use fixed port for rpc.statd, useful if NFS server is behind firewall.
+          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;
         description = ''
-          Use fixed port for NFS lock manager kernel module (lockd/nlockmgr),
-          useful if NFS server is behind firewall.
+          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.
         '';
       };
     };
@@ -68,13 +70,16 @@ in
 
     boot.initrd.kernelModules = mkIf inInitrd [ "nfs" ];
 
+    # FIXME: should use upstream units from nfs-utils.
+
     systemd.services.statd =
       { description = "NFSv3 Network Status Monitor";
 
         path = [ pkgs.nfs-utils pkgs.sysvtools pkgs.utillinux ];
 
-        wantedBy = [ "remote-fs-pre.target" ];
+        wants = [ "remote-fs-pre.target" ];
         before = [ "remote-fs-pre.target" ];
+        wantedBy = [ "remote-fs.target" ];
         requires = [ "basic.target" "rpcbind.service" ];
         after = [ "basic.target" "rpcbind.service" ];
 
@@ -100,8 +105,9 @@ in
 
         path = [ pkgs.sysvtools pkgs.utillinux ];
 
-        wantedBy = [ "remote-fs-pre.target" ];
+        wants = [ "remote-fs-pre.target" ];
         before = [ "remote-fs-pre.target" ];
+        wantedBy = [ "remote-fs.target" ];
         requires = [ "rpcbind.service" ];
         after = [ "rpcbind.service" ];
 
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index c5f41cc338c..045cbeb7cff 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -22,12 +22,18 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  splKernelPkg = kernel.spl;
-  zfsKernelPkg = kernel.zfs;
-  zfsUserPkg = pkgs.zfs;
+  packages = if config.boot.zfs.enableUnstable then {
+    spl = kernel.splUnstable;
+    zfs = kernel.zfsUnstable;
+    zfsUser = pkgs.zfsUnstable;
+  } else {
+    spl = kernel.spl;
+    zfs = kernel.zfs;
+    zfsUser = pkgs.zfs;
+  };
 
   autosnapPkg = pkgs.zfstools.override {
-    zfs = zfsUserPkg;
+    zfs = packages.zfsUser;
   };
 
   zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
@@ -54,6 +60,18 @@ in
 
   options = {
     boot.zfs = {
+      enableUnstable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use the unstable zfs package. This might be an option, if the latest
+          kernel is not yet supported by a published release of ZFS. Enabling
+          this option will install a development version of ZFS on Linux. The
+          version will have already passed an extensive test suite, but it is
+          more likely to hit an undiscovered bug compared to running a released
+          version of ZFS on Linux.
+        '';
+      };
 
       extraPools = mkOption {
         type = types.listOf types.str;
@@ -218,16 +236,16 @@ in
 
       boot = {
         kernelModules = [ "spl" "zfs" ] ;
-        extraModulePackages = [ splKernelPkg zfsKernelPkg ];
+        extraModulePackages = with packages; [ spl zfs ];
       };
 
       boot.initrd = mkIf inInitrd {
         kernelModules = [ "spl" "zfs" ];
         extraUtilsCommands =
           ''
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zfs
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zdb
-            copy_bin_and_libs ${zfsUserPkg}/sbin/zpool
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zfs
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zdb
+            copy_bin_and_libs ${packages.zfsUser}/sbin/zpool
           '';
         extraUtilsCommandsTest = mkIf inInitrd
           ''
@@ -264,14 +282,14 @@ in
         zfsSupport = true;
       };
 
-      environment.etc."zfs/zed.d".source = "${zfsUserPkg}/etc/zfs/zed.d/*";
+      environment.etc."zfs/zed.d".source = "${packages.zfsUser}/etc/zfs/zed.d/*";
 
-      system.fsPackages = [ zfsUserPkg ];                  # XXX: needed? zfs doesn't have (need) a fsck
-      environment.systemPackages = [ zfsUserPkg ]
-        ++ optional enableAutoSnapshots autosnapPkg;       # so the user can run the command to see flags
+      system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck
+      environment.systemPackages = [ packages.zfsUser ]
+        ++ optional enableAutoSnapshots autosnapPkg; # so the user can run the command to see flags
 
-      services.udev.packages = [ zfsUserPkg ];             # to hook zvol naming, etc.
-      systemd.packages = [ zfsUserPkg ];
+      services.udev.packages = [ packages.zfsUser ]; # to hook zvol naming, etc.
+      systemd.packages = [ packages.zfsUser ];
 
       systemd.services = let
         getPoolFilesystems = pool:
@@ -298,7 +316,7 @@ in
               RemainAfterExit = true;
             };
             script = ''
-              zpool_cmd="${zfsUserPkg}/sbin/zpool"
+              zpool_cmd="${packages.zfsUser}/sbin/zpool"
               ("$zpool_cmd" list "${pool}" >/dev/null) || "$zpool_cmd" import -d ${cfgZfs.devNodes} -N ${optionalString cfgZfs.forceImportAll "-f"} "${pool}"
             '';
           };
@@ -314,7 +332,7 @@ in
               RemainAfterExit = true;
             };
             script = ''
-              ${zfsUserPkg}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
+              ${packages.zfsUser}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
             '';
           };
 
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index c960e401f9b..c50ea5c7964 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -46,6 +46,23 @@ in
     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 [];
+
         networkLocalCommands = {
           after = [ "network-setup.service" ];
           bindsTo = [ "network-setup.service" ];
@@ -54,16 +71,22 @@ in
         networkSetup =
           { description = "Networking Setup";
 
-            after = [ "network-interfaces.target" "network-pre.target" ];
-            before = [ "network.target" ];
-            wantedBy = [ "network.target" ];
+            after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ];
+            before = [ "network.target" "shutdown.target" ];
+            wants = [ "network.target" ];
+            conflicts = [ "shutdown.target" ];
+            wantedBy = [ "multi-user.target" ];
 
             unitConfig.ConditionCapability = "CAP_NET_ADMIN";
 
             path = [ pkgs.iproute ];
 
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+            };
+
+            unitConfig.DefaultDependencies = false;
 
             script =
               ''
@@ -108,10 +131,14 @@ in
           in
           nameValuePair "network-addresses-${i.name}"
           { description = "Address configuration of ${i.name}";
-            wantedBy = [ "network-interfaces.target" ];
-            before = [ "network-interfaces.target" ];
-            bindsTo = [ (subsystemDevice i.name) ];
-            after = [ (subsystemDevice i.name) "network-pre.target" ];
+            wantedBy = [ "network-setup.service" ];
+            # propagate stop and reload from network-setup
+            partOf = [ "network-setup.service" ];
+            # order before network-setup because the routes that are configured
+            # there may need ip addresses configured
+            before = [ "network-setup.service" ];
+            bindsTo = deviceDependency i.name;
+            after = [ "network-pre.target" ] ++ (deviceDependency i.name);
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -129,21 +156,11 @@ in
                   echo "checking ip ${address}..."
                   if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
                     echo "added ip ${address}..."
-                    restart_network_setup=true
                   elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
                     echo "failed to add ${address}"
                     exit 1
                   fi
-                '')
-              + optionalString (ips != [ ])
-                ''
-                  if [ "$restart_network_setup" = "true" ]; then
-                    # Ensure that the default gateway remains set.
-                    # (Flushing this interface may have removed it.)
-                    ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  fi
-                  ${config.systemd.package}/bin/systemctl start ip-up.target
-                '';
+                '');
             preStop = flip concatMapStrings (ips) (ip:
                 let
                   address = "${ip.address}/${toString ip.prefixLength}";
@@ -157,10 +174,11 @@ in
 
         createTunDevice = i: nameValuePair "${i.name}-netdev"
           { description = "Virtual Network Interface ${i.name}";
-            requires = [ "dev-net-tun.device" ];
+            bindsTo = [ "dev-net-tun.device" ];
             after = [ "dev-net-tun.device" "network-pre.target" ];
-            wantedBy = [ "network.target" (subsystemDevice i.name) ];
-            before = [ "network-interfaces.target" (subsystemDevice i.name) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice i.name) ];
+            partOf = [ "network-setup.service" ];
+            before = [ "network-setup.service" (subsystemDevice i.name) ];
             path = [ pkgs.iproute ];
             serviceConfig = {
               Type = "oneshot";
@@ -178,15 +196,15 @@ in
 
         createBridgeDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
           in
           { description = "Bridge Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps ++ optional v.rstp "mstpd.service";
-            partOf = optional v.rstp "mstpd.service";
+            partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service";
             after = [ "network-pre.target" "mstpd.service" ] ++ deps
               ++ concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) v.interfaces;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -219,15 +237,15 @@ in
 
         createVswitchDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
             ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules;
           in
           { description = "Open vSwitch Interface ${n}";
-            wantedBy = [ "network.target" "vswitchd.service" ] ++ deps;
+            wantedBy = [ "network-setup.service" "vswitchd.service" ] ++ deps;
             bindsTo =  [ "vswitchd.service" (subsystemDevice n) ] ++ deps;
-            partOf = [ "vswitchd.service" ];
+            partOf = [ "network-setup.service" "vswitchd.service" ];
             after = [ "network-pre.target" "vswitchd.service" ] ++ deps;
-            before = [ "network-interfaces.target" ];
+            before = [ "network-setup.service" ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute config.virtualisation.vswitch.package ];
@@ -252,14 +270,15 @@ in
 
         createBondDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = map subsystemDevice v.interfaces;
+            deps = concatLists (map deviceDependency v.interfaces);
           in
           { description = "Bond Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps
               ++ concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) v.interfaces;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute pkgs.gawk ];
@@ -289,13 +308,14 @@ in
 
         createMacvlanDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = [ (subsystemDevice v.interface) ];
+            deps = deviceDependency v.interface;
           in
           { description = "Vlan Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -313,13 +333,14 @@ in
 
         createSitDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = optional (v.dev != null) (subsystemDevice v.dev);
+            deps = deviceDependency v.dev;
           in
           { description = "6-to-4 Tunnel Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
@@ -340,13 +361,14 @@ in
 
         createVlanDevice = n: v: nameValuePair "${n}-netdev"
           (let
-            deps = [ (subsystemDevice v.interface) ];
+            deps = deviceDependency v.interface;
           in
           { description = "Vlan Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
             bindsTo = deps;
+            partOf = [ "network-setup.service" ];
             after = [ "network-pre.target" ] ++ deps;
-            before = [ "network-interfaces.target" (subsystemDevice n) ];
+            before = [ "network-setup.service" (subsystemDevice n) ];
             serviceConfig.Type = "oneshot";
             serviceConfig.RemainAfterExit = true;
             path = [ pkgs.iproute ];
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 301ee43fd0e..974041d7e1a 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -43,7 +43,7 @@ in
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
     });
 
-    systemd.services.dhcpcd.enable = mkDefault false;
+    networking.dhcpcd.enable = mkDefault false;
 
     systemd.services.network-local-commands = {
       after = [ "systemd-networkd.service" ];
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 1e0b874297a..a69435ff593 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -97,21 +97,22 @@ let
 
   addrOpts = v:
     assert v == 4 || v == 6;
-    {
-      address = mkOption {
-        type = types.str;
-        description = ''
-          IPv${toString v} address of the interface.  Leave empty to configure the
-          interface using DHCP.
-        '';
-      };
+    { options = {
+        address = mkOption {
+          type = types.str;
+          description = ''
+            IPv${toString v} address of the interface.  Leave empty to configure the
+            interface using DHCP.
+          '';
+        };
 
-      prefixLength = mkOption {
-        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
-        description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
-        '';
+        prefixLength = mkOption {
+          type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+          description = ''
+            Subnet mask of the interface, specified as the number of
+            bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+          '';
+        };
       };
     };
 
@@ -141,8 +142,7 @@ let
           { address = "10.0.0.1"; prefixLength = 16; }
           { address = "192.168.1.1"; prefixLength = 24; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 4;
+        type = with types; listOf (submodule (addrOpts 4));
         description = ''
           List of IPv4 addresses that will be statically assigned to the interface.
         '';
@@ -154,8 +154,7 @@ let
           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
           { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; }
         ];
-        type = types.listOf types.optionSet;
-        options = addrOpts 6;
+        type = with types; listOf (submodule (addrOpts 6));
         description = ''
           List of IPv6 addresses that will be statically assigned to the interface.
         '';
@@ -231,7 +230,7 @@ let
         type = types.bool;
         description = ''
           Whether this interface is virtual and should be created by tunctl.
-          This is mainly useful for creating bridges between a host a virtual
+          This is mainly useful for creating bridges between a host and a virtual
           network such as VPN or a virtual machine.
         '';
       };
@@ -246,7 +245,7 @@ let
 
       virtualType = mkOption {
         default = null;
-        type = types.nullOr (types.addCheck types.str (v: v == "tun" || v == "tap"));
+        type = with types; nullOr (enum [ "tun" "tap" ]);
         description = ''
           The explicit type of interface to create. Accepts tun or tap strings.
           Also accepts null to implicitly detect the type of device.
@@ -311,9 +310,9 @@ in
         generate a random 32-bit ID using the following commands:
 
         <literal>cksum /etc/machine-id | while read c rest; do printf "%x" $c; done</literal>
-        
+
         (this derives it from the machine-id that systemd generates) or
-        
+
         <literal>head -c4 /dev/urandom | od -A none -t x4</literal>
       '';
     };
@@ -391,7 +390,7 @@ in
     };
 
     networking.localCommands = mkOption {
-      type = types.str;
+      type = types.lines;
       default = "";
       example = "text=anything; echo You can put $text here.";
       description = ''
@@ -415,8 +414,7 @@ in
         <option>networking.useDHCP</option> is true, then every
         interface not listed here will be configured using DHCP.
       '';
-      type = types.loaOf types.optionSet;
-      options = [ interfaceOpts ];
+      type = with types; loaOf (submodule interfaceOpts);
     };
 
     networking.vswitches = mkOption {
@@ -434,53 +432,55 @@ in
           interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the vSwitch.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the vSwitch.";
+          };
 
-        controllers = mkOption {
-          type = types.listOf types.str;
-          default = [];
-          example = [ "ptcp:6653:[::1]" ];
-          description = ''
-            Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
-          '';
-        };
+          controllers = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = [ "ptcp:6653:[::1]" ];
+            description = ''
+              Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>.
+            '';
+          };
 
-        openFlowRules = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            actions=normal
-          '';
-          description = ''
-            OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
-            loaded with <literal>ovs-ofctl</literal> within one atomic operation.
-          '';
-        };
+          openFlowRules = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              actions=normal
+            '';
+            description = ''
+              OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are
+              loaded with <literal>ovs-ofctl</literal> within one atomic operation.
+            '';
+          };
+
+          extraOvsctlCmds = mkOption {
+            type = types.lines;
+            default = "";
+            example = ''
+              set-fail-mode <switch_name> secure
+              set Bridge <switch_name> stp_enable=true
+            '';
+            description = ''
+              Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
+              All commands are bundled together with the operations for adding the interfaces
+              into one atomic operation.
+            '';
+          };
 
-        extraOvsctlCmds = mkOption {
-          type = types.lines;
-          default = "";
-          example = ''
-            set-fail-mode <switch_name> secure
-            set Bridge <switch_name> stp_enable=true
-          '';
-          description = ''
-            Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>.
-            All commands are bundled together with the operations for adding the interfaces
-            into one atomic operation.
-          '';
         };
 
-      };
+      });
 
     };
 
@@ -499,25 +499,27 @@ in
           bridge's network interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        interfaces = mkOption {
-          example = [ "eth0" "eth1" ];
-          type = types.listOf types.str;
-          description =
-            "The physical network interfaces connected by the bridge.";
-        };
+          interfaces = mkOption {
+            example = [ "eth0" "eth1" ];
+            type = types.listOf types.str;
+            description =
+              "The physical network interfaces connected by the bridge.";
+          };
+
+          rstp = mkOption {
+            example = true;
+            default = false;
+            type = types.bool;
+            description = "Whether the bridge interface should enable rstp.";
+          };
 
-        rstp = mkOption {
-          example = true;
-          default = false;
-          type = types.bool;
-          description = "Whether the bridge interface should enable rstp.";
         };
 
-      };
+      });
 
     };
 
@@ -538,65 +540,66 @@ in
         name specifying the name of the bond's network interface
       '';
 
-      type = types.attrsOf types.optionSet;
-
-      options = {
-
-        interfaces = mkOption {
-          example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
-          type = types.listOf types.str;
-          description = "The interfaces to bond together";
-        };
+      type = with types; attrsOf (submodule {
 
-        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.
-          '';
-        };
+        options = {
 
-        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.
-          '';
-        };
+          interfaces = mkOption {
+            example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
+            type = types.listOf types.str;
+            description = "The interfaces to bond together";
+          };
 
-        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
-          '';
-        };
+          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.
+            '';
+          };
+
+          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
+            '';
+          };
+
+          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.
+            '';
+          };
 
-        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 {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         wan = {
@@ -608,26 +611,28 @@ in
         This option allows you to define macvlan interfaces which should
         be automatically created.
       '';
-      options = {
-
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the macvlan will transmit packets through.";
-        };
+      type = with types; attrsOf (submodule {
+        options = {
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the macvlan will transmit packets through.";
+          };
+
+          mode = mkOption {
+            default = null;
+            type = types.nullOr types.str;
+            example = "vepa";
+            description = "The mode of the macvlan device.";
+          };
 
-        mode = mkOption {
-          default = null;
-          type = types.nullOr types.str;
-          example = "vepa";
-          description = "The mode of the macvlan device.";
         };
 
-      };
+      });
     };
 
     networking.sits = mkOption {
-      type = types.attrsOf types.optionSet;
       default = { };
       example = literalExample {
         hurricane = {
@@ -644,46 +649,49 @@ in
       description = ''
         This option allows you to define 6-to-4 interfaces which should be automatically created.
       '';
-      options = {
-
-        remote = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.1";
-          description = ''
-            The address of the remote endpoint to forward traffic over.
-          '';
-        };
-
-        local = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "10.0.0.22";
-          description = ''
-            The address of the local endpoint which the remote
-            side should send packets to.
-          '';
-        };
-
-        ttl = mkOption {
-          type = types.nullOr types.int;
-          default = null;
-          example = 255;
-          description = ''
-            The time-to-live of the connection to the remote tunnel endpoint.
-          '';
-        };
+      type = with types; attrsOf (submodule {
+        options = {
+
+          remote = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.1";
+            description = ''
+              The address of the remote endpoint to forward traffic over.
+            '';
+          };
+
+          local = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "10.0.0.22";
+            description = ''
+              The address of the local endpoint which the remote
+              side should send packets to.
+            '';
+          };
+
+          ttl = mkOption {
+            type = types.nullOr types.int;
+            default = null;
+            example = 255;
+            description = ''
+              The time-to-live of the connection to the remote tunnel endpoint.
+            '';
+          };
+
+          dev = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "enp4s0f0";
+            description = ''
+              The underlying network device on which the tunnel resides.
+            '';
+          };
 
-        dev = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "enp4s0f0";
-          description = ''
-            The underlying network device on which the tunnel resides.
-          '';
         };
 
-      };
+      });
     };
 
     networking.vlans = mkOption {
@@ -706,23 +714,26 @@ in
           specifying the name of the vlan interface.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
+        options = {
 
-        id = mkOption {
-          example = 1;
-          type = types.int;
-          description = "The vlan identifier";
-        };
+          id = mkOption {
+            example = 1;
+            type = types.int;
+            description = "The vlan identifier";
+          };
+
+          interface = mkOption {
+            example = "enp4s0";
+            type = types.str;
+            description = "The interface the vlan will transmit packets through.";
+          };
 
-        interface = mkOption {
-          example = "enp4s0";
-          type = types.str;
-          description = "The interface the vlan will transmit packets through.";
         };
 
-      };
+      });
+
     };
 
     networking.wlanInterfaces = mkOption {
@@ -760,73 +771,69 @@ in
           would have to be created explicitly.
         '';
 
-      type = types.attrsOf types.optionSet;
+      type = with types; attrsOf (submodule {
 
-      options = {
-
-        device = mkOption {
-          type = types.string;
-          example = "wlp6s0";
-          description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
-        };
-
-        type = mkOption {
-          type = types.string;
-          default = "managed";
-          example = "ibss";
-          description = ''
-            The type of the WLAN interface. The type has to be either <literal>managed</literal>,
-            <literal>ibss</literal>, <literal>monitor</literal>, <literal>mesh</literal> or <literal>wds</literal>.
-            Also, the type has to be supported by the underlying hardware of the device.
-          '';
-        };
+        options = {
 
-        meshID = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          description = "MeshID of interface with type <literal>mesh</literal>.";
-        };
+          device = mkOption {
+            type = types.string;
+            example = "wlp6s0";
+            description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>.";
+          };
 
-        flags = mkOption {
-          type = types.nullOr types.string;
-          default = null;
-          example = "control";
-          description = ''
-            Flags for interface of type <literal>monitor</literal>. The valid flags are:
-            none:     no special flags
-            fcsfail:  show frames with FCS errors
-            control:  show control frames
-            otherbss: show frames from other BSSes
-            cook:     use cooked mode
-            active:   use active mode (ACK incoming unicast packets)
-          '';
-        };
+          type = mkOption {
+            type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ];
+            default = "managed";
+            example = "ibss";
+            description = ''
+              The type of the WLAN interface.
+              The type has to be supported by the underlying hardware of the device.
+            '';
+          };
+
+          meshID = mkOption {
+            type = types.nullOr types.string;
+            default = null;
+            description = "MeshID of interface with type <literal>mesh</literal>.";
+          };
+
+          flags = mkOption {
+            type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]);
+            default = null;
+            example = "control";
+            description = ''
+              Flags for interface of type <literal>monitor</literal>.
+            '';
+          };
+
+          fourAddr = mkOption {
+            type = types.nullOr types.bool;
+            default = null;
+            description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
+          };
+
+          mac = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            example = "02:00:00:00:00:01";
+            description = ''
+              MAC address to use for the device. If <literal>null</literal>, then the MAC of the
+              underlying hardware WLAN device is used.
+
+              INFO: Locally administered MAC addresses are of the form:
+              <itemizedlist>
+              <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
+              <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
+              </itemizedlist>
+            '';
+          };
 
-        fourAddr = mkOption {
-          type = types.nullOr types.bool;
-          default = null;
-          description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>.";
         };
 
-        mac = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "02:00:00:00:00:01";
-          description = ''
-            MAC address to use for the device. If <literal>null</literal>, then the MAC of the
-            underlying hardware WLAN device is used.
-
-            INFO: Locally administered MAC addresses are of the form:
-            <itemizedlist>
-            <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem>
-            <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem>
-            </itemizedlist>
-          '';
-        };
+      });
 
-      };
     };
 
     networking.useDHCP = mkOption {
@@ -938,20 +945,23 @@ in
         domainname "${cfg.domain}"
       '';
 
-    environment.etc = mkIf (cfg.hostId != null)
-      [
-        {
-          target = "hostid";
-          source = pkgs.runCommand "gen-hostid" {} ''
-            hi="${cfg.hostId}"
-            ${if pkgs.stdenv.isBigEndian then ''
-              echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out
-            '' else ''
-              echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out
-            ''}
-          '';
-        }
-      ];
+    environment.etc."hostid" = mkIf (cfg.hostId != null)
+      { source = pkgs.runCommand "gen-hostid" {} ''
+          hi="${cfg.hostId}"
+          ${if pkgs.stdenv.isBigEndian then ''
+            echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out
+          '' else ''
+            echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out
+          ''}
+        '';
+      };
+
+    # static hostname configuration needed for hostnamectl and the
+    # org.freedesktop.hostname1 dbus service (both provided by systemd)
+    environment.etc."hostname" = mkIf (cfg.hostName != "")
+      {
+        text = cfg.hostName + "\n";
+      };
 
     environment.systemPackages =
       [ pkgs.host
@@ -967,8 +977,10 @@ in
       ]
       ++ bridgeStp;
 
+    # The network-interfaces target is kept for backwards compatibility.
+    # New modules must NOT use it.
     systemd.targets."network-interfaces" =
-      { description = "All Network Interfaces";
+      { description = "All Network Interfaces (deprecated)";
         wantedBy = [ "network.target" ];
         before = [ "network.target" ];
         after = [ "network-pre.target" ];
@@ -991,12 +1003,17 @@ in
         '';
       };
     } // (listToAttrs (flip map interfaces (i:
+      let
+        deviceDependency = if config.boot.isContainer
+          then []
+          else [ (subsystemDevice i.name) ];
+      in
       nameValuePair "network-link-${i.name}"
       { description = "Link configuration of ${i.name}";
         wantedBy = [ "network-interfaces.target" ];
         before = [ "network-interfaces.target" ];
-        bindsTo = [ (subsystemDevice i.name) ];
-        after = [ (subsystemDevice i.name) "network-pre.target" ];
+        bindsTo = deviceDependency;
+        after = [ "network-pre.target" ] ++ deviceDependency;
         path = [ pkgs.iproute ];
         serviceConfig = {
           Type = "oneshot";
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index e216351b434..099ead3d846 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -9,6 +9,15 @@ let kernel = config.boot.kernelPackages.kernel; in
 
 {
 
+  # This option is a dummy that if used in conjunction with
+  # modules/virtualisation/qemu-vm.nix gets merged with the same option defined
+  # there and only is declared here because some modules use
+  # test-instrumentation.nix but not qemu-vm.nix.
+  #
+  # One particular example are the boot tests where we want instrumentation
+  # within the images but not other stuff like setting up 9p filesystems.
+  options.virtualisation.qemu.program = mkOption { type = types.path; };
+
   config = {
 
     systemd.services.backdoor =
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index f9c3f2e53ad..80e481d79b9 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -94,7 +94,6 @@ let cfg = config.ec2; in
             elif [ "$fsType" = ext3 ]; then
                 mp="/disk$diskNr"
                 diskNr=$((diskNr + 1))
-                echo "mounting $device on $mp..."
                 if mountFS "$device" "$mp" "" ext3; then
                     if [ -z "$diskForUnionfs" ]; then diskForUnionfs="$mp"; fi
                 fi
@@ -138,7 +137,7 @@ let cfg = config.ec2; in
     # Allow root logins only using the SSH key that the user specified
     # at instance creation time.
     services.openssh.enable = true;
-    services.openssh.permitRootLogin = "without-password";
+    services.openssh.permitRootLogin = "prohibit-password";
 
     # Force getting the hostname from EC2.
     networking.hostName = mkDefault "";
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix
index a89cd454dc7..6817eb837a0 100644
--- a/nixos/modules/virtualisation/azure-agent.nix
+++ b/nixos/modules/virtualisation/azure-agent.nix
@@ -178,7 +178,8 @@ in
 
     systemd.services.waagent = {
       wantedBy = [ "multi-user.target" ];
-      after = [ "ip-up.target" "sshd.service" ];
+      after = [ "network-online.target" "sshd.service" ];
+      wants = [ "network-online.target" ];
 
       path = [ pkgs.e2fsprogs ];
       description = "Windows Azure Agent Service";
diff --git a/nixos/modules/virtualisation/azure-bootstrap-blobs.nix b/nixos/modules/virtualisation/azure-bootstrap-blobs.nix
new file mode 100644
index 00000000000..281be9a1231
--- /dev/null
+++ b/nixos/modules/virtualisation/azure-bootstrap-blobs.nix
@@ -0,0 +1,3 @@
+{
+    "16.03" = "https://nixos.blob.core.windows.net/images/nixos-image-16.03.847.8688c17-x86_64-linux.vhd";
+}
diff --git a/nixos/modules/virtualisation/azure-common.nix b/nixos/modules/virtualisation/azure-common.nix
index 70a3d752f6d..5cd2304a295 100644
--- a/nixos/modules/virtualisation/azure-common.nix
+++ b/nixos/modules/virtualisation/azure-common.nix
@@ -24,7 +24,7 @@ with lib;
   # Allow root logins only using the SSH key that the user specified
   # at instance creation time, ping client connections to avoid timeouts
   services.openssh.enable = true;
-  services.openssh.permitRootLogin = "without-password";
+  services.openssh.permitRootLogin = "prohibit-password";
   services.openssh.extraConfig = ''
     ClientAliveInterval 180
   '';
diff --git a/nixos/modules/virtualisation/brightbox-image.nix b/nixos/modules/virtualisation/brightbox-image.nix
index 760a7100c6e..7f45f0f34f7 100644
--- a/nixos/modules/virtualisation/brightbox-image.nix
+++ b/nixos/modules/virtualisation/brightbox-image.nix
@@ -20,7 +20,7 @@ in
 
           postVM =
             ''
-              PATH=$PATH:${stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
+              PATH=$PATH:${lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
               pushd $out
               ${pkgs.qemu_kvm}/bin/qemu-img convert -c -O qcow2 $diskImageBase nixos.qcow2
               rm $diskImageBase
@@ -103,7 +103,7 @@ in
   # Allow root logins only using the SSH key that the user specified
   # at instance creation time.
   services.openssh.enable = true;
-  services.openssh.permitRootLogin = "without-password";
+  services.openssh.permitRootLogin = "prohibit-password";
 
   # Force getting the hostname from Google Compute.
   networking.hostName = mkDefault "";
@@ -116,8 +116,8 @@ in
 
       wantedBy = [ "multi-user.target" "sshd.service" ];
       before = [ "sshd.service" ];
-      wants = [ "ip-up.target" ];
-      after = [ "ip-up.target" ];
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
 
       path = [ pkgs.wget pkgs.iproute ];
 
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 413aa94339f..7d445fa0951 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -12,21 +12,21 @@ let
         ''
         echo "Bringing ${name} up"
         ip link set dev ${name} up
-        ${optionalString (cfg . "localAddress" or null != null) ''
+        ${optionalString (cfg.localAddress != null) ''
           echo "Setting ip for ${name}"
-          ip addr add ${cfg . "localAddress"} dev ${name}
+          ip addr add ${cfg.localAddress} dev ${name}
         ''}
-        ${optionalString (cfg . "localAddress6" or null != null) ''
+        ${optionalString (cfg.localAddress6 != null) ''
           echo "Setting ip6 for ${name}"
-          ip -6 addr add ${cfg . "localAddress6"} dev ${name}
+          ip -6 addr add ${cfg.localAddress6} dev ${name}
         ''}
-        ${optionalString (cfg . "hostAddress" or null != null) ''
+        ${optionalString (cfg.hostAddress != null) ''
           echo "Setting route to host for ${name}"
-          ip route add ${cfg . "hostAddress"} dev ${name}
+          ip route add ${cfg.hostAddress} dev ${name}
         ''}
-        ${optionalString (cfg . "hostAddress6" or null != null) ''
+        ${optionalString (cfg.hostAddress6 != null) ''
           echo "Setting route6 to host for ${name}"
-          ip -6 route add ${cfg . "hostAddress6"} dev ${name}
+          ip -6 route add ${cfg.hostAddress6} dev ${name}
         ''}
         ''
         );
@@ -56,9 +56,7 @@ let
             ip -6 route add default via $HOST_ADDRESS6
           fi
 
-          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
-          ip a
-          ip r
+          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)}
         fi
 
         # Start the regular stage 1 script.
@@ -67,7 +65,8 @@ let
     );
 
   nspawnExtraVethArgs = (name: cfg: "--network-veth-extra=${name}");
-  startScript = (cfg:
+
+  startScript = cfg:
     ''
       mkdir -p -m 0755 "$root/etc" "$root/var/lib"
       mkdir -p -m 0700 "$root/var/lib/private" "$root/root" /run/containers
@@ -92,11 +91,7 @@ let
         fi
       fi
 
-      ${if cfg . "extraVeths" or null != null then
-        ''extraFlags+=" ${concatStringsSep " " (mapAttrsToList nspawnExtraVethArgs cfg . "extraVeths" or {})}"''
-        else
-          ''# No extra veth pairs to create''
-      }
+      extraFlags+=" ${concatStringsSep " " (mapAttrsToList nspawnExtraVethArgs cfg.extraVeths)}"
 
       for iface in $INTERFACES; do
         extraFlags+=" --network-interface=$iface"
@@ -134,11 +129,16 @@ let
         --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
         --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
         --setenv PATH="$PATH" \
+        ${if cfg.additionalCapabilities != null && cfg.additionalCapabilities != [] then
+          ''--capability="${concatStringsSep " " cfg.additionalCapabilities}"'' else ""
+        } \
+        ${if cfg.tmpfs != null && cfg.tmpfs != [] then
+          ''--tmpfs=${concatStringsSep " --tmpfs=" cfg.tmpfs}'' else ""
+        } \
         ${containerInit cfg} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
-    ''
-    );
+    '';
 
-  preStartScript = (cfg:
+  preStartScript = cfg:
     ''
       # Clean up existing machined registration and interfaces.
       machinectl terminate "$INSTANCE" 2> /dev/null || true
@@ -151,45 +151,43 @@ let
       ${concatStringsSep "\n" (
         mapAttrsToList (name: cfg:
           ''ip link del dev ${name} 2> /dev/null || true ''
-        ) cfg . "extraVeths" or {}
+        ) cfg.extraVeths
       )}
-   ''
-    );
+   '';
+
   postStartScript = (cfg:
     let
-      ipcall = (cfg: ipcmd: variable: attribute:
-        if cfg . attribute or null == null then
+      ipcall = cfg: ipcmd: variable: attribute:
+        if cfg.${attribute} == null then
           ''
             if [ -n "${variable}" ]; then
               ${ipcmd} add ${variable} dev $ifaceHost
             fi
           ''
         else
-          ''${ipcmd} add ${cfg . attribute} dev $ifaceHost''
-        );
-      renderExtraVeth = (name: cfg:
-        if cfg . "hostBridge" or null != null then
+          ''${ipcmd} add ${cfg.${attribute}} dev $ifaceHost'';
+      renderExtraVeth = name: cfg:
+        if cfg.hostBridge != null then
           ''
             # Add ${name} to bridge ${cfg.hostBridge}
             ip link set dev ${name} master ${cfg.hostBridge} up
           ''
         else
           ''
-          # Set IPs and routes for ${name}
-          ${optionalString (cfg . "hostAddress" or null != null) ''
-            ip addr add ${cfg . "hostAddress"} dev ${name}
-          ''}
-          ${optionalString (cfg . "hostAddress6" or null != null) ''
-            ip -6 addr add ${cfg . "hostAddress6"} dev ${name}
-          ''}
-          ${optionalString (cfg . "localAddress" or null != null) ''
-            ip route add ${cfg . "localAddress"} dev ${name}
-          ''}
-          ${optionalString (cfg . "localAddress6" or null != null) ''
-            ip -6 route add ${cfg . "localAddress6"} dev ${name}
-          ''}
-          ''
-        );
+            # Set IPs and routes for ${name}
+            ${optionalString (cfg.hostAddress != null) ''
+              ip addr add ${cfg.hostAddress} dev ${name}
+            ''}
+            ${optionalString (cfg.hostAddress6 != null) ''
+              ip -6 addr add ${cfg.hostAddress6} dev ${name}
+            ''}
+            ${optionalString (cfg.localAddress != null) ''
+              ip route add ${cfg.localAddress} dev ${name}
+            ''}
+            ${optionalString (cfg.localAddress6 != null) ''
+              ip -6 route add ${cfg.localAddress6} dev ${name}
+            ''}
+          '';
     in
       ''
         if [ "$PRIVATE_NETWORK" = 1 ]; then
@@ -202,7 +200,7 @@ let
             ${ipcall cfg "ip route" "$LOCAL_ADDRESS" "localAddress"}
             ${ipcall cfg "ip -6 route" "$LOCAL_ADDRESS6" "localAddress6"}
           fi
-          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg . "extraVeths" or {})}
+          ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)}
         fi
 
         # Get the leader PID so that we can signal it in
@@ -213,6 +211,41 @@ let
       ''
   );
 
+  serviceDirectives = cfg: {
+    ExecReload = pkgs.writeScript "reload-container"
+      ''
+        #! ${pkgs.stdenv.shell} -e
+        ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \
+          bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
+      '';
+
+    SyslogIdentifier = "container %i";
+
+    EnvironmentFile = "-/etc/containers/%i.conf";
+
+    Type = "notify";
+
+    # Note that on reboot, systemd-nspawn returns 133, so this
+    # unit will be restarted. On poweroff, it returns 0, so the
+    # unit won't be restarted.
+    RestartForceExitStatus = "133";
+    SuccessExitStatus = "133";
+
+    Restart = "on-failure";
+
+    # Hack: we don't want to kill systemd-nspawn, since we call
+    # "machinectl poweroff" in preStop to shut down the
+    # container cleanly. But systemd requires sending a signal
+    # (at least if we want remaining processes to be killed
+    # after the timeout). So send an ignored signal.
+    KillMode = "mixed";
+    KillSignal = "WINCH";
+
+    DevicePolicy = "closed";
+    DeviceAllow = map (d: "${d.node} ${d.modifier}") cfg.allowedDevices;
+  };
+
+
   system = config.nixpkgs.system;
 
   bindMountOpts = { name, config, ... }: {
@@ -243,6 +276,27 @@ let
 
   };
 
+  allowedDeviceOpts = { name, config, ... }: {
+    options = {
+      node = mkOption {
+        example = "/dev/net/tun";
+        type = types.str;
+        description = "Path to device node";
+      };
+      modifier = mkOption {
+        example = "rw";
+        type = types.str;
+        description = ''
+          Device node access modifier. Takes a combination
+          <literal>r</literal> (read), <literal>w</literal> (write), and
+          <literal>m</literal> (mknod). See the
+          <literal>systemd.resource-control(5)</literal> man page for more
+          information.'';
+      };
+    };
+  };
+
+
   mkBindFlag = d:
                let flagPrefix = if d.isReadOnly then " --bind-ro=" else " --bind=";
                    mountstr = if d.hostPath != null then "${d.hostPath}:${d.mountPoint}" else "${d.mountPoint}";
@@ -307,6 +361,18 @@ let
 
   };
 
+  dummyConfig =
+    {
+      extraVeths = {};
+      additionalCapabilities = [];
+      allowedDevices = [];
+      hostAddress = null;
+      hostAddress6 = null;
+      localAddress = null;
+      localAddress6 = null;
+      tmpfs = null;
+    };
+
 in
 
 {
@@ -367,6 +433,26 @@ in
               '';
             };
 
+            additionalCapabilities = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "CAP_NET_ADMIN" "CAP_MKNOD" ];
+              description = ''
+                Grant additional capabilities to the container.  See the
+                capabilities(7) and systemd-nspawn(1) man pages for more
+                information.
+              '';
+            };
+            enableTun = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Allows the container to create and setup tunnel interfaces
+                by granting the <literal>NET_ADMIN</literal> capability and
+                enabling access to <literal>/dev/net/tun</literal>.
+              '';
+            };
+
             privateNetwork = mkOption {
               type = types.bool;
               default = false;
@@ -390,10 +476,20 @@ in
               '';
             };
 
+            macvlans = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "eth1" "eth2" ];
+              description = ''
+                The list of host interfaces from which macvlans will be
+                created. For each interface specified, a macvlan interface
+                will be created and moved to the container.
+              '';
+            };
+
             extraVeths = mkOption {
-              type = types.attrsOf types.optionSet;
+              type = with types; attrsOf (submodule { options = networkOptions; });
               default = {};
-              options = networkOptions;
               description = ''
                 Extra veth-pairs to be created for the container
               '';
@@ -408,8 +504,7 @@ in
             };
 
             bindMounts = mkOption {
-              type = types.loaOf types.optionSet;
-              options = [ bindMountOpts ];
+              type = with types; loaOf (submodule bindMountOpts);
               default = {};
               example = { "/home" = { hostPath = "/home/alice";
                                       isReadOnly = false; };
@@ -421,6 +516,27 @@ in
                 '';
             };
 
+            allowedDevices = mkOption {
+              type = with types; listOf (submodule allowedDeviceOpts);
+              default = [];
+              example = [ { node = "/dev/net/tun"; modifier = "rw"; } ];
+              description = ''
+                A list of device nodes to which the containers has access to.
+              '';
+            };
+
+            tmpfs = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "/var" ];
+              description = ''
+                Mounts a set of tmpfs file systems into the container.
+                Multiple paths can be specified.
+                Valid items must conform to the --tmpfs argument
+                of systemd-nspawn. See systemd-nspawn(1) for details.
+              '';
+            };
+
           } // networkOptions;
 
           config = mkMerge
@@ -451,7 +567,7 @@ in
         containers.  Each container appears as a service
         <literal>container-<replaceable>name</replaceable></literal>
         on the host system, allowing it to be started and stopped via
-        <command>systemctl</command> .
+        <command>systemctl</command>.
       '';
     };
 
@@ -470,11 +586,11 @@ in
       environment.INSTANCE = "%i";
       environment.root = "/var/lib/containers/%i";
 
-      preStart = preStartScript {};
+      preStart = preStartScript dummyConfig;
 
-      script = startScript {};
+      script = startScript dummyConfig;
 
-      postStart = postStartScript {};
+      postStart = postStartScript dummyConfig;
 
       preStop =
         ''
@@ -487,59 +603,39 @@ in
 
       restartIfChanged = false;
 
-      serviceConfig = {
-        ExecReload = pkgs.writeScript "reload-container"
-          ''
-            #! ${pkgs.stdenv.shell} -e
-            ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \
-              bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
-          '';
-
-        SyslogIdentifier = "container %i";
-
-        EnvironmentFile = "-/etc/containers/%i.conf";
-
-        Type = "notify";
-
-        # Note that on reboot, systemd-nspawn returns 133, so this
-        # unit will be restarted. On poweroff, it returns 0, so the
-        # unit won't be restarted.
-        RestartForceExitStatus = "133";
-        SuccessExitStatus = "133";
-
-        Restart = "on-failure";
-
-        # Hack: we don't want to kill systemd-nspawn, since we call
-        # "machinectl poweroff" in preStop to shut down the
-        # container cleanly. But systemd requires sending a signal
-        # (at least if we want remaining processes to be killed
-        # after the timeout). So send an ignored signal.
-        KillMode = "mixed";
-        KillSignal = "WINCH";
-
-        DevicePolicy = "closed";
-      };
+      serviceConfig = serviceDirectives dummyConfig;
     };
   in {
     systemd.services = listToAttrs (filter (x: x.value != null) (
       # The generic container template used by imperative containers
       [{ name = "container@"; value = unit; }]
       # declarative containers
-      ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (
-        unit // {
-          preStart = preStartScript cfg;
-          script = startScript cfg;
-          postStart = postStartScript cfg;
-        } // (
-        if cfg.autoStart then
-          {
-            wantedBy = [ "multi-user.target" ];
-            wants = [ "network.target" ];
-            after = [ "network.target" ];
-            restartTriggers = [ cfg.path ];
-            reloadIfChanged = true;
-          }
-        else {})
+      ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (let
+          config = cfg // (
+          if cfg.enableTun then
+            {
+              allowedDevices = cfg.allowedDevices
+                ++ [ { node = "/dev/net/tun"; modifier = "rw"; } ];
+              additionalCapabilities = cfg.additionalCapabilities
+                ++ [ "CAP_NET_ADMIN" ];
+            }
+          else {});
+        in
+          unit // {
+            preStart = preStartScript config;
+            script = startScript config;
+            postStart = postStartScript config;
+            serviceConfig = serviceDirectives config;
+          } // (
+          if config.autoStart then
+            {
+              wantedBy = [ "multi-user.target" ];
+              wants = [ "network.target" ];
+              after = [ "network.target" ];
+              restartTriggers = [ config.path ];
+              reloadIfChanged = true;
+            }
+          else {})
       )) config.containers)
     ));
 
@@ -569,6 +665,7 @@ in
               ''}
             ''}
             INTERFACES="${toString cfg.interfaces}"
+            MACVLANS="${toString cfg.macvlans}"
             ${optionalString cfg.autoStart ''
               AUTO_START=1
             ''}
@@ -579,10 +676,10 @@ in
     # Generate /etc/hosts entries for the containers.
     networking.extraHosts = concatStrings (mapAttrsToList (name: cfg: optionalString (cfg.localAddress != null)
       ''
-        ${cfg.localAddress} ${name}.containers
+        ${head (splitString "/" cfg.localAddress)} ${name}.containers
       '') config.containers);
 
-    networking.dhcpcd.denyInterfaces = [ "ve-*" ];
+    networking.dhcpcd.denyInterfaces = [ "ve-*" "vb-*" ];
 
     environment.systemPackages = [ pkgs.nixos-container ];
   });
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index ebc2be087a5..4b30a38f832 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -28,25 +28,63 @@ in
             <command>docker</command> command line tool.
           '';
       };
-    socketActivation =
+
+    listenOptions =
+      mkOption {
+        type = types.listOf types.str;
+        default = ["/var/run/docker.sock"];
+        description =
+          ''
+            A list of unix and tcp docker should listen to. The format follows
+            ListenStream as described in systemd.socket(5).
+          '';
+      };
+
+    enableOnBoot =
       mkOption {
         type = types.bool;
         default = true;
         description =
           ''
-            This option enables docker with socket activation. I.e. docker will
-            start when first called by client.
+            When enabled dockerd is started on boot. This is required for
+            container, which are created with the
+            <literal>--restart=always</literal> flag, to work. If this option is
+            disabled, docker might be started on demand by socket activation.
           '';
       };
+
+    liveRestore =
+      mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            Allow dockerd to be restarted without affecting running container.
+            This option is incompatible with docker swarm.
+          '';
+      };
+
     storageDriver =
       mkOption {
-        type = types.enum ["aufs" "btrfs" "devicemapper" "overlay" "zfs"];
-        default = "devicemapper";
+        type = types.nullOr (types.enum ["aufs" "btrfs" "devicemapper" "overlay" "overlay2" "zfs"]);
+        default = null;
         description =
           ''
-            This option determines which Docker storage driver to use.
+            This option determines which Docker storage driver to use. By default
+            it let's docker automatically choose preferred storage driver.
           '';
       };
+
+    logDriver =
+      mkOption {
+        type = types.enum ["none" "json-file" "syslog" "journald" "gelf" "fluentd" "awslogs" "splunk" "etwlogs" "gcplogs"];
+        default = "journald";
+        description =
+          ''
+            This option determines which Docker log driver to use.
+          '';
+      };
+
     extraOptions =
       mkOption {
         type = types.separatedString " ";
@@ -57,64 +95,43 @@ in
             <command>docker</command> daemon.
           '';
       };
-
-    postStart =
-      mkOption {
-        type = types.lines;
-        default = ''
-          while ! [ -e /var/run/docker.sock ]; do
-            sleep 0.1
-          done
-        '';
-        description = ''
-          The postStart phase of the systemd service. You may need to
-          override this if you are passing in flags to docker which
-          don't cause the socket file to be created. This option is ignored
-          if socket activation is used.
-        '';
-      };
-
-
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable (mkMerge [
-    { environment.systemPackages = [ pkgs.docker ];
+  config = mkIf cfg.enable (mkMerge [{
+      environment.systemPackages = [ pkgs.docker ];
       users.extraGroups.docker.gid = config.ids.gids.docker;
+      systemd.packages = [ pkgs.docker ];
+
       systemd.services.docker = {
-        description = "Docker Application Container Engine";
-        wantedBy = optional (!cfg.socketActivation) "multi-user.target";
-        after = [ "network.target" ] ++ (optional cfg.socketActivation "docker.socket") ;
-        requires = optional cfg.socketActivation "docker.socket";
+        wantedBy = optional cfg.enableOnBoot "multi-user.target";
         serviceConfig = {
-          ExecStart = "${pkgs.docker}/bin/docker daemon --group=docker --storage-driver=${cfg.storageDriver} ${optionalString cfg.socketActivation "--host=fd://"} ${cfg.extraOptions}";
-          #  I'm not sure if that limits aren't too high, but it's what
-          #  goes in config bundled with docker itself
-          LimitNOFILE = 1048576;
-          LimitNPROC = 1048576;
+          ExecStart = [
+            ""
+            ''
+              ${pkgs.docker}/bin/dockerd \
+                --group=docker \
+                --host=fd:// \
+                --log-driver=${cfg.logDriver} \
+                ${optionalString (cfg.storageDriver != null) "--storage-driver=${cfg.storageDriver}"} \
+                ${optionalString cfg.liveRestore "--live-restore" } \
+                ${cfg.extraOptions}
+            ''];
+          ExecReload=[
+            ""
+            "${pkgs.procps}/bin/kill -s HUP $MAINPID"
+          ];
         } // proxy_env;
 
         path = [ pkgs.kmod ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs);
-
-        postStart = if cfg.socketActivation then "" else cfg.postStart;
-
-        # Presumably some containers are running we don't want to interrupt
-        restartIfChanged = false;
       };
+      systemd.sockets.docker.socketConfig.ListenStream = cfg.listenOptions;
     }
-    (mkIf cfg.socketActivation {
-      systemd.sockets.docker = {
-        description = "Docker Socket for the API";
-        wantedBy = [ "sockets.target" ];
-        socketConfig = {
-          ListenStream = "/var/run/docker.sock";
-          SocketMode = "0660";
-          SocketUser = "root";
-          SocketGroup = "docker";
-        };
-      };
-    })
   ]);
 
+  imports = [
+    (mkRemovedOptionModule ["virtualisation" "docker" "socketActivation"] "This option was removed in favor of starting docker at boot")
+  ];
+
 }
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index bdf6ed4dcd2..ffd1cbec3ce 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -1,4 +1,4 @@
-{
+let self = {
   "14.04".ap-northeast-1.hvm-ebs = "ami-71c6f470";
   "14.04".ap-northeast-1.pv-ebs = "ami-4dcbf84c";
   "14.04".ap-northeast-1.pv-s3 = "ami-8fc4f68e";
@@ -134,4 +134,52 @@
   "16.03".us-west-2.hvm-s3 = "ami-925c9ff2";
   "16.03".us-west-2.pv-ebs = "ami-5e61a23e";
   "16.03".us-west-2.pv-s3 = "ami-734c8f13";
-}
+
+  # 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";
+
+  latest = self."16.09";
+}; in self
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 59b77dde5d9..0e6825c3f5d 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -111,7 +111,7 @@ in
   # Allow root logins only using the SSH key that the user specified
   # at instance creation time.
   services.openssh.enable = true;
-  services.openssh.permitRootLogin = "without-password";
+  services.openssh.permitRootLogin = "prohibit-password";
   services.openssh.passwordAuthentication = mkDefault false;
 
   # Force getting the hostname from Google Compute.
@@ -125,7 +125,7 @@ in
     169.254.169.254 metadata.google.internal metadata
   '';
 
-  services.ntp.servers = [ "metadata.google.internal" ];
+  networking.timeServers = [ "metadata.google.internal" ];
 
   networking.usePredictableInterfaceNames = false;
 
@@ -134,8 +134,8 @@ in
 
       wantedBy = [ "sshd.service" ];
       before = [ "sshd.service" ];
-      after = [ "network-online.target" "ip-up.target" ];
-      wants = [ "network-online.target" "ip-up.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
 
       script = let wget = "${pkgs.wget}/bin/wget --retry-connrefused -t 15 --waitretry=10 --header='Metadata-Flavor: Google'";
                    mktemp = "mktemp --tmpdir=/run"; in
diff --git a/nixos/modules/virtualisation/grow-partition.nix b/nixos/modules/virtualisation/grow-partition.nix
index abc2e766959..5039118d78e 100644
--- a/nixos/modules/virtualisation/grow-partition.nix
+++ b/nixos/modules/virtualisation/grow-partition.nix
@@ -24,7 +24,7 @@ with lib;
       copy_bin_and_libs ${pkgs.gnused}/bin/sed
       copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
       copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
-      cp -v ${pkgs.cloud-utils}/bin/growpart $out/bin/growpart
+      cp -v ${pkgs.cloud-utils}/bin/.growpart-wrapped $out/bin/growpart
       ln -s sed $out/bin/gnused
     '';
 
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index ea503a9526f..5f669dee754 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -123,7 +123,7 @@ in {
         # config file. But this path can unfortunately be garbage collected
         # while still being used by the virtual machine. So update the
         # emulator path on each startup to something valid (re-scan $PATH).
-        for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do
+        for file in /var/lib/libvirt/qemu/*.xml /var/lib/libvirt/lxc/*.xml; do
             test -f "$file" || continue
             # get (old) emulator path from config file
             emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,')
diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
index 22da012e414..6759ff0f2fe 100644
--- a/nixos/modules/virtualisation/lxc.nix
+++ b/nixos/modules/virtualisation/lxc.nix
@@ -62,19 +62,17 @@ in
             </citerefentry>.
           '';
       };
-
   };
 
   ###### implementation
 
   config = mkIf cfg.enable {
-
     environment.systemPackages = [ pkgs.lxc ];
-
     environment.etc."lxc/lxc.conf".text = cfg.systemConfig;
     environment.etc."lxc/lxc-usernet".text = cfg.usernetConfig;
     environment.etc."lxc/default.conf".text = cfg.defaultConfig;
 
+    security.apparmor.packages = [ pkgs.lxc ];
+    security.apparmor.profiles = [ "${pkgs.lxc}/etc/apparmor.d/lxc-containers" ];
   };
-
 }
diff --git a/nixos/modules/virtualisation/lxcfs.nix b/nixos/modules/virtualisation/lxcfs.nix
new file mode 100644
index 00000000000..48462dc66da
--- /dev/null
+++ b/nixos/modules/virtualisation/lxcfs.nix
@@ -0,0 +1,49 @@
+# LXC Configuration
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.virtualisation.lxc.lxcfs;
+in {
+  meta.maintainers = [ maintainers.mic92 ];
+
+  ###### interface
+  options.virtualisation.lxc.lxcfs = {
+    enable =
+      mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          This enables LXCFS, a FUSE filesystem for LXC.
+          To use lxcfs in include the following configuration in your
+          container configuration:
+          <code>
+            virtualisation.lxc.defaultConfig = "lxc.include = ''${pkgs.lxcfs}/share/lxc/config/common.conf.d/00-lxcfs.conf";
+          </code>
+        '';
+      };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.cgmanager.enable = true;
+
+    systemd.services.lxcfs = {
+      description = "FUSE filesystem for LXC";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "cgmanager.service" ];
+      after = [ "cgmanager.service" ];
+      before = [ "lxc.service" ];
+      restartIfChanged = false;
+      serviceConfig = {
+        ExecStartPre="${pkgs.coreutils}/bin/mkdir -p /var/lib/lxcfs";
+        ExecStart="${pkgs.lxcfs}/bin/lxcfs /var/lib/lxcfs";
+        ExecStopPost="-${pkgs.fuse}/bin/fusermount -u /var/lib/lxcfs";
+        KillMode="process";
+        Restart="on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/nova-image.nix b/nixos/modules/virtualisation/nova-image.nix
index 7971212b47c..e253c77ebb4 100644
--- a/nixos/modules/virtualisation/nova-image.nix
+++ b/nixos/modules/virtualisation/nova-image.nix
@@ -31,7 +31,7 @@ with lib;
 
   # Allow root logins
   services.openssh.enable = true;
-  services.openssh.permitRootLogin = "without-password";
+  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
diff --git a/nixos/modules/virtualisation/openstack/common.nix b/nixos/modules/virtualisation/openstack/common.nix
new file mode 100644
index 00000000000..2feb0a87395
--- /dev/null
+++ b/nixos/modules/virtualisation/openstack/common.nix
@@ -0,0 +1,84 @@
+{ lib }:
+
+with lib;
+
+rec {
+  # A shell script string helper to get the value of a secret at
+  # runtime.
+  getSecret = secretOption:
+    if secretOption.storage == "fromFile"
+    then ''$(cat ${secretOption.value})''
+    else ''${secretOption.value}'';
+
+
+  # A shell script string help to replace at runtime in a file the
+  # pattern of a secret by its value.
+  replaceSecret = secretOption: filename: ''
+    sed -i "s/${secretOption.pattern}/${getSecret secretOption}/g" ${filename}
+    '';
+
+  # This generates an option that can be used to declare secrets which
+  # can be stored in the nix store, or not. A pattern is written in
+  # the nix store to represent the secret. The pattern can
+  # then be overwritten with the value of the secret at runtime.
+  mkSecretOption = {name, description ? ""}:
+    mkOption {
+      description = description;
+      type = types.submodule ({
+        options = {
+          pattern = mkOption {
+            type = types.str;
+            default = "##${name}##";
+            description = "The pattern that represent the secret.";
+            };
+          storage = mkOption {
+            type = types.enum [ "fromNixStore" "fromFile" ];
+            description = ''
+            Choose the way the password is provisionned. If
+            fromNixStore is used, the value is the password and it is
+            written in the nix store. If fromFile is used, the value
+            is a path from where the password will be read at
+            runtime. This is generally used with <link
+            xlink:href="https://nixos.org/nixops/manual/#opt-deployment.keys">
+            deployment keys</link> of Nixops.
+           '';};
+            value = mkOption {
+              type = types.str;
+	      description = ''
+	      If the storage is fromNixStore, the value is the password itself,
+	      otherwise it is a path to the file that contains the password.
+	      '';
+	      };
+            };});
+  };
+  
+  databaseOption = name: {
+    host = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = ''
+        Host of the database.
+      '';
+    };
+
+    name = mkOption {
+      type = types.str;
+      default = name;
+      description = ''
+        Name of the existing database.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = name;
+      description = ''
+        The database user. The user must exist and has access to
+        the specified database.
+      '';
+    };
+    password = mkSecretOption {
+      name = name + "MysqlPassword";
+      description = "The database user's password";};
+  };
+}
diff --git a/nixos/modules/virtualisation/openstack/glance.nix b/nixos/modules/virtualisation/openstack/glance.nix
new file mode 100644
index 00000000000..4d85718e369
--- /dev/null
+++ b/nixos/modules/virtualisation/openstack/glance.nix
@@ -0,0 +1,245 @@
+{ config, lib, pkgs, ... }:
+
+with lib; with import ./common.nix {inherit lib;};
+
+let
+  cfg = config.virtualisation.openstack.glance;
+  commonConf = ''
+    [database]
+    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
+    notification_driver = noop
+
+    [keystone_authtoken]
+    auth_url = ${cfg.authUrl}
+    auth_plugin = password
+    project_name = service
+    project_domain_id = default
+    user_domain_id = default
+    username = ${cfg.serviceUsername}
+    password = ${cfg.servicePassword.pattern}
+
+    [glance_store]
+    default_store = file
+    filesystem_store_datadir = /var/lib/glance/images/
+  '';
+  glanceApiConfTpl = pkgs.writeText "glance-api.conf" ''
+    ${commonConf}
+
+    [paste_deploy]
+    flavor = keystone
+    config_file = ${cfg.package}/etc/glance-api-paste.ini
+  '';
+  glanceRegistryConfTpl = pkgs.writeText "glance-registry.conf" ''
+    ${commonConf}
+
+    [paste_deploy]
+    config_file = ${cfg.package}/etc/glance-registry-paste.ini
+  '';
+  glanceApiConf = "/var/lib/glance/glance-api.conf";
+  glanceRegistryConf = "/var/lib/glance/glance-registry.conf";
+
+in {
+  options.virtualisation.openstack.glance = {
+    package = mkOption {
+      type = types.package;
+      default = pkgs.glance;
+      example = literalExample "pkgs.glance";
+      description = ''
+        Glance package to use.
+      '';
+    };
+
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        This option enables Glance as a single-machine
+        installation. That is, all of Glance's components are
+        enabled on this machine. This is useful for evaluating and
+        experimenting with Glance. Note we are currently not
+        providing any configurations for a multi-node setup.
+      '';
+    };
+
+    authUrl = mkOption {
+      type = types.str;
+      default = http://localhost:5000;
+      description = ''
+        Complete public Identity (Keystone) API endpoint. Note this is
+        unversionned.
+      '';
+    };
+
+    serviceUsername = mkOption {
+      type = types.str;
+      default = "glance";
+      description = ''
+        The Glance service username. This user is created if bootstrap
+        is enable, otherwise it has to be manually created before
+        starting this service.
+      '';
+    };
+
+    servicePassword = mkSecretOption {
+      name = "glanceAdminPassword";
+      description = ''
+        The Glance service user's password.
+      '';
+    };
+
+    database = databaseOption "glance";
+
+    bootstrap = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Bootstrap the Glance service by creating the service tenant,
+          an admin account and a public endpoint. This option provides
+          a ready-to-use glance service. This is only done at the
+          first Glance execution by the systemd post start section.
+          The keystone admin account is used to create required
+          Keystone resource for the Glance service.
+
+          <note><para> This option is a helper for setting up
+          development or testing environments.</para></note>
+        '';
+      };
+
+      endpointPublic = mkOption {
+        type = types.str;
+        default = "http://localhost:9292";
+        description = ''
+          The public image endpoint. The link <link
+          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
+          create endpoint</link> provides more informations
+          about that.
+        '';
+      };
+
+      keystoneAdminUsername = mkOption {
+        type = types.str;
+        default = "admin";
+        description = ''
+          The keystone admin user name used to create the Glance account.
+        '';
+      };
+
+      keystoneAdminPassword = mkSecretOption {
+        name = "keystoneAdminPassword";
+        description = ''
+          The keystone admin user's password.
+        '';
+      };
+
+      keystoneAdminTenant = mkOption {
+        type = types.str;
+        default = "admin";
+        description = ''
+          The keystone admin tenant used to create the Glance account.
+        '';
+      };
+      keystoneAuthUrl = mkOption {
+        type = types.str;
+        default = "http://localhost:5000/v2.0";
+        description = ''
+          The keystone auth url used to create the Glance account.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers = [{
+      name = "glance";
+      group = "glance";
+      uid = config.ids.gids.glance;
+
+    }];
+    users.extraGroups = [{
+      name = "glance";
+      gid = config.ids.gids.glance;
+    }];
+
+    systemd.services.glance-registry = {
+      description = "OpenStack Glance Registry Daemon";
+      after = [ "network.target"];
+      path = [ pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -m 775 -p /var/lib/glance/{images,scrubber,image_cache}
+        chown glance:glance /var/lib/glance/{images,scrubber,image_cache}
+
+        # Secret file managment
+        cp ${glanceRegistryConfTpl} ${glanceRegistryConf};
+        chown glance:glance ${glanceRegistryConf};
+        chmod 640 ${glanceRegistryConf}
+        ${replaceSecret cfg.database.password glanceRegistryConf}
+        ${replaceSecret cfg.servicePassword glanceRegistryConf}
+
+        cp ${glanceApiConfTpl} ${glanceApiConf};
+        chown glance:glance ${glanceApiConf};
+        chmod 640 ${glanceApiConf}
+        ${replaceSecret cfg.database.password glanceApiConf}
+        ${replaceSecret cfg.servicePassword glanceApiConf}
+
+        # Initialise the database
+        ${cfg.package}/bin/glance-manage --config-file=${glanceApiConf} --config-file=${glanceRegistryConf} db_sync
+      '';
+      postStart = ''
+        set -eu
+        export OS_AUTH_URL=${cfg.bootstrap.keystoneAuthUrl}
+        export OS_USERNAME=${cfg.bootstrap.keystoneAdminUsername}
+        export OS_PASSWORD=${getSecret cfg.bootstrap.keystoneAdminPassword}
+        export OS_TENANT_NAME=${cfg.bootstrap.keystoneAdminTenant}
+
+        # Wait until the keystone is available for use
+        count=0
+        while ! keystone user-get ${cfg.bootstrap.keystoneAdminUsername} > /dev/null
+        do
+            if [ $count -eq 30 ]
+            then
+                echo "Tried 30 times, giving up..."
+                exit 1
+            fi
+
+            echo "Keystone not yet started. Waiting for 1 second..."
+            count=$((count++))
+            sleep 1
+        done
+
+        # If the service glance doesn't exist, we consider glance is
+        # not initialized
+        if ! keystone service-get glance
+        then
+            keystone service-create --type image --name glance
+            ID=$(keystone service-get glance | awk '/ id / { print $4 }')
+            keystone endpoint-create --region RegionOne --service $ID --internalurl http://localhost:9292 --adminurl http://localhost:9292 --publicurl ${cfg.bootstrap.endpointPublic}
+
+            keystone user-create --name ${cfg.serviceUsername} --tenant service --pass ${getSecret cfg.servicePassword}
+            keystone user-role-add --tenant service --user ${cfg.serviceUsername} --role admin
+        fi
+        '';
+      serviceConfig = {
+        PermissionsStartOnly = true; # preStart must be run as root
+        TimeoutStartSec = "600"; # 10min for initial db migrations
+        User = "glance";
+        Group = "glance";
+        ExecStart = "${cfg.package}/bin/glance-registry --config-file=${glanceRegistryConf}";
+      };
+    };
+    systemd.services.glance-api = {
+      description = "OpenStack Glance API Daemon";
+      after = [ "glance-registry.service" "network.target"];
+      requires = [ "glance-registry.service" "network.target"]; 
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        PermissionsStartOnly = true; # preStart must be run as root
+        User = "glance";
+        Group = "glance";
+        ExecStart = "${cfg.package}/bin/glance-api --config-file=${glanceApiConf}";
+      };
+    };
+  };
+
+}
diff --git a/nixos/modules/virtualisation/openstack/keystone.nix b/nixos/modules/virtualisation/openstack/keystone.nix
new file mode 100644
index 00000000000..e32c5a4cae1
--- /dev/null
+++ b/nixos/modules/virtualisation/openstack/keystone.nix
@@ -0,0 +1,220 @@
+{ config, lib, pkgs, ... }:
+
+with lib; with import ./common.nix {inherit lib;};
+
+let
+  cfg = config.virtualisation.openstack.keystone;
+  keystoneConfTpl = pkgs.writeText "keystone.conf" ''
+    [DEFAULT]
+    admin_token = ${cfg.adminToken.pattern}
+    policy_file=${cfg.package}/etc/policy.json
+
+    [database]
+
+    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
+
+    [paste_deploy]
+    config_file = ${cfg.package}/etc/keystone-paste.ini
+
+    ${cfg.extraConfig}
+  '';
+  keystoneConf = "/var/lib/keystone/keystone.conf";
+
+in {
+  options.virtualisation.openstack.keystone = {
+    package = mkOption {
+      type = types.package;
+      example = literalExample "pkgs.keystone";
+      description = ''
+        Keystone package to use.
+      '';
+    };
+
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Enable Keystone, the OpenStack Identity Service
+      '';
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Additional text appended to <filename>keystone.conf</filename>,
+        the main Keystone configuration file.
+      '';
+    };
+
+    adminToken = mkSecretOption {
+      name = "adminToken";
+      description = ''
+        This is the admin token used to boostrap keystone,
+        ie. to provision first resources.
+      '';
+    };
+
+    bootstrap = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Bootstrap the Keystone service by creating the service
+          tenant, an admin account and a public endpoint. This options
+          provides a ready-to-use admin account. This is only done at
+          the first Keystone execution by the systemd post start.
+
+          Note this option is a helper for setting up development or
+          testing environments.
+        '';
+      };
+
+      endpointPublic = mkOption {
+        type = types.str;
+        default = "http://localhost:5000/v2.0";
+        description = ''
+          The public identity endpoint. The link <link
+          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
+          create keystone endpoint</link> provides more informations
+          about that.
+        '';
+      };
+
+      adminUsername = mkOption {
+        type = types.str;
+        default = "admin";
+        description = ''
+          A keystone admin username.
+        '';
+      };
+
+      adminPassword = mkSecretOption {
+        name = "keystoneAdminPassword";
+        description = ''
+          The keystone admin user's password.
+        '';
+      };
+
+      adminTenant = mkOption {
+        type = types.str;
+        default = "admin";
+        description = ''
+          A keystone admin tenant name.
+        '';
+      };
+    };
+
+    database = {
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Host of the database.
+        '';
+      };
+
+      name = mkOption {
+        type = types.str;
+        default = "keystone";
+        description = ''
+          Name of the existing database.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "keystone";
+        description = ''
+          The database user. The user must exist and has access to
+          the specified database.
+        '';
+      };
+      password = mkSecretOption {
+        name = "mysqlPassword";
+        description = "The database user's password";};
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Note: when changing the default, make it conditional on
+    # ‘system.stateVersion’ to maintain compatibility with existing
+    # systems!
+    virtualisation.openstack.keystone.package = mkDefault pkgs.keystone;
+
+    users.extraUsers = [{
+      name = "keystone";
+      group = "keystone";
+      uid = config.ids.uids.keystone;
+    }];
+    users.extraGroups = [{
+      name = "keystone";
+      gid = config.ids.gids.keystone;
+    }];
+
+    systemd.services.keystone-all = {
+        description = "OpenStack Keystone Daemon";
+        after = [ "network.target"];
+        path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          mkdir -m 755 -p /var/lib/keystone
+
+          cp ${keystoneConfTpl} ${keystoneConf};
+          chown keystone:keystone ${keystoneConf};
+          chmod 640 ${keystoneConf}
+
+          ${replaceSecret cfg.database.password keystoneConf}
+          ${replaceSecret cfg.adminToken keystoneConf}
+
+          # Initialise the database
+          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
+          # Set up the keystone's PKI infrastructure
+          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} pki_setup --keystone-user keystone --keystone-group keystone
+        '';
+        postStart = optionalString cfg.bootstrap.enable ''
+          set -eu
+          # Wait until the keystone is available for use
+          count=0
+          while ! curl --fail -s  http://localhost:35357/v2.0 > /dev/null 
+          do
+              if [ $count -eq 30 ]
+              then
+                  echo "Tried 30 times, giving up..."
+                  exit 1
+              fi
+
+              echo "Keystone not yet started. Waiting for 1 second..."
+              count=$((count++))
+              sleep 1
+          done
+
+          # We use the service token to create a first admin user
+          export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
+          export OS_SERVICE_TOKEN=${getSecret cfg.adminToken}
+
+          # If the tenant service doesn't exist, we consider
+          # keystone is not initialized
+          if ! keystone tenant-get service
+          then
+              keystone tenant-create --name service
+              keystone tenant-create --name ${cfg.bootstrap.adminTenant}
+              keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${getSecret cfg.bootstrap.adminPassword}
+              keystone role-create --name admin
+              keystone role-create --name Member
+              keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
+              keystone service-create --type identity --name keystone
+              ID=$(keystone service-get keystone | awk '/ id / { print $4 }')
+              keystone endpoint-create --region RegionOne --service $ID --publicurl ${cfg.bootstrap.endpointPublic} --adminurl http://localhost:35357/v2.0 --internalurl http://localhost:5000/v2.0
+          fi
+        '';
+        serviceConfig = {
+          PermissionsStartOnly = true; # preStart must be run as root
+          TimeoutStartSec = "600"; # 10min for initial db migrations
+          User = "keystone";
+          Group = "keystone";
+          ExecStart = "${cfg.package}/bin/keystone-all --config-file=${keystoneConf}";
+        };
+      };
+  };
+}
diff --git a/nixos/modules/virtualisation/parallels-guest.nix b/nixos/modules/virtualisation/parallels-guest.nix
index 204ab0b0df6..bd85973ee56 100644
--- a/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixos/modules/virtualisation/parallels-guest.nix
@@ -57,7 +57,7 @@ in
 
     boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" "acpi_memhotplug" ];
 
-    services.ntp.enable = false;
+    services.timesyncd.enable = false;
 
     systemd.services.prltoolsd = {
       description = "Parallels Tools' service";
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 777ee31e4dc..586f5d9c0a3 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -13,6 +13,8 @@ with lib;
 
 let
 
+  qemu = config.system.build.qemu or pkgs.qemu_test;
+
   vmName =
     if config.networking.hostName == ""
     then "noname"
@@ -32,7 +34,7 @@ let
       NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
 
       if ! test -e "$NIX_DISK_IMAGE"; then
-          ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
+          ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
             ${toString config.virtualisation.diskSize}M || exit 1
       fi
 
@@ -47,7 +49,7 @@ let
       ${if cfg.useBootLoader then ''
         # Create a writable copy/snapshot of the boot disk.
         # A writable boot disk can be booted from automatically.
-        ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 -b ${bootDisk}/disk.img $TMPDIR/disk.img || exit 1
+        ${qemu}/bin/qemu-img create -f qcow2 -b ${bootDisk}/disk.img $TMPDIR/disk.img || exit 1
 
         ${if cfg.useEFIBoot then ''
           # VM needs a writable flash BIOS.
@@ -63,14 +65,14 @@ let
       extraDisks=""
       ${flip concatMapStrings cfg.emptyDiskImages (size: ''
         if ! test -e "empty$idx.qcow2"; then
-            ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
+            ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
         fi
         extraDisks="$extraDisks -drive index=$idx,file=$(pwd)/empty$idx.qcow2,if=${cfg.qemu.diskInterface},werror=report"
         idx=$((idx + 1))
       '')}
 
       # Start QEMU.
-      exec ${pkgs.qemu_kvm}/bin/qemu-kvm \
+      exec ${qemu}/bin/qemu-kvm \
           -name ${vmName} \
           -m ${toString config.virtualisation.memorySize} \
           ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
@@ -121,7 +123,7 @@ let
               mkdir $out
               diskImage=$out/disk.img
               bootFlash=$out/bios.bin
-              ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 $diskImage "40M"
+              ${qemu}/bin/qemu-img create -f qcow2 $diskImage "40M"
               ${if cfg.useEFIBoot then ''
                 cp ${pkgs.OVMF-CSM}/FV/OVMF.fd $bootFlash
                 chmod 0644 $bootFlash
@@ -272,11 +274,11 @@ in
 
     virtualisation.writableStore =
       mkOption {
-        default = false;
+        default = true; # FIXME
         description =
           ''
             If enabled, the Nix store in the VM is made writable by
-            layering a unionfs-fuse/tmpfs filesystem on top of the host's Nix
+            layering an overlay filesystem on top of the host's Nix
             store.
           '';
       };
@@ -393,6 +395,13 @@ in
         chmod 1777 $targetRoot/tmp
 
         mkdir -p $targetRoot/boot
+
+        ${optionalString cfg.writableStore ''
+          echo "mounting overlay filesystem on /nix/store..."
+          mkdir -p 0755 $targetRoot/nix/.rw-store/store $targetRoot/nix/.rw-store/work $targetRoot/nix/store
+          mount -t overlay overlay $targetRoot/nix/store \
+            -o lowerdir=$targetRoot/nix/.ro-store,upperdir=$targetRoot/nix/.rw-store/store,workdir=$targetRoot/nix/.rw-store/work || fail
+        ''}
       '';
 
     # After booting, register the closure of the paths in
@@ -410,7 +419,8 @@ in
       '';
 
     boot.initrd.availableKernelModules =
-      optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx";
+      optional cfg.writableStore "overlay"
+      ++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx";
 
     virtualisation.bootDevice =
       mkDefault (if cfg.qemu.diskInterface == "scsi" then "/dev/sda" else "/dev/vda");
@@ -430,13 +440,13 @@ in
         ${if cfg.writableStore then "/nix/.ro-store" else "/nix/store"} =
           { device = "store";
             fsType = "9p";
-            options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
+            options = [ "trans=virtio" "version=9p2000.L" "veryloose" ];
             neededForBoot = true;
           };
         "/tmp/xchg" =
           { device = "xchg";
             fsType = "9p";
-            options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
+            options = [ "trans=virtio" "version=9p2000.L" "veryloose" ];
             neededForBoot = true;
           };
         "/tmp/shared" =
@@ -445,12 +455,6 @@ in
             options = [ "trans=virtio" "version=9p2000.L" ];
             neededForBoot = true;
           };
-      } // optionalAttrs cfg.writableStore
-      { "/nix/store" =
-          { fsType = "unionfs-fuse";
-            device = "unionfs";
-            options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ];
-          };
       } // optionalAttrs (cfg.writableStore && cfg.writableStoreUseTmpfs)
       { "/nix/.rw-store" =
           { fsType = "tmpfs";
@@ -470,7 +474,7 @@ in
     boot.initrd.luks.devices = mkVMOverride {};
 
     # Don't run ntpd in the guest.  It should get the correct time from KVM.
-    services.ntp.enable = false;
+    services.timesyncd.enable = false;
 
     system.build.vm = pkgs.runCommand "nixos-vm" { preferLocalBuild = true; }
       ''
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index ee8569d3c0c..b3647482f2c 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -4,10 +4,15 @@ with lib;
 
 let
   cfg = config.virtualisation.virtualbox.host;
-  virtualbox = config.boot.kernelPackages.virtualbox.override {
+
+  virtualbox = pkgs.virtualbox.override {
     inherit (cfg) enableHardening headless;
   };
 
+  kernelModules = config.boot.kernelPackages.virtualbox.override {
+    inherit virtualbox;
+  };
+
 in
 
 {
@@ -60,7 +65,7 @@ in
 
   config = mkIf cfg.enable (mkMerge [{
     boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ];
-    boot.extraModulePackages = [ virtualbox ];
+    boot.extraModulePackages = [ kernelModules ];
     environment.systemPackages = [ virtualbox ];
 
     security.permissionsWrappers.setuid = let
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index b6a5b3e4788..d68b3bb7390 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -34,7 +34,7 @@ in {
       postVM =
         ''
           export HOME=$PWD
-          export PATH=${pkgs.linuxPackages.virtualbox}/bin:$PATH
+          export PATH=${pkgs.virtualbox}/bin:$PATH
 
           echo "creating VirtualBox pass-through disk wrapper (no copying invovled)..."
           VBoxManage internalcommands createrawvmdk -filename disk.vmdk -rawdisk $diskImage
diff --git a/nixos/modules/virtualisation/vmware-guest.nix b/nixos/modules/virtualisation/vmware-guest.nix
index b9a4f3b11dc..ac5f87817fe 100644
--- a/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixos/modules/virtualisation/vmware-guest.nix
@@ -5,6 +5,7 @@ with lib;
 let
   cfg = config.services.vmwareGuest;
   open-vm-tools = pkgs.open-vm-tools;
+  xf86inputvmmouse = pkgs.xorg.xf86inputvmmouse;
 in
 {
   options = {
@@ -29,18 +30,17 @@ in
 
     services.xserver = {
       videoDrivers = mkOverride 50 [ "vmware" ];
+      modules = [ xf86inputvmmouse ];
 
       config = ''
-          Section "InputDevice"
+          Section "InputClass"
             Identifier "VMMouse"
+            MatchDevicePath "/dev/input/event*"
+            MatchProduct "ImPS/2 Generic Wheel Mouse"
             Driver "vmmouse"
           EndSection
         '';
 
-      serverLayoutSection = ''
-          InputDevice "VMMouse"
-        '';
-
       displayManager.sessionCommands = ''
           ${open-vm-tools}/bin/vmware-user-suid-wrapper
         '';
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index a0b2d5363eb..67eef0ec1e4 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -324,18 +324,18 @@ in
         domain-needed
         dhcp-hostsfile=/var/run/xen/dnsmasq.etherfile
         dhcp-authoritative
-        dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END,$XEN_BRIDGE_NETWORK_ADDRESS
+        dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END
         dhcp-no-override
         no-ping
         dhcp-leasefile=/var/run/xen/dnsmasq.leasefile
         EOF
 
         # DHCP
-        ${pkgs.iptables}/bin/iptables -I INPUT  -i ${cfg.bridge.name} -p tcp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
-        ${pkgs.iptables}/bin/iptables -I INPUT  -i ${cfg.bridge.name} -p udp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -I INPUT  -i ${cfg.bridge.name} -p tcp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -I INPUT  -i ${cfg.bridge.name} -p udp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
         # DNS
-        ${pkgs.iptables}/bin/iptables -I INPUT  -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
-        ${pkgs.iptables}/bin/iptables -I INPUT  -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -I INPUT  -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -I INPUT  -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
 
         ${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge.name}
         ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} ${cfg.bridge.address}
@@ -347,11 +347,11 @@ in
         ${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge.name}
 
         # DNS
-        ${pkgs.iptables}/bin/iptables -D INPUT  -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
-        ${pkgs.iptables}/bin/iptables -D INPUT  -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -D INPUT  -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -D INPUT  -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
         # DHCP
-        ${pkgs.iptables}/bin/iptables -D INPUT  -i ${cfg.bridge.name} -p udp --sport 68 --dport 67 -j ACCEPT
-        ${pkgs.iptables}/bin/iptables -D INPUT  -i ${cfg.bridge.name} -p tcp --sport 68 --dport 67 -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -D INPUT  -i ${cfg.bridge.name} -p udp --sport 68 --dport 67 -j ACCEPT
+        ${pkgs.iptables}/bin/iptables -w -D INPUT  -i ${cfg.bridge.name} -p tcp --sport 68 --dport 67 -j ACCEPT
       '';
     };
 
diff --git a/nixos/modules/virtualisation/xen-domU.nix b/nixos/modules/virtualisation/xen-domU.nix
index 2db3190ad13..8dd0d1dbfd2 100644
--- a/nixos/modules/virtualisation/xen-domU.nix
+++ b/nixos/modules/virtualisation/xen-domU.nix
@@ -18,5 +18,5 @@
   services.syslogd.tty = "hvc0";
 
   # Don't run ntpd, since we should get the correct time from Dom0.
-  services.ntp.enable = false;
+  services.timesyncd.enable = false;
 }
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index f2ca6af7e3a..70b29aa23a5 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -73,6 +73,7 @@ in rec {
         (all nixos.tests.ipv6)
         (all nixos.tests.i3wm)
         (all nixos.tests.kde4)
+        (all nixos.tests.kde5)
         #(all nixos.tests.lightdm)
         (all nixos.tests.login)
         (all nixos.tests.misc)
@@ -88,6 +89,7 @@ in rec {
         (all nixos.tests.networking.scripted.sit)
         (all nixos.tests.networking.scripted.vlan)
         (all nixos.tests.nfs3)
+        (all nixos.tests.nfs4)
         (all nixos.tests.openssh)
         (all nixos.tests.printing)
         (all nixos.tests.proxy)
diff --git a/nixos/release.nix b/nixos/release.nix
index 70a7ba5af89..dfa9b67654f 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -227,31 +227,39 @@ in rec {
   tests.containers-bridge = callTest tests/containers-bridge.nix {};
   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-tmpfs = callTest tests/containers-tmpfs.nix {};
+  tests.containers-hosts = callTest tests/containers-hosts.nix {};
+  tests.containers-macvlans = callTest tests/containers-macvlans.nix {};
   tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
-  tests.dockerRegistry = hydraJob (import tests/docker-registry.nix { system = "x86_64-linux"; });
   tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
   tests.ecryptfs = callTest tests/ecryptfs.nix {};
   tests.etcd = hydraJob (import tests/etcd.nix { system = "x86_64-linux"; });
   tests.ec2-nixops = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-nixops;
   tests.ec2-config = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-config;
+  tests.ferm = callTest tests/ferm.nix {};
   tests.firefox = callTest tests/firefox.nix {};
   tests.firewall = callTest tests/firewall.nix {};
   tests.fleet = hydraJob (import tests/fleet.nix { system = "x86_64-linux"; });
   #tests.gitlab = callTest tests/gitlab.nix {};
+  tests.glance = callTest tests/glance.nix {};
   tests.gocd-agent = callTest tests/gocd-agent.nix {};
   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.hibernate = callTest tests/hibernate.nix {};
+  tests.hound = callTest tests/hound.nix {};
   tests.i3wm = callTest tests/i3wm.nix {};
   tests.installer = callSubTests tests/installer.nix {};
   tests.influxdb = callTest tests/influxdb.nix {};
   tests.ipv6 = callTest tests/ipv6.nix {};
   tests.jenkins = callTest tests/jenkins.nix {};
   tests.kde4 = callTest tests/kde4.nix {};
+  tests.kde5 = callTest tests/kde5.nix {};
   tests.keymap = callSubTests tests/keymap.nix {};
   tests.initrdNetwork = callTest tests/initrd-network.nix {};
+  tests.keystone = callTest tests/keystone.nix {};
   tests.kubernetes = hydraJob (import tests/kubernetes.nix { system = "x86_64-linux"; });
   tests.latestKernel.login = callTest tests/login.nix { latestKernel = true; };
   #tests.lightdm = callTest tests/lightdm.nix {};
@@ -259,6 +267,7 @@ in rec {
   #tests.logstash = callTest tests/logstash.nix {};
   tests.mathics = callTest tests/mathics.nix {};
   tests.misc = callTest tests/misc.nix {};
+  tests.mongodb = callTest tests/mongodb.nix {};
   tests.mumble = callTest tests/mumble.nix {};
   tests.munin = callTest tests/munin.nix {};
   tests.mysql = callTest tests/mysql.nix {};
@@ -271,18 +280,20 @@ 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.leaps = callTest tests/leaps.nix { };
   tests.nsd = callTest tests/nsd.nix {};
   tests.openssh = callTest tests/openssh.nix {};
-  tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
+  #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
   tests.peerflix = callTest tests/peerflix.nix {};
   tests.postgresql = callTest tests/postgresql.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.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-kde5 = callTest tests/sddm-kde5.nix {};
   tests.simple = callTest tests/simple.nix {};
   tests.smokeping = callTest tests/smokeping.nix {};
   tests.taskserver = callTest tests/taskserver.nix {};
diff --git a/nixos/tests/blivet.nix b/nixos/tests/blivet.nix
index 33a79e65efe..2adc2ee1eee 100644
--- a/nixos/tests/blivet.nix
+++ b/nixos/tests/blivet.nix
@@ -1,4 +1,4 @@
-import ./make-test.nix ({ pkgs, ... }: with pkgs.pythonPackages; rec {
+import ./make-test.nix ({ pkgs, ... }: with pkgs.python2Packages; rec {
   name = "blivet";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ aszlig ];
@@ -69,6 +69,7 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.pythonPackages; rec {
     sed -i \
       -e '1i import tempfile' \
       -e 's|_STORE_FILE_PATH = .*|_STORE_FILE_PATH = tempfile.gettempdir()|' \
+      -e 's|DEFAULT_STORE_SIZE = .*|DEFAULT_STORE_SIZE = 409600|' \
       tests/loopbackedtestcase.py
 
     PYTHONPATH=".:$(< "${pkgs.stdenv.mkDerivation {
diff --git a/nixos/tests/boot-stage1.nix b/nixos/tests/boot-stage1.nix
index ccd8394a1f0..50186525cf3 100644
--- a/nixos/tests/boot-stage1.nix
+++ b/nixos/tests/boot-stage1.nix
@@ -3,7 +3,7 @@ import ./make-test.nix ({ pkgs, ... }: {
 
   machine = { config, pkgs, lib, ... }: {
     boot.extraModulePackages = let
-      compileKernelModule = name: source: pkgs.runCommand name rec {
+      compileKernelModule = name: source: pkgs.runCommandCC name rec {
         inherit source;
         kdev = config.boot.kernelPackages.kernel.dev;
         kver = config.boot.kernelPackages.kernel.modDirVersion;
@@ -62,7 +62,7 @@ import ./make-test.nix ({ pkgs, ... }: {
     boot.initrd.kernelModules = [ "kcanary" ];
 
     boot.initrd.extraUtilsCommands = let
-      compile = name: source: pkgs.runCommand name { inherit source; } ''
+      compile = name: source: pkgs.runCommandCC name { inherit source; } ''
         mkdir -p "$out/bin"
         echo "$source" | gcc -Wall -o "$out/bin/$name" -xc -
       '';
diff --git a/nixos/tests/cadvisor.nix b/nixos/tests/cadvisor.nix
index c55b08c0e92..f0083ab18e4 100644
--- a/nixos/tests/cadvisor.nix
+++ b/nixos/tests/cadvisor.nix
@@ -13,10 +13,6 @@ import ./make-test.nix ({ pkgs, ... } : {
       services.cadvisor.enable = true;
       services.cadvisor.storageDriver = "influxdb";
       services.influxdb.enable = true;
-      systemd.services.influxdb.postStart = mkAfter ''
-        ${pkgs.curl.bin}/bin/curl -X POST 'http://localhost:8086/db?u=root&p=root' \
-          -d '{"name": "root"}'
-      '';
     };
   };
 
@@ -27,6 +23,12 @@ import ./make-test.nix ({ pkgs, ... } : {
       $machine->succeed("curl http://localhost:8080/containers/");
 
       $influxdb->waitForUnit("influxdb.service");
+
+      # create influxdb database
+      $influxdb->succeed(q~
+        curl -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE root"
+      ~);
+
       $influxdb->waitForUnit("cadvisor.service");
       $influxdb->succeed("curl http://localhost:8080/containers/");
     '';
diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix
index 9a6414f81c3..55b1fb5a722 100644
--- a/nixos/tests/chromium.nix
+++ b/nixos/tests/chromium.nix
@@ -118,7 +118,7 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
       "ulimit -c unlimited; ".
       "chromium $args \"$url\" & disown"
     );
-    $machine->waitForText(qr/Type to search or enter a URL to navigate/);
+    $machine->waitForText(qr/startup done/);
     $machine->waitUntilSucceeds("${xdo "check-startup" ''
       search --sync --onlyvisible --name "startup done"
       # close first start help popup
diff --git a/nixos/tests/cjdns.nix b/nixos/tests/cjdns.nix
index f61c82b916a..f32ec52dfc2 100644
--- a/nixos/tests/cjdns.nix
+++ b/nixos/tests/cjdns.nix
@@ -54,7 +54,7 @@ import ./make-test.nix ({ pkgs, ...} : {
           services.cjdns =
             { UDPInterface =
                 { bind = "0.0.0.0:1024";
-                  connectTo."192.168.0.1:1024}" =
+                  connectTo."192.168.0.1:1024" =
                     { password = carolPassword;
                       publicKey = carolPubKey;
                     };
diff --git a/nixos/tests/containers-hosts.nix b/nixos/tests/containers-hosts.nix
new file mode 100644
index 00000000000..c7a85f190a5
--- /dev/null
+++ b/nixos/tests/containers-hosts.nix
@@ -0,0 +1,52 @@
+# Test for NixOS' container support.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-hosts";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ montag451 ];
+  };
+
+  machine =
+    { config, pkgs, lib, ... }:
+    {
+      virtualisation.memorySize = 256;
+      virtualisation.vlans = [];
+
+      networking.bridges.br0.interfaces = [];
+      networking.interfaces.br0 = {
+        ip4 = [ { address = "10.11.0.254"; prefixLength = 24; } ];
+      };
+
+      # Force /etc/hosts to be the only source for host name resolution
+      environment.etc."nsswitch.conf".text = lib.mkForce ''
+        hosts: files
+      '';
+
+      containers.simple = {
+        autoStart = true;
+        privateNetwork = true;
+        localAddress = "10.10.0.1";
+        hostAddress = "10.10.0.254";
+
+        config = {};
+      };
+
+      containers.netmask = {
+        autoStart = true;
+        privateNetwork = true;
+        hostBridge = "br0";
+        localAddress = "10.11.0.1/24";
+
+        config = {};
+      };
+    };
+
+  testScript = ''
+    startAll;
+    $machine->waitForUnit("default.target");
+
+    # Ping the containers using the entries added in /etc/hosts
+    $machine->succeed("ping -n -c 1 simple.containers");
+    $machine->succeed("ping -n -c 1 netmask.containers");
+  '';
+})
diff --git a/nixos/tests/containers-macvlans.nix b/nixos/tests/containers-macvlans.nix
new file mode 100644
index 00000000000..721f9848149
--- /dev/null
+++ b/nixos/tests/containers-macvlans.nix
@@ -0,0 +1,82 @@
+# Test for NixOS' container support.
+
+let
+  # containers IP on VLAN 1
+  containerIp1 = "192.168.1.253";
+  containerIp2 = "192.168.1.254";
+in
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-macvlans";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ montag451 ];
+  };
+
+  nodes = {
+
+    machine1 =
+      { config, pkgs, lib, ... }:
+      {
+        virtualisation.memorySize = 256;
+        virtualisation.vlans = [ 1 ];
+
+        # To be able to ping containers from the host, it is necessary
+        # to create a macvlan on the host on the VLAN 1 network.
+        networking.macvlans.mv-eth1-host = {
+          interface = "eth1";
+          mode = "bridge";
+        };
+        networking.interfaces.eth1.ip4 = lib.mkForce [];
+        networking.interfaces.mv-eth1-host = {
+          ip4 = [ { address = "192.168.1.1"; prefixLength = 24; } ];
+        };
+
+        containers.test1 = {
+          autoStart = true;
+          macvlans = [ "eth1" ];
+
+          config = {
+            networking.interfaces.mv-eth1 = {
+              ip4 = [ { address = containerIp1; prefixLength = 24; } ];
+            };
+          };
+        };
+
+        containers.test2 = {
+          autoStart = true;
+          macvlans = [ "eth1" ];
+
+          config = {
+            networking.interfaces.mv-eth1 = {
+              ip4 = [ { address = containerIp2; prefixLength = 24; } ];
+            };
+          };
+        };
+      };
+
+    machine2 =
+      { config, pkgs, ... }:
+      {
+        virtualisation.memorySize = 256;
+        virtualisation.vlans = [ 1 ];
+      };
+
+  };
+
+  testScript = ''
+    startAll;
+    $machine1->waitForUnit("default.target");
+    $machine2->waitForUnit("default.target");
+
+    # Ping between containers to check that macvlans are created in bridge mode
+    $machine1->succeed("nixos-container run test1 -- ping -n -c 1 ${containerIp2}");
+
+    # Ping containers from the host (machine1)
+    $machine1->succeed("ping -n -c 1 ${containerIp1}");
+    $machine1->succeed("ping -n -c 1 ${containerIp2}");
+
+    # Ping containers from the second machine to check that containers are reachable from the outside
+    $machine2->succeed("ping -n -c 1 ${containerIp1}");
+    $machine2->succeed("ping -n -c 1 ${containerIp2}");
+  '';
+})
diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix
new file mode 100644
index 00000000000..a3b0b29951b
--- /dev/null
+++ b/nixos/tests/containers-physical_interfaces.nix
@@ -0,0 +1,133 @@
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-physical_interfaces";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ kampfschlaefer ];
+  };
+
+  nodes = {
+    server = { config, pkgs, ... }:
+      {
+        virtualisation.memorySize = 256;
+        virtualisation.vlans = [ 1 ];
+
+        containers.server = {
+          privateNetwork = true;
+          interfaces = [ "eth1" ];
+
+          config = {
+            networking.interfaces.eth1 = {
+              ip4 = [ { address = "10.10.0.1"; prefixLength = 24; } ];
+            };
+            networking.firewall.enable = false;
+          };
+        };
+      };
+    bridged = { config, pkgs, ... }: {
+      virtualisation.memorySize = 128;
+      virtualisation.vlans = [ 1 ];
+
+      containers.bridged = {
+        privateNetwork = true;
+        interfaces = [ "eth1" ];
+
+        config = {
+          networking.bridges.br0.interfaces = [ "eth1" ];
+          networking.interfaces.br0 = {
+            ip4 = [ { address = "10.10.0.2"; prefixLength = 24; } ];
+          };
+          networking.firewall.enable = false;
+        };
+      };
+    };
+
+    bonded = { config, pkgs, ... }: {
+      virtualisation.memorySize = 128;
+      virtualisation.vlans = [ 1 ];
+
+      containers.bonded = {
+        privateNetwork = true;
+        interfaces = [ "eth1" ];
+
+        config = {
+          networking.bonds.bond0 = {
+            interfaces = [ "eth1" ];
+            mode = "active-backup";
+          };
+          networking.interfaces.bond0 = {
+            ip4 = [ { address = "10.10.0.3"; prefixLength = 24; } ];
+          };
+          networking.firewall.enable = false;
+        };
+      };
+    };
+
+    bridgedbond = { config, pkgs, ... }: {
+      virtualisation.memorySize = 128;
+      virtualisation.vlans = [ 1 ];
+
+      containers.bridgedbond = {
+        privateNetwork = true;
+        interfaces = [ "eth1" ];
+
+        config = {
+          networking.bonds.bond0 = {
+            interfaces = [ "eth1" ];
+            mode = "active-backup";
+          };
+          networking.bridges.br0.interfaces = [ "bond0" ];
+          networking.interfaces.br0 = {
+            ip4 = [ { address = "10.10.0.4"; prefixLength = 24; } ];
+          };
+          networking.firewall.enable = false;
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    subtest "prepare server", sub {
+      $server->waitForUnit("default.target");
+      $server->succeed("ip link show dev eth1 >&2");
+    };
+
+    subtest "simple physical interface", sub {
+      $server->succeed("nixos-container start server");
+      $server->waitForUnit("container\@server");
+      $server->succeed("systemctl -M server list-dependencies network-addresses-eth1.service >&2");
+
+      # The other tests will ping this container on its ip. Here we just check
+      # that the device is present in the container.
+      $server->succeed("nixos-container run server -- ip a show dev eth1 >&2");
+    };
+
+    subtest "physical device in bridge in container", sub {
+      $bridged->waitForUnit("default.target");
+      $bridged->succeed("nixos-container start bridged");
+      $bridged->waitForUnit("container\@bridged");
+      $bridged->succeed("systemctl -M bridged list-dependencies network-addresses-br0.service >&2");
+      $bridged->succeed("systemctl -M bridged status -n 30 -l network-addresses-br0.service");
+      $bridged->succeed("nixos-container run bridged -- ping -w 10 -c 1 -n 10.10.0.1");
+    };
+
+    subtest "physical device in bond in container", sub {
+      $bonded->waitForUnit("default.target");
+      $bonded->succeed("nixos-container start bonded");
+      $bonded->waitForUnit("container\@bonded");
+      $bonded->succeed("systemctl -M bonded list-dependencies network-addresses-bond0 >&2");
+      $bonded->succeed("systemctl -M bonded status -n 30 -l network-addresses-bond0 >&2");
+      $bonded->succeed("nixos-container run bonded -- ping -w 10 -c 1 -n 10.10.0.1");
+    };
+
+    subtest "physical device in bond in bridge in container", sub {
+      $bridgedbond->waitForUnit("default.target");
+      $bridgedbond->succeed("nixos-container start bridgedbond");
+      $bridgedbond->waitForUnit("container\@bridgedbond");
+      $bridgedbond->succeed("systemctl -M bridgedbond list-dependencies network-addresses-br0.service >&2");
+      $bridgedbond->succeed("systemctl -M bridgedbond status -n 30 -l network-addresses-br0.service");
+      $bridgedbond->succeed("nixos-container run bridgedbond -- ping -w 10 -c 1 -n 10.10.0.1");
+    };
+  '';
+})
diff --git a/nixos/tests/containers-tmpfs.nix b/nixos/tests/containers-tmpfs.nix
new file mode 100644
index 00000000000..564831fa273
--- /dev/null
+++ b/nixos/tests/containers-tmpfs.nix
@@ -0,0 +1,79 @@
+# Test for NixOS' container support.
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "containers-bridge";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ ckampka ];
+  };
+
+  machine =
+    { config, pkgs, ... }:
+    { imports = [ ../modules/installer/cd-dvd/channel.nix ];
+      virtualisation.writableStore = true;
+      virtualisation.memorySize = 768;
+
+      containers.tmpfs =
+        {
+          autoStart = true;
+          tmpfs = [
+            # Mount var as a tmpfs
+            "/var"
+
+            # Add a nested mount inside a tmpfs
+            "/var/log"
+
+            # Add a tmpfs on a path that does not exist
+            "/some/random/path"
+          ];
+          config = { };
+        };
+
+      virtualisation.pathsInNixDB = [ pkgs.stdenv ];
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit("default.target");
+      $machine->succeed("nixos-container list") =~ /tmpfs/ or die;
+
+      # Start the tmpfs container.
+      #$machine->succeed("nixos-container status tmpfs") =~ /up/ or die;
+
+      # Verify that /var is mounted as a tmpfs
+      #$machine->succeed("nixos-container run tmpfs -- systemctl status var.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die;
+      $machine->succeed("nixos-container run tmpfs -- mountpoint -q /var 2>/dev/null");
+
+      # Verify that /var/log is mounted as a tmpfs
+      $machine->succeed("nixos-container run tmpfs -- systemctl status var-log.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die;
+      $machine->succeed("nixos-container run tmpfs -- mountpoint -q /var/log 2>/dev/null");
+
+      # Verify that /some/random/path is mounted as a tmpfs
+      $machine->succeed("nixos-container run tmpfs -- systemctl status some-random-path.mount --no-pager 2>/dev/null") =~ /What: tmpfs/ or die;
+      $machine->succeed("nixos-container run tmpfs -- mountpoint -q /some/random/path 2>/dev/null");
+
+      # Verify that files created in the container in a non-tmpfs directory are visible on the host.
+      # This establishes legitimacy for the following tests
+      $machine->succeed("nixos-container run tmpfs -- touch /root/test.file 2>/dev/null");
+      $machine->succeed("nixos-container run tmpfs -- ls -l  /root | grep -q test.file 2>/dev/null");
+      $machine->succeed("test -e /var/lib/containers/tmpfs/root/test.file");
+
+
+      # Verify that /some/random/path is writable and that files created there
+      # are not in the hosts container dir but in the tmpfs
+      $machine->succeed("nixos-container run tmpfs -- touch /some/random/path/test.file 2>/dev/null");
+      $machine->succeed("nixos-container run tmpfs -- test -e /some/random/path/test.file 2>/dev/null");
+
+      $machine->fail("test -e /var/lib/containers/tmpfs/some/random/path/test.file");
+
+      # Verify that files created in the hosts container dir in a path where a tmpfs file system has been mounted
+      # are not visible to the container as the do not exist in the tmpfs
+      $machine->succeed("touch /var/lib/containers/tmpfs/var/test.file");
+
+      $machine->succeed("test -e /var/lib/containers/tmpfs/var/test.file");
+      $machine->succeed("ls -l /var/lib/containers/tmpfs/var/ | grep -q test.file 2>/dev/null");
+
+      $machine->fail("nixos-container run tmpfs -- ls -l /var | grep -q test.file 2>/dev/null");
+
+    '';
+
+})
diff --git a/nixos/tests/dnscrypt-proxy.nix b/nixos/tests/dnscrypt-proxy.nix
index b686e9582a7..26409949ec6 100644
--- a/nixos/tests/dnscrypt-proxy.nix
+++ b/nixos/tests/dnscrypt-proxy.nix
@@ -22,8 +22,6 @@ import ./make-test.nix ({ pkgs, ... }: {
   };
 
   testScript = ''
-    $client->start;
-    $client->waitForUnit("sockets.target");
     $client->waitForUnit("dnsmasq");
 
     # The daemon is socket activated; sending a single ping should activate it.
diff --git a/nixos/tests/docker-registry.nix b/nixos/tests/docker-registry.nix
index eed3284202f..109fca440e5 100644
--- a/nixos/tests/docker-registry.nix
+++ b/nixos/tests/docker-registry.nix
@@ -3,14 +3,14 @@
 import ./make-test.nix ({ pkgs, ...} : {
   name = "docker-registry";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ offline ];
+    maintainers = [ globin ];
   };
 
   nodes = {
     registry = { config, pkgs, ... }: {
       services.dockerRegistry.enable = true;
       services.dockerRegistry.port = 8080;
-      services.dockerRegistry.host = "0.0.0.0";
+      services.dockerRegistry.listenAddress = "0.0.0.0";
       networking.firewall.allowedTCPPorts = [ 8080 ];
     };
 
diff --git a/nixos/tests/docker.nix b/nixos/tests/docker.nix
index 06e511d6e0b..1b57a94a05d 100644
--- a/nixos/tests/docker.nix
+++ b/nixos/tests/docker.nix
@@ -11,9 +11,6 @@ import ./make-test.nix ({ pkgs, ...} : {
       { config, pkgs, ... }:
         {
           virtualisation.docker.enable = true;
-          # FIXME: The default "devicemapper" storageDriver fails in NixOS VM
-          # tests.
-          virtualisation.docker.storageDriver = "overlay";
         };
     };
 
diff --git a/nixos/tests/ecryptfs.nix b/nixos/tests/ecryptfs.nix
index db800c7bb2c..041be0f5a62 100644
--- a/nixos/tests/ecryptfs.nix
+++ b/nixos/tests/ecryptfs.nix
@@ -21,13 +21,13 @@ import ./make-test.nix ({ pkgs, ... }:
     $machine->log("ecryptfs-migrate-home said: $out");
 
     # Log alice in (ecryptfs passwhrase is wrapped during first login)
-    $machine->sleep(2); # urgh: wait for username prompt
+    $machine->waitUntilTTYMatches(1, "login: ");
     $machine->sendChars("alice\n");
-    $machine->sleep(1);
+    $machine->waitUntilTTYMatches(1, "Password: ");
     $machine->sendChars("foobar\n");
-    $machine->sleep(2);
+    $machine->waitUntilTTYMatches(1, "alice\@machine");
     $machine->sendChars("logout\n");
-    $machine->sleep(2);
+    $machine->waitUntilTTYMatches(1, "login: ");
 
     # Why do I need to do this??
     $machine->succeed("su alice -c ecryptfs-umount-private || true");
@@ -39,10 +39,11 @@ import ./make-test.nix ({ pkgs, ... }:
     $machine->log("keyctl unlink said: " . $out);
 
     # Log alice again
+    $machine->waitUntilTTYMatches(1, "login: ");
     $machine->sendChars("alice\n");
-    $machine->sleep(1);
+    $machine->waitUntilTTYMatches(1, "Password: ");
     $machine->sendChars("foobar\n");
-    $machine->sleep(2);
+    $machine->waitUntilTTYMatches(1, "alice\@machine");
 
     # Create some files in encrypted home
     $machine->succeed("su alice -c 'touch ~alice/a'");
@@ -50,7 +51,7 @@ import ./make-test.nix ({ pkgs, ... }:
 
     # Logout
     $machine->sendChars("logout\n");
-    $machine->sleep(2);
+    $machine->waitUntilTTYMatches(1, "login: ");
 
     # Why do I need to do this??
     $machine->succeed("su alice -c ecryptfs-umount-private || true");
@@ -62,10 +63,11 @@ import ./make-test.nix ({ pkgs, ... }:
     $machine->succeed("su alice -c 'test \! -f ~alice/b'");
 
     # Log alice once more
+    $machine->waitUntilTTYMatches(1, "login: ");
     $machine->sendChars("alice\n");
-    $machine->sleep(1);
+    $machine->waitUntilTTYMatches(1, "Password: ");
     $machine->sendChars("foobar\n");
-    $machine->sleep(2);
+    $machine->waitUntilTTYMatches(1, "alice\@machine");
 
     # Check that the files are there
     $machine->sleep(1);
@@ -77,5 +79,6 @@ import ./make-test.nix ({ pkgs, ... }:
     $machine->succeed("su alice -c 'ls -lh ~alice/'");
 
     $machine->sendChars("logout\n");
+    $machine->waitUntilTTYMatches(1, "login: ");
   '';
 })
diff --git a/nixos/tests/etcd-cluster.nix b/nixos/tests/etcd-cluster.nix
new file mode 100644
index 00000000000..3971997a9bf
--- /dev/null
+++ b/nixos/tests/etcd-cluster.nix
@@ -0,0 +1,157 @@
+# This test runs simple etcd cluster
+
+import ./make-test.nix ({ pkgs, ... } : let
+
+  runWithOpenSSL = file: cmd: pkgs.runCommand file {
+    buildInputs = [ pkgs.openssl ];
+  } cmd;
+
+  ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
+  ca_pem = runWithOpenSSL "ca.pem" ''
+    openssl req \
+      -x509 -new -nodes -key ${ca_key} \
+      -days 10000 -out $out -subj "/CN=etcd-ca"
+  '';
+  etcd_key = runWithOpenSSL "etcd-key.pem" "openssl genrsa -out $out 2048";
+  etcd_csr = runWithOpenSSL "etcd.csr" ''
+    openssl req \
+       -new -key ${etcd_key} \
+       -out $out -subj "/CN=etcd" \
+       -config ${openssl_cnf}
+  '';
+  etcd_cert = runWithOpenSSL "etcd.pem" ''
+    openssl x509 \
+      -req -in ${etcd_csr} \
+      -CA ${ca_pem} -CAkey ${ca_key} \
+      -CAcreateserial -out $out \
+      -days 365 -extensions v3_req \
+      -extfile ${openssl_cnf}
+  '';
+
+  etcd_client_key = runWithOpenSSL "etcd-client-key.pem"
+    "openssl genrsa -out $out 2048";
+
+  etcd_client_csr = runWithOpenSSL "etcd-client-key.pem" ''
+    openssl req \
+      -new -key ${etcd_client_key} \
+      -out $out -subj "/CN=etcd-client" \
+      -config ${client_openssl_cnf}
+  '';
+
+  etcd_client_cert = runWithOpenSSL "etcd-client.crt" ''
+    openssl x509 \
+      -req -in ${etcd_client_csr} \
+      -CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
+      -out $out -days 365 -extensions v3_req \
+      -extfile ${client_openssl_cnf}
+  '';
+
+  openssl_cnf = pkgs.writeText "openssl.cnf" ''
+    ions = v3_req
+    distinguished_name = req_distinguished_name
+    [req_distinguished_name]
+    [ v3_req ]
+    basicConstraints = CA:FALSE
+    keyUsage = digitalSignature, keyEncipherment
+    extendedKeyUsage = serverAuth
+    subjectAltName = @alt_names
+    [alt_names]
+    DNS.1 = node1
+    DNS.2 = node2
+    DNS.3 = node3
+    IP.1 = 127.0.0.1
+  '';
+
+  client_openssl_cnf = pkgs.writeText "client-openssl.cnf" ''
+    ions = v3_req
+    distinguished_name = req_distinguished_name
+    [req_distinguished_name]
+    [ v3_req ]
+    basicConstraints = CA:FALSE
+    keyUsage = digitalSignature, keyEncipherment
+    extendedKeyUsage = clientAuth
+  '';
+
+  nodeConfig = {
+    services = {
+      etcd = {
+        enable = true;
+        keyFile = etcd_key;
+        certFile = etcd_cert;
+        trustedCaFile = ca_pem;
+        peerClientCertAuth = true;
+        listenClientUrls = ["https://127.0.0.1:2379"];
+        listenPeerUrls = ["https://0.0.0.0:2380"];
+      };
+    };
+
+    environment.variables = {
+      ETCDCTL_CERT_FILE = "${etcd_client_cert}";
+      ETCDCTL_KEY_FILE = "${etcd_client_key}";
+      ETCDCTL_CA_FILE = "${ca_pem}";
+      ETCDCTL_PEERS = "https://127.0.0.1:2379";
+    };
+
+    networking.firewall.allowedTCPPorts = [ 2380 ];
+  };
+in {
+  name = "etcd";
+
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ offline ];
+  };
+
+  nodes = {
+    node1 = { config, pkgs, nodes, ... }: {
+      require = [nodeConfig];
+      services.etcd = {
+        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
+        initialAdvertisePeerUrls = ["https://node1:2380"];
+      };
+    };
+
+    node2 = { config, pkgs, ... }: {
+      require = [nodeConfig];
+      services.etcd = {
+        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
+        initialAdvertisePeerUrls = ["https://node2:2380"];
+      };
+    };
+
+    node3 = { config, pkgs, ... }: {
+      require = [nodeConfig];
+      services.etcd = {
+        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380" "node3=https://node3:2380"];
+        initialAdvertisePeerUrls = ["https://node3:2380"];
+        initialClusterState = "existing";
+      };
+    };
+  };
+
+  testScript = ''
+    subtest "should start etcd cluster", sub {
+      $node1->start();
+      $node2->start();
+      $node1->waitForUnit("etcd.service");
+      $node2->waitForUnit("etcd.service");
+      $node2->waitUntilSucceeds("etcdctl cluster-health");
+      $node1->succeed("etcdctl set /foo/bar 'Hello world'");
+      $node2->succeed("etcdctl get /foo/bar | grep 'Hello world'");
+    };
+
+    subtest "should add another member", sub {
+      $node1->succeed("etcdctl member add node3 https://node3:2380");
+      $node3->start();
+      $node3->waitForUnit("etcd.service");
+      $node3->waitUntilSucceeds("etcdctl member list | grep 'node3'");
+      $node3->succeed("etcdctl cluster-health");
+    };
+
+    subtest "should survive member crash", sub {
+      $node3->crash;
+      $node1->succeed("etcdctl cluster-health");
+      $node1->succeed("etcdctl set /foo/bar 'Hello degraded world'");
+      $node1->succeed("etcdctl get /foo/bar | grep 'Hello degraded world'");
+    };
+  '';
+})
diff --git a/nixos/tests/etcd.nix b/nixos/tests/etcd.nix
index bac4ec6a918..f8a6791a834 100644
--- a/nixos/tests/etcd.nix
+++ b/nixos/tests/etcd.nix
@@ -1,111 +1,27 @@
-# This test runs etcd as single node, multy node and using discovery
+# This test runs simple etcd node
 
 import ./make-test.nix ({ pkgs, ... } : {
   name = "etcd";
+
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ offline ];
   };
 
   nodes = {
-    simple =
-      { config, pkgs, nodes, ... }:
-        {
-          services.etcd.enable = true;
-          services.etcd.listenClientUrls = ["http://0.0.0.0:4001"];
-          environment.systemPackages = [ pkgs.curl ];
-          networking.firewall.allowedTCPPorts = [ 4001 ];
-        };
-
-
-    node1 =
-      { config, pkgs, nodes, ... }:
-        {
-          services = {
-            etcd = {
-              enable = true;
-              listenPeerUrls = ["http://0.0.0.0:7001"];
-              initialAdvertisePeerUrls = ["http://node1:7001"];
-              initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"];
-            };
-          };
-
-          networking.firewall.allowedTCPPorts = [ 7001 ];
-        };
-
-    node2 =
-      { config, pkgs, ... }:
-        {
-          services = {
-            etcd = {
-              enable = true;
-              listenPeerUrls = ["http://0.0.0.0:7001"];
-              initialAdvertisePeerUrls = ["http://node2:7001"];
-              initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"];
-            };
-          };
-
-          networking.firewall.allowedTCPPorts = [ 7001 ];
-        };
-
-    discovery1 =
-      { config, pkgs, nodes, ... }:
-        {
-          services = {
-            etcd = {
-              enable = true;
-              listenPeerUrls = ["http://0.0.0.0:7001"];
-              initialAdvertisePeerUrls = ["http://discovery1:7001"];
-              discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/";
-            };
-          };
-
-          networking.firewall.allowedTCPPorts = [ 7001 ];
-        };
-
-    discovery2 =
-      { config, pkgs, ... }:
-        {
-          services = {
-            etcd = {
-              enable = true;
-              listenPeerUrls = ["http://0.0.0.0:7001"];
-              initialAdvertisePeerUrls = ["http://discovery2:7001"];
-              discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/";
-            };
-          };
-
-          networking.firewall.allowedTCPPorts = [ 7001 ];
-        };
+    node = { config, pkgs, nodes, ... }: {
+      services.etcd.enable = true;
     };
+  };
 
   testScript = ''
-    subtest "single node", sub {
-      $simple->start();
-      $simple->waitForUnit("etcd.service");
-      $simple->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
-      $simple->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
+    subtest "should start etcd node", sub {
+      $node->start();
+      $node->waitForUnit("etcd.service");
     };
 
-    subtest "multy node", sub {
-      $node1->start();
-      $node2->start();
-      $node1->waitForUnit("etcd.service");
-      $node2->waitForUnit("etcd.service");
-      $node1->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
-      $node2->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
-      $node1->shutdown();
-      $node2->shutdown();
-    };
-
-    subtest "discovery", sub {
-      $simple->succeed("curl -X PUT http://localhost:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=2");
-
-      $discovery1->start();
-      $discovery2->start();
-      $discovery1->waitForUnit("etcd.service");
-      $discovery2->waitForUnit("etcd.service");
-      $discovery1->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
-      $discovery2->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
-    };
+    subtest "should write and read some values to etcd", sub {
+      $node->succeed("etcdctl set /foo/bar 'Hello world'");
+      $node->succeed("etcdctl get /foo/bar | grep 'Hello world'");
+    }
   '';
 })
diff --git a/nixos/tests/ferm.nix b/nixos/tests/ferm.nix
new file mode 100644
index 00000000000..8f2a8c01eeb
--- /dev/null
+++ b/nixos/tests/ferm.nix
@@ -0,0 +1,72 @@
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "ferm";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ mic92 ];
+  };
+
+  nodes =
+    { client =
+        { config, pkgs, ... }:
+        with pkgs.lib;
+        {
+          networking = {
+            interfaces.eth1.ip6 = mkOverride 0 [ { address = "fd00::2"; prefixLength = 64; } ];
+            interfaces.eth1.ip4 = mkOverride 0 [ { address = "192.168.1.2"; prefixLength = 24; } ];
+          };
+      };
+      server =
+        { config, pkgs, ... }:
+        with pkgs.lib;
+        {
+          networking = {
+            interfaces.eth1.ip6 = mkOverride 0 [ { address = "fd00::1"; prefixLength = 64; } ];
+            interfaces.eth1.ip4 = mkOverride 0 [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          };
+
+          services = {
+            ferm.enable = true;
+            ferm.config = ''
+              domain (ip ip6) table filter chain INPUT {
+                interface lo ACCEPT;
+                proto tcp dport 8080 REJECT reject-with tcp-reset;
+              }
+            '';
+            nginx.enable = true;
+            nginx.httpConfig = ''
+              server {
+                listen 80;
+                listen [::]:80;
+                listen 8080;
+                listen [::]:8080;
+
+                location /status { stub_status on; }
+              }
+            '';
+          };
+        };
+    };
+
+  testScript =
+    ''
+      startAll;
+
+      $client->waitForUnit("network.target");
+      $server->waitForUnit("ferm.service");
+      $server->waitForUnit("nginx.service");
+      $server->waitUntilSucceeds("ss -ntl | grep -q 80");
+
+      subtest "port 80 is allowed", sub {
+          $client->succeed("curl --fail -g http://192.168.1.1:80/status");
+          $client->succeed("curl --fail -g http://[fd00::1]:80/status");
+      };
+
+      subtest "port 8080 is not allowed", sub {
+          $server->succeed("curl --fail -g http://192.168.1.1:8080/status");
+          $server->succeed("curl --fail -g http://[fd00::1]:8080/status");
+
+          $client->fail("curl --fail -g http://192.168.1.1:8080/status");
+          $client->fail("curl --fail -g http://[fd00::1]:8080/status");
+      };
+    '';
+})
diff --git a/nixos/tests/firewall.nix b/nixos/tests/firewall.nix
index 8f2cb27b60f..1119a5312eb 100644
--- a/nixos/tests/firewall.nix
+++ b/nixos/tests/firewall.nix
@@ -15,6 +15,16 @@ import ./make-test.nix ( { pkgs, ... } : {
           services.httpd.adminAddr = "foo@example.org";
         };
 
+      # Dummy configuration to check whether firewall.service will be honored
+      # during system activation. This only needs to be different to the
+      # original walled configuration so that there is a change in the service
+      # file.
+      walled2 =
+        { config, pkgs, nodes, ... }:
+        { networking.firewall.enable = true;
+          networking.firewall.rejectPackets = true;
+        };
+
       attacker =
         { config, pkgs, ... }:
         { services.httpd.enable = true;
@@ -23,28 +33,33 @@ import ./make-test.nix ( { pkgs, ... } : {
         };
     };
 
-  testScript =
-    { nodes, ... }:
-    ''
-      startAll;
+  testScript = { nodes, ... }: let
+    newSystem = nodes.walled2.config.system.build.toplevel;
+  in ''
+    $walled->start;
+    $attacker->start;
+
+    $walled->waitForUnit("firewall");
+    $walled->waitForUnit("httpd");
+    $attacker->waitForUnit("network.target");
 
-      $walled->waitForUnit("firewall");
-      $walled->waitForUnit("httpd");
-      $attacker->waitForUnit("network.target");
+    # Local connections should still work.
+    $walled->succeed("curl -v http://localhost/ >&2");
 
-      # Local connections should still work.
-      $walled->succeed("curl -v http://localhost/ >&2");
+    # Connections to the firewalled machine should fail, but ping should succeed.
+    $attacker->fail("curl --fail --connect-timeout 2 http://walled/ >&2");
+    $attacker->succeed("ping -c 1 walled >&2");
 
-      # Connections to the firewalled machine should fail, but ping should succeed.
-      $attacker->fail("curl --fail --connect-timeout 2 http://walled/ >&2");
-      $attacker->succeed("ping -c 1 walled >&2");
+    # Outgoing connections/pings should still work.
+    $walled->succeed("curl -v http://attacker/ >&2");
+    $walled->succeed("ping -c 1 attacker >&2");
 
-      # Outgoing connections/pings should still work.
-      $walled->succeed("curl -v http://attacker/ >&2");
-      $walled->succeed("ping -c 1 attacker >&2");
+    # If we stop the firewall, then connections should succeed.
+    $walled->stopJob("firewall");
+    $attacker->succeed("curl -v http://walled/ >&2");
 
-      # If we stop the firewall, then connections should succeed.
-      $walled->stopJob("firewall");
-      $attacker->succeed("curl -v http://walled/ >&2");
-    '';
+    # Check whether activation of a new configuration reloads the firewall.
+    $walled->succeed("${newSystem}/bin/switch-to-configuration test 2>&1" .
+                     " | grep -qF firewall.service");
+  '';
 })
diff --git a/nixos/tests/flannel.nix b/nixos/tests/flannel.nix
new file mode 100644
index 00000000000..7f27903a302
--- /dev/null
+++ b/nixos/tests/flannel.nix
@@ -0,0 +1,55 @@
+import ./make-test.nix ({ pkgs, ...} : rec {
+  name = "flannel";
+
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ offline ];
+  };
+
+  nodes = let
+    flannelConfig = {
+      services.flannel = {
+        enable = true;
+        network = "10.1.0.0/16";
+        iface = "eth1";
+        etcd.endpoints = ["http://etcd:2379"];
+      };
+
+      networking.firewall.allowedUDPPorts = [ 8472 ];
+    };
+  in {
+    etcd = { config, pkgs, ... }: {
+      services = {
+        etcd = {
+          enable = true;
+          listenClientUrls = ["http://etcd:2379"];
+          listenPeerUrls = ["http://etcd:2380"];
+          initialAdvertisePeerUrls = ["http://etcd:2379"];
+          initialCluster = ["etcd=http://etcd:2379"];
+        };
+      };
+
+      networking.firewall.allowedTCPPorts = [ 2379 ];
+    };
+
+    node1 = { config, ... }: {
+      require = [flannelConfig];
+    };
+
+    node2 = { config, ... }: {
+      require = [flannelConfig];
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    $node1->waitForUnit("flannel.service");
+    $node2->waitForUnit("flannel.service");
+
+    my $ip1 = $node1->succeed("ip -4 addr show flannel.1 | grep -oP '(?<=inet).*(?=/)'");
+    my $ip2 = $node2->succeed("ip -4 addr show flannel.1 | grep -oP '(?<=inet).*(?=/)'");
+
+    $node1->waitUntilSucceeds("ping -c 1 $ip2");
+    $node2->waitUntilSucceeds("ping -c 1 $ip1");
+  '';
+})
diff --git a/nixos/tests/glance.nix b/nixos/tests/glance.nix
new file mode 100644
index 00000000000..992b77227a4
--- /dev/null
+++ b/nixos/tests/glance.nix
@@ -0,0 +1,77 @@
+{ system ? builtins.currentSystem }:
+
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
+
+let
+  glanceMysqlPassword = "glanceMysqlPassword";
+  glanceAdminPassword = "glanceAdminPassword";
+
+  createDb = pkgs.writeText "db-provisionning.sql" ''
+    create database keystone;
+    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';
+    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';
+
+    create database glance;
+    GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' IDENTIFIED BY '${glanceMysqlPassword}';
+    GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY '${glanceMysqlPassword}';
+  '';
+
+  image =
+    (import ../lib/eval-config.nix {
+      inherit system;
+      modules = [ ../../nixos/modules/virtualisation/nova-image.nix ];
+    }).config.system.build.novaImage;
+
+  # The admin keystone account
+  adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=keystone OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
+
+in makeTest {
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lewo ];
+  };
+  machine =
+    { config, pkgs, ... }:
+    {
+      services.mysql.enable = true;
+      services.mysql.package = pkgs.mysql;
+      services.mysql.initialScript = createDb;
+
+      virtualisation = {
+        openstack.keystone = {
+          enable = true;
+          database.password = { value = "keystone"; storage = "fromNixStore"; };
+          adminToken = { value = "adminToken"; storage = "fromNixStore"; };
+          bootstrap.enable = true;
+          bootstrap.adminPassword = { value = "keystone"; storage = "fromNixStore"; };
+        };
+
+        openstack.glance = {
+          enable = true;
+          database.password = { value = glanceMysqlPassword; storage = "fromNixStore"; };
+          servicePassword = { value = glanceAdminPassword; storage = "fromNixStore"; };
+
+          bootstrap = {
+            enable = true;
+            keystoneAdminPassword = { value = "keystone"; storage = "fromNixStore"; };
+          };
+        };
+
+        memorySize = 2096;
+        diskSize = 4 * 1024;
+        };
+
+      environment.systemPackages = with pkgs.pythonPackages; with pkgs; [
+        openstackclient
+      ];
+    };
+
+  testScript =
+    ''
+     $machine->waitForUnit("glance-api.service");
+
+     # Since Glance api can take time to start, we retry until success
+     $machine->waitUntilSucceeds("${adminOpenstackCmd} image create nixos --file ${image}/nixos.img --disk-format qcow2 --container-format bare --public");
+     $machine->succeed("${adminOpenstackCmd} image list") =~ /nixos/ or die;
+    '';
+}
diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix
index bd8f9502e2f..4787d42d695 100644
--- a/nixos/tests/gnome3.nix
+++ b/nixos/tests/gnome3.nix
@@ -29,7 +29,7 @@ import ./make-test.nix ({ pkgs, ...} : {
       $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
       $machine->succeed("xauth merge ~alice/.Xauthority");
       $machine->waitForWindow(qr/Terminal/);
-      $machine->mustSucceed("timeout 900 bash -c 'journalctl -f|grep -m 1 \"GNOME Shell started\"'");
+      $machine->succeed("timeout 900 bash -c 'while read msg; do if [[ \$msg =~ \"GNOME Shell started\" ]]; then break; fi; done < <(journalctl -f)'");
       $machine->sleep(10);
       $machine->screenshot("screen");
     '';
diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix
index e585a7402d3..ee9e0709e5e 100644
--- a/nixos/tests/grsecurity.nix
+++ b/nixos/tests/grsecurity.nix
@@ -8,7 +8,9 @@ import ./make-test.nix ({ pkgs, ...} : {
 
   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 = ''
@@ -20,16 +22,14 @@ import ./make-test.nix ({ pkgs, ...} : {
 
     subtest "paxtest", sub {
       # TODO: running paxtest blackhat hangs the vm
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/anonmap") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execbss") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execdata") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execheap") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/execstack") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotanon") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotbss") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotdata") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotheap") =~ /Killed/ or die;
-      $machine->succeed("${pkgs.paxtest}/lib/paxtest/mprotstack") =~ /Killed/ or die;
+      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
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 787929f8904..7616a75b021 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 -p 4444";
+      systemd.services.listener.serviceConfig.ExecStart = "${pkgs.netcat}/bin/nc -l 4444";
     };
 
     probe = { config, lib, pkgs, ...}: {
@@ -36,7 +36,7 @@ import ./make-test.nix (pkgs: {
       $machine->waitForShutdown;
       $machine->start;
       $probe->waitForUnit("network.target");
-      $probe->waitUntilSucceeds("echo test | nc -c machine 4444");
+      $probe->waitUntilSucceeds("echo test | nc machine 4444");
     '';
 
 })
diff --git a/nixos/tests/hound.nix b/nixos/tests/hound.nix
new file mode 100644
index 00000000000..82fd44e8e36
--- /dev/null
+++ b/nixos/tests/hound.nix
@@ -0,0 +1,58 @@
+# Test whether `houndd` indexes nixpkgs
+import ./make-test.nix ({ pkgs, ... } : {
+  name = "hound";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ grahamc ];
+  };
+  machine = { config, pkgs, ... }: {
+    services.hound = {
+      enable = true;
+      config = ''
+        {
+          "max-concurrent-indexers": 1,
+          "dbpath": "/var/lib/hound/data",
+          "repos": {
+            "nix": {
+              "url": "file:///var/lib/hound/my-git"
+            }
+          }
+        }
+      '';
+    };
+
+    systemd.services.houndseed = {
+      description = "seed hound with a git repo";
+      requiredBy = [ "hound.service" ];
+      before = [ "hound.service" ];
+
+      serviceConfig = {
+        User = "hound";
+        Group = "hound";
+        WorkingDirectory = "/var/lib/hound";
+      };
+      path = [ pkgs.git ];
+      script = ''
+        git config --global user.email "you@example.com"
+        git config --global user.name "Your Name"
+        git init my-git --bare
+        git init my-git-clone
+        cd my-git-clone
+        echo 'hi nix!' > hello
+        git add hello
+        git commit -m "hello there :)"
+        git remote add origin /var/lib/hound/my-git
+        git push origin master
+      '';
+    };
+  };
+
+  testScript =
+    '' startAll;
+
+       $machine->waitForUnit("network.target");
+       $machine->waitForUnit("hound.service");
+       $machine->waitForOpenPort(6080);
+       $machine->succeed('curl http://127.0.0.1:6080/api/v1/search\?stats\=fosho\&repos\=\*\&rng=%3A20\&q\=hi\&files\=\&i=nope | grep "Filename" | grep "hello"');
+
+    '';
+})
diff --git a/nixos/tests/influxdb.nix b/nixos/tests/influxdb.nix
index 0408d8983ad..ee126091667 100644
--- a/nixos/tests/influxdb.nix
+++ b/nixos/tests/influxdb.nix
@@ -17,23 +17,17 @@ import ./make-test.nix ({ pkgs, ...} : {
   
     $one->waitForUnit("influxdb.service");
 
-    # Check if admin interface is avalible
-    $one->waitUntilSucceeds("curl -f 127.0.0.1:8083");
-
     # create database
     $one->succeed(q~
-      curl -X POST 'http://localhost:8086/db?u=root&p=root' \
-        -d '{"name": "test"}'
+      curl -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE test"
     ~);
 
     # write some points and run simple query
     $one->succeed(q~
-      curl -X POST 'http://localhost:8086/db/test/series?u=root&p=root' \
-        -d '[{"name":"foo","columns":["val"],"points":[[6666]]}]'
+      curl -XPOST 'http://localhost:8086/write?db=test' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000'
     ~);
     $one->succeed(q~
-      curl -G 'http://localhost:8086/db/test/series?u=root&p=root' \
-        --data-urlencode 'q=select * from foo limit 1' | grep 6666
+      curl -GET 'http://localhost:8086/query' --data-urlencode "db=test" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'"  | grep "0\.64"
     ~);
   '';
 })
diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix
new file mode 100644
index 00000000000..92d742e4f37
--- /dev/null
+++ b/nixos/tests/ipfs.nix
@@ -0,0 +1,37 @@
+
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "ipfs";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ mguentner ];
+  };
+
+  nodes = {
+    adder =
+      { config, pkgs, ... }:
+      {
+        services.ipfs = {
+          enable = true;
+          gatewayAddress = "/ip4/127.0.0.1/tcp/2323";
+          apiAddress = "/ip4/127.0.0.1/tcp/2324";
+        };
+      };
+    getter =
+      { config, pkgs, ... }:
+      {
+         services.ipfs.enable = true;
+      };
+  };
+
+  testScript = ''
+    startAll;
+    $adder->waitForUnit("ipfs");
+    # * => needs ipfs dht (internet)
+    # $getter->waitForUnit("ipfs");
+    $adder->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/2324 id");
+    $adder->mustSucceed("([[ -n '$(ipfs --api /ip4/127.0.0.1/tcp/2324 config Addresses.gatewayAddress | grep /ip4/127.0.0.1/tcp/2323)' ]])");
+    # * $getter->waitUntilSucceeds("ipfs --api /ip4/127.0.0.1/tcp/5001 id");
+    # * my $ipfsHash = $adder->mustSucceed("echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | cut -d' ' -f2");
+    $adder->mustSucceed("([[ -n '$(echo fnord | ipfs --api /ip4/127.0.0.1/tcp/2324 add | grep added)' ]])");
+    # * $getter->mustSucceed("ipfs --api /ip4/127.0.0.1/tcp/5001 cat $ipfsHash");
+    '';
+})
diff --git a/nixos/tests/sddm-kde5.nix b/nixos/tests/kde5.nix
index 0247d267aaa..2b61d6f3f0a 100644
--- a/nixos/tests/sddm-kde5.nix
+++ b/nixos/tests/kde5.nix
@@ -1,7 +1,7 @@
 import ./make-test.nix ({ pkgs, ...} :
 
 {
-  name = "sddm";
+  name = "kde5";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ ttuegel ];
   };
@@ -18,35 +18,31 @@ import ./make-test.nix ({ pkgs, ...} :
       };
     };
     services.xserver.desktopManager.kde5.enable = true;
+    virtualisation.writableStore = false; # FIXME
   };
 
-  enableOCR = true;
-
   testScript = { nodes, ... }:
   let xdo = "${pkgs.xdotool}/bin/xdotool"; in
-   ''     
-    sub krunner {
-      my ($win,) = @_;
-      $machine->execute("${xdo} key Alt+F2 sleep 1 type $win");
-      $machine->execute("${xdo} search --sync --onlyvisible --class krunner sleep 5 key Return");
-    }
+   ''
+    startAll;
+
+    $machine->waitForFile("/home/alice/.Xauthority");
+    $machine->succeed("xauth merge ~alice/.Xauthority");
 
     $machine->waitUntilSucceeds("pgrep plasmashell");
-    $machine->succeed("xauth merge ~alice/.Xauthority");    
-    $machine->waitForWindow(qr/Desktop.*/);
+    $machine->waitForWindow("^Desktop ");
 
     # Check that logging in has given the user ownership of devices.
     $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
-    
-    krunner("dolphin");
-    $machine->waitForWindow(qr/.*Dolphin/);
-    
-    krunner("konsole");
-    $machine->waitForWindow(qr/.*Konsole/);
-    
-    krunner("systemsettings5");
-    $machine->waitForWindow(qr/.*Settings/);
-    $machine->sleep(20);
+
+    $machine->execute("su - alice -c 'DISPLAY=:0.0 dolphin &'");
+    $machine->waitForWindow(" Dolphin");
+
+    $machine->execute("su - alice -c 'DISPLAY=:0.0 konsole &'");
+    $machine->waitForWindow("Konsole");
+
+    $machine->execute("su - alice -c 'DISPLAY=:0.0 systemsettings5 &'");
+    $machine->waitForWindow("Settings");
 
     $machine->execute("${xdo} key Alt+F1 sleep 10");
     $machine->screenshot("screen");
diff --git a/nixos/tests/keystone.nix b/nixos/tests/keystone.nix
new file mode 100644
index 00000000000..358e352f776
--- /dev/null
+++ b/nixos/tests/keystone.nix
@@ -0,0 +1,82 @@
+{ system ? builtins.currentSystem }:
+
+with import ../lib/testing.nix { inherit system; };
+with pkgs.lib;
+
+let
+  keystoneMysqlPassword = "keystoneMysqlPassword";
+  keystoneMysqlPasswordFile = "/var/run/keystoneMysqlPassword";
+  keystoneAdminPassword = "keystoneAdminPassword";
+
+  createKeystoneDb = pkgs.writeText "create-keystone-db.sql" ''
+    create database keystone;
+    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY '${keystoneMysqlPassword}';
+    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY '${keystoneMysqlPassword}';
+  '';
+  # The admin keystone account
+  adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=${keystoneAdminPassword} OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
+  # The created demo keystone account
+  demoOpenstackCmd = "OS_TENANT_NAME=demo OS_USERNAME=demo OS_PASSWORD=demo OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
+
+in makeTest {
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lewo ];
+  };
+  machine =
+    { config, pkgs, ... }:
+    {
+      # This is to simulate nixops deployment process.
+      # https://nixos.org/nixops/manual/#opt-deployment.keys
+      boot.postBootCommands = "echo ${keystoneMysqlPassword} > ${keystoneMysqlPasswordFile}";
+
+      services.mysql.enable = true;
+      services.mysql.initialScript = createKeystoneDb;
+
+      virtualisation = {
+
+        openstack.keystone = {
+	  enable = true;
+	  # Check if we can get the secret from a file
+	  database.password = {
+	    value = keystoneMysqlPasswordFile;
+	    storage = "fromFile";
+	  };
+	  adminToken = {
+	    value = "adminToken";
+	    storage = "fromNixStore";
+	  };
+
+	  bootstrap.enable = true;
+	  # Check if we can get the secret from the store
+	  bootstrap.adminPassword = {
+	    value = keystoneAdminPassword;
+	    storage = "fromNixStore";
+	  };
+	};
+
+        memorySize = 2096;
+        diskSize = 4 * 1024;
+	};
+
+      environment.systemPackages = with pkgs.pythonPackages; with pkgs; [
+        openstackclient
+      ];
+    };
+
+  testScript =
+    ''
+     $machine->waitForUnit("keystone-all.service");
+
+     # Verify that admin ccount is working
+     $machine->succeed("${adminOpenstackCmd} token issue");
+
+     # Try to create a new user
+     $machine->succeed("${adminOpenstackCmd} project create --domain default --description 'Demo Project' demo");
+     $machine->succeed("${adminOpenstackCmd} user create --domain default --password demo demo");
+     $machine->succeed("${adminOpenstackCmd} role create user");
+     $machine->succeed("${adminOpenstackCmd} role add --project demo --user demo user");
+
+     # Verify this new account is working
+     $machine->succeed("${demoOpenstackCmd} token issue");
+    '';
+}
diff --git a/nixos/tests/kubernetes.nix b/nixos/tests/kubernetes.nix
index b19ea67b0ba..273bd3c80c1 100644
--- a/nixos/tests/kubernetes.nix
+++ b/nixos/tests/kubernetes.nix
@@ -1,182 +1,408 @@
-# This test runs two node kubernetes cluster and checks if simple redis pod works
+{ system ? builtins.currentSystem }:
 
-import ./make-test.nix ({ pkgs, ...} : rec {
-  name = "kubernetes";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ offline ];
+with import ../lib/testing.nix { inherit system; };
+with import ../lib/qemu-flags.nix;
+with pkgs.lib;
+
+let
+  redisPod = pkgs.writeText "redis-master-pod.json" (builtins.toJSON {
+    kind = "Pod";
+    apiVersion = "v1";
+    metadata.name = "redis";
+    metadata.labels.name = "redis";
+    spec.containers = [{
+      name = "redis";
+      image = "redis";
+      args = ["--bind" "0.0.0.0"];
+      imagePullPolicy = "Never";
+      ports = [{
+        name = "redis-server";
+        containerPort = 6379;
+      }];
+    }];
+  });
+
+  redisService = pkgs.writeText "redis-service.json" (builtins.toJSON {
+    kind = "Service";
+    apiVersion = "v1";
+    metadata.name = "redis";
+    spec = {
+      ports = [{port = 6379; targetPort = 6379;}];
+      selector = {name = "redis";};
+    };
+  });
+
+  redisImage = pkgs.dockerTools.buildImage {
+    name = "redis";
+    tag = "latest";
+    contents = pkgs.redis;
+    config.Entrypoint = "/bin/redis-server";
   };
 
-  redisMaster = builtins.toFile "redis-master-pod.yaml" ''
-      id: redis-master-pod
-      kind: Pod
-      apiVersion: v1beta1
-      desiredState:
-        manifest:
-          version: v1beta1
-          id: redis-master-pod
-          containers:
-            - name: master
-              image: master:5000/nix
-              cpu: 100
-              ports:
-                - name: redis-server
-                  containerPort: 6379
-                  hostPort: 6379
-              volumeMounts:
-                - name: nix-store
-                  mountPath: /nix/store
-                  readOnly: true
-              volumeMounts:
-                - name: system-profile
-                  mountPath: /bin
-                  readOnly: true
-              command:
-                - /bin/redis-server
-          volumes:
-            - name: nix-store
-              source:
-                hostDir:
-                  path: /nix/store
-            - name: system-profile
-              source:
-                hostDir:
-                  path: /run/current-system/sw/bin
-      labels:
-        name: redis
-        role: master
+  testSimplePod = ''
+    $kubernetes->execute("docker load < ${redisImage}");
+    $kubernetes->waitUntilSucceeds("kubectl create -f ${redisPod}");
+    $kubernetes->succeed("kubectl create -f ${redisService}");
+    $kubernetes->waitUntilSucceeds("kubectl get pod redis | grep Running");
+    $kubernetes->succeed("nc -z \$\(dig \@10.10.0.1 redis.default.svc.cluster.local +short\) 6379");
   '';
+in {
+  # This test runs kubernetes on a single node
+  trivial = makeTest {
+    name = "kubernetes-trivial";
 
-  nodes = {
-    master =
-      { config, pkgs, lib, nodes, ... }:
-        {
-          virtualisation.memorySize = 768;
-          services.kubernetes = {
-            roles = ["master" "node"];
-            dockerCfg = ''{"master:5000":{}}'';
-            controllerManager.machines = ["master" "node"];
-            apiserver.address = "0.0.0.0";
-            verbose = true;
-          };
-          virtualisation.docker.extraOptions = "--iptables=false --ip-masq=false -b cbr0 --insecure-registry master:5000";
+    nodes = {
+      kubernetes =
+        { config, pkgs, lib, nodes, ... }:
+          {
+            virtualisation.memorySize = 768;
+            virtualisation.diskSize = 2048;
 
-          services.etcd = {
-            listenPeerUrls = ["http://0.0.0.0:7001"];
-            initialAdvertisePeerUrls = ["http://master:7001"];
-            initialCluster = ["master=http://master:7001" "node=http://node:7001"];
-          };
-          services.dockerRegistry.enable = true;
-          services.dockerRegistry.host = "0.0.0.0";
-          services.dockerRegistry.port = 5000;
+            programs.bash.enableCompletion = true;
 
-          virtualisation.vlans = [ 1 2 ];
-          networking.bridges = {
-            cbr0.interfaces = [ "eth2" ];
-          };
-          networking.interfaces = {
-            cbr0 = {
-              ipAddress = "10.10.0.1";
-              prefixLength = 24;
-            };
-            eth2.ip4 = lib.mkOverride 0 [ ];
+            services.kubernetes.roles = ["master" "node"];
+            virtualisation.docker.extraOptions = "--iptables=false --ip-masq=false -b cbr0";
+
+            networking.bridges.cbr0.interfaces = [];
+            networking.interfaces.cbr0 = {};
           };
-          networking.localCommands = ''
-            ip route add 10.10.0.0/16 dev cbr0
-            ip route flush cache
-          '';
-          networking.extraHosts = "127.0.0.1 master";
+    };
+
+    testScript = ''
+      startAll;
+
+      $kubernetes->waitUntilSucceeds("kubectl get nodes | grep kubernetes | grep Ready");
+
+      ${testSimplePod}
+    '';
+  };
+
+  cluster = let
+    runWithOpenSSL = file: cmd: pkgs.runCommand file {
+      buildInputs = [ pkgs.openssl ];
+    } cmd;
+
+    ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
+    ca_pem = runWithOpenSSL "ca.pem" ''
+      openssl req \
+        -x509 -new -nodes -key ${ca_key} \
+        -days 10000 -out $out -subj "/CN=etcd-ca"
+    '';
+    etcd_key = runWithOpenSSL "etcd-key.pem" "openssl genrsa -out $out 2048";
+    etcd_csr = runWithOpenSSL "etcd.csr" ''
+      openssl req \
+        -new -key ${etcd_key} \
+        -out $out -subj "/CN=etcd" \
+        -config ${openssl_cnf}
+    '';
+    etcd_cert = runWithOpenSSL "etcd.pem" ''
+      openssl x509 \
+        -req -in ${etcd_csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} \
+        -CAcreateserial -out $out \
+        -days 365 -extensions v3_req \
+        -extfile ${openssl_cnf}
+    '';
+
+    etcd_client_key = runWithOpenSSL "etcd-client-key.pem"
+      "openssl genrsa -out $out 2048";
+
+    etcd_client_csr = runWithOpenSSL "etcd-client-key.pem" ''
+      openssl req \
+        -new -key ${etcd_client_key} \
+        -out $out -subj "/CN=etcd-client" \
+        -config ${client_openssl_cnf}
+    '';
+
+    etcd_client_cert = runWithOpenSSL "etcd-client.crt" ''
+      openssl x509 \
+        -req -in ${etcd_client_csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
+        -out $out -days 365 -extensions v3_req \
+        -extfile ${client_openssl_cnf}
+    '';
 
-          networking.firewall.enable = false;
-          #networking.firewall.allowedTCPPorts = [ 4001 7001 ];
+    apiserver_key = runWithOpenSSL "apiserver-key.pem" "openssl genrsa -out $out 2048";
 
-          environment.systemPackages = [ pkgs.redis ];
+    apiserver_csr = runWithOpenSSL "apiserver.csr" ''
+      openssl req \
+        -new -key ${apiserver_key} \
+        -out $out -subj "/CN=kube-apiserver" \
+        -config ${apiserver_cnf}
+    '';
+
+    apiserver_cert = runWithOpenSSL "apiserver.pem" ''
+      openssl x509 \
+        -req -in ${apiserver_csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
+        -out $out -days 365 -extensions v3_req \
+        -extfile ${apiserver_cnf}
+    '';
+
+    worker_key = runWithOpenSSL "worker-key.pem" "openssl genrsa -out $out 2048";
+
+    worker_csr = runWithOpenSSL "worker.csr" ''
+      openssl req \
+        -new -key ${worker_key} \
+        -out $out -subj "/CN=kube-worker" \
+        -config ${worker_cnf}
+    '';
+
+    worker_cert = runWithOpenSSL "worker.pem" ''
+      openssl x509 \
+        -req -in ${worker_csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
+        -out $out -days 365 -extensions v3_req \
+        -extfile ${worker_cnf}
+    '';
+
+    openssl_cnf = pkgs.writeText "openssl.cnf" ''
+      [req]
+      req_extensions = v3_req
+      distinguished_name = req_distinguished_name
+      [req_distinguished_name]
+      [ v3_req ]
+      basicConstraints = CA:FALSE
+      keyUsage = digitalSignature, keyEncipherment
+      extendedKeyUsage = serverAuth
+      subjectAltName = @alt_names
+      [alt_names]
+      DNS.1 = etcd1
+      DNS.2 = etcd2
+      DNS.3 = etcd3
+      IP.1 = 127.0.0.1
+    '';
+
+    client_openssl_cnf = pkgs.writeText "client-openssl.cnf" ''
+      [req]
+      req_extensions = v3_req
+      distinguished_name = req_distinguished_name
+      [req_distinguished_name]
+      [ v3_req ]
+      basicConstraints = CA:FALSE
+      keyUsage = digitalSignature, keyEncipherment
+      extendedKeyUsage = clientAuth
+    '';
+
+    apiserver_cnf = pkgs.writeText "apiserver-openssl.cnf" ''
+      [req]
+      req_extensions = v3_req
+      distinguished_name = req_distinguished_name
+      [req_distinguished_name]
+      [ v3_req ]
+      basicConstraints = CA:FALSE
+      keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+      subjectAltName = @alt_names
+      [alt_names]
+      DNS.1 = kubernetes
+      DNS.2 = kubernetes.default
+      DNS.3 = kubernetes.default.svc
+      DNS.4 = kubernetes.default.svc.cluster.local
+      IP.1 = 10.10.10.1
+    '';
+
+    worker_cnf = pkgs.writeText "worker-openssl.cnf" ''
+      [req]
+      req_extensions = v3_req
+      distinguished_name = req_distinguished_name
+      [req_distinguished_name]
+      [ v3_req ]
+      basicConstraints = CA:FALSE
+      keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+      subjectAltName = @alt_names
+      [alt_names]
+      DNS.1 = kubeWorker1
+      DNS.2 = kubeWorker2
+    '';
+
+    etcdNodeConfig = {
+      virtualisation.memorySize = 128;
+
+      services = {
+        etcd = {
+          enable = true;
+          keyFile = etcd_key;
+          certFile = etcd_cert;
+          trustedCaFile = ca_pem;
+          peerClientCertAuth = true;
+          listenClientUrls = ["https://0.0.0.0:2379"];
+          listenPeerUrls = ["https://0.0.0.0:2380"];
         };
+      };
 
-    node =
-      { config, pkgs, lib, nodes, ... }:
-        {
-          services.kubernetes = {
-            roles = ["node"];
-            dockerCfg = ''{"master:5000":{}}'';
-            kubelet.apiServers = ["master:8080"];
-            verbose = true;
-          };
-          virtualisation.docker.extraOptions = "--iptables=false --ip-masq=false -b cbr0 --insecure-registry master:5000";
-          services.etcd = {
-            listenPeerUrls = ["http://0.0.0.0:7001"];
-            initialAdvertisePeerUrls = ["http://node:7001"];
-            initialCluster = ["master=http://master:7001" "node=http://node:7001"];
-          };
+      environment.variables = {
+        ETCDCTL_CERT_FILE = "${etcd_client_cert}";
+        ETCDCTL_KEY_FILE = "${etcd_client_key}";
+        ETCDCTL_CA_FILE = "${ca_pem}";
+        ETCDCTL_PEERS = "https://127.0.0.1:2379";
+      };
 
-          virtualisation.vlans = [ 1 2 ];
-          networking.bridges = {
-            cbr0.interfaces = [ "eth2" ];
-          };
-          networking.interfaces = {
-            cbr0 = {
-              ipAddress = "10.10.1.1";
-              prefixLength = 24;
-            };
-            eth2.ip4 = lib.mkOverride 0 [ ];
-          };
-          networking.localCommands = ''
-            ip route add 10.10.0.0/16 dev cbr0
-            ip route flush cache
-          '';
-          networking.extraHosts = "127.0.0.1 node";
+      networking.firewall.allowedTCPPorts = [ 2379 2380 ];
+    };
 
-          networking.firewall.enable = false;
-          #networking.firewall.allowedTCPPorts = [ 4001 7001 ];
+    kubeConfig = {
+      virtualisation.diskSize = 2048;
+      programs.bash.enableCompletion = true;
 
-          environment.systemPackages = [ pkgs.redis ];
+      services.flannel = {
+        enable = true;
+        network = "10.10.0.0/16";
+        iface = "eth1";
+        etcd = {
+          endpoints = ["https://etcd1:2379" "https://etcd2:2379" "https://etcd3:2379"];
+          keyFile = etcd_client_key;
+          certFile = etcd_client_cert;
+          caFile = ca_pem;
         };
+      };
+
+      # vxlan
+      networking.firewall.allowedUDPPorts = [ 8472 ];
 
-    client =
-      { config, pkgs, nodes, ... }:
-        {
-          virtualisation.docker.enable = true;
-          virtualisation.docker.extraOptions = "--insecure-registry master:5000";
-          environment.systemPackages = [ pkgs.kubernetes ];
-          environment.etc."test/redis-master-pod.yaml".source = redisMaster;
-          environment.etc."test/pause".source = "${pkgs.kubernetes}/bin/kube-pause";
-          environment.etc."test/Dockerfile".source = pkgs.writeText "Dockerfile" ''
-            FROM scratch
-            ADD pause /
-            ENTRYPOINT ["/pause"]
-          '';
+      systemd.services.docker.after = ["flannel.service"];
+      systemd.services.docker.serviceConfig.EnvironmentFile = "/run/flannel/subnet.env";
+      virtualisation.docker.extraOptions = "--iptables=false --ip-masq=false --bip $FLANNEL_SUBNET";
+
+      services.kubernetes.verbose = true;
+      services.kubernetes.etcd = {
+        servers = ["https://etcd1:2379" "https://etcd2:2379" "https://etcd3:2379"];
+        keyFile = etcd_client_key;
+        certFile = etcd_client_cert;
+        caFile = ca_pem;
+      };
+
+      environment.systemPackages = [ pkgs.bind pkgs.tcpdump pkgs.utillinux ];
+    };
+
+    kubeMasterConfig = {pkgs, ...}: {
+      require = [kubeConfig];
+
+      # kube apiserver
+      networking.firewall.allowedTCPPorts = [ 443 ];
+
+      virtualisation.memorySize = 512;
+
+      services.kubernetes = {
+        roles = ["master"];
+        scheduler.leaderElect = true;
+        controllerManager.leaderElect = true;
+
+        apiserver = {
+          publicAddress = "0.0.0.0";
+          advertiseAddress = "192.168.1.8";
+          tlsKeyFile = apiserver_key;
+          tlsCertFile = apiserver_cert;
+          clientCaFile = ca_pem;
+          kubeletClientCaFile = ca_pem;
+          kubeletClientKeyFile = worker_key;
+          kubeletClientCertFile = worker_cert;
         };
-  };
+      };
+    };
 
-  testScript = ''
-    startAll;
-
-    $master->waitForUnit("kubernetes-apiserver.service");
-    $master->waitForUnit("kubernetes-scheduler.service");
-    $master->waitForUnit("kubernetes-controller-manager.service");
-    $master->waitForUnit("kubernetes-kubelet.service");
-    $master->waitForUnit("kubernetes-proxy.service");
-
-    $node->waitForUnit("kubernetes-kubelet.service");
-    $node->waitForUnit("kubernetes-proxy.service");
-
-    $master->waitUntilSucceeds("kubectl get minions | grep master");
-    $master->waitUntilSucceeds("kubectl get minions | grep node");
-
-    $client->waitForUnit("docker.service");
-    $client->succeed("tar cv --files-from /dev/null | docker import - nix");
-    $client->succeed("docker tag nix master:5000/nix");
-    $master->waitForUnit("docker-registry.service");
-    $client->succeed("docker push master:5000/nix");
-    $client->succeed("mkdir -p /root/pause");
-    $client->succeed("cp /etc/test/pause /root/pause/");
-    $client->succeed("cp /etc/test/Dockerfile /root/pause/");
-    $client->succeed("cd /root/pause && docker build -t master:5000/pause .");
-    $client->succeed("docker push master:5000/pause");
-
-    subtest "simple pod", sub {
-      $client->succeed("kubectl create -f ${redisMaster} -s http://master:8080");
-      $client->waitUntilSucceeds("kubectl get pods -s http://master:8080 | grep redis-master | grep -i running");
-    }
+    kubeWorkerConfig = { pkgs, ... }: {
+      require = [kubeConfig];
 
-  '';
-})
+      virtualisation.memorySize = 512;
+
+      # kubelet
+      networking.firewall.allowedTCPPorts = [ 10250 ];
+
+      services.kubernetes = {
+        roles = ["node"];
+        kubeconfig = {
+          server = "https://kubernetes:443";
+          caFile = ca_pem;
+          certFile = worker_cert;
+          keyFile = worker_key;
+        };
+        kubelet = {
+          tlsKeyFile = worker_key;
+          tlsCertFile = worker_cert;
+        };
+      };
+    };
+  in makeTest {
+    name = "kubernetes-cluster";
+
+    nodes = {
+      etcd1 = { config, pkgs, nodes, ... }: {
+        require = [etcdNodeConfig];
+        services.etcd = {
+          advertiseClientUrls = ["https://etcd1:2379"];
+          initialCluster = ["etcd1=https://etcd1:2380" "etcd2=https://etcd2:2380" "etcd3=https://etcd3:2380"];
+          initialAdvertisePeerUrls = ["https://etcd1:2380"];
+        };
+      };
+
+      etcd2 = { config, pkgs, ... }: {
+        require = [etcdNodeConfig];
+        services.etcd = {
+          advertiseClientUrls = ["https://etcd2:2379"];
+          initialCluster = ["etcd1=https://etcd1:2380" "etcd2=https://etcd2:2380" "etcd3=https://etcd3:2380"];
+          initialAdvertisePeerUrls = ["https://etcd2:2380"];
+        };
+      };
+
+      etcd3 = { config, pkgs, ... }: {
+        require = [etcdNodeConfig];
+        services.etcd = {
+          advertiseClientUrls = ["https://etcd3:2379"];
+          initialCluster = ["etcd1=https://etcd1:2380" "etcd2=https://etcd2:2380" "etcd3=https://etcd3:2380"];
+          initialAdvertisePeerUrls = ["https://etcd3:2380"];
+        };
+      };
+
+      kubeMaster1 = { config, pkgs, lib, nodes, ... }: {
+        require = [kubeMasterConfig];
+      };
+
+      kubeMaster2 = { config, pkgs, lib, nodes, ... }: {
+        require = [kubeMasterConfig];
+      };
+
+      # Kubernetes TCP load balancer
+      kubernetes = { config, pkgs, ... }: {
+        # kubernetes
+        networking.firewall.allowedTCPPorts = [ 443 ];
+
+        services.haproxy.enable = true;
+        services.haproxy.config = ''
+          global
+              log 127.0.0.1 local0 notice
+              user haproxy
+              group haproxy
+
+          defaults
+              log global
+              retries 2
+              timeout connect 3000
+              timeout server 5000
+              timeout client 5000
+
+          listen kubernetes
+              bind 0.0.0.0:443
+              mode tcp
+              option ssl-hello-chk
+              balance roundrobin
+              server kube-master-1 kubeMaster1:443 check
+              server kube-master-2 kubeMaster2:443 check
+        '';
+      };
+
+      kubeWorker1 = { config, pkgs, lib, nodes, ... }: {
+        require = [kubeWorkerConfig];
+      };
+
+      kubeWorker2 = { config, pkgs, lib, nodes, ... }: {
+        require = [kubeWorkerConfig];
+      };
+    };
+
+    testScript = ''
+      startAll;
+
+      ${testSimplePod}
+    '';
+  };
+}
diff --git a/nixos/tests/leaps.nix b/nixos/tests/leaps.nix
new file mode 100644
index 00000000000..3c390e1a169
--- /dev/null
+++ b/nixos/tests/leaps.nix
@@ -0,0 +1,29 @@
+import ./make-test.nix ({ pkgs,  ... }:
+
+{
+  name = "leaps";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ qknight ];
+  };
+
+  nodes =
+    { 
+      client = { };
+
+      server =
+        { services.leaps = {
+            enable = true;
+            port = 6666;
+            path = "/leaps/";
+          };
+          networking.firewall.enable = false;
+        };
+    };
+
+  testScript =
+    ''
+      startAll;
+      $server->waitForOpenPort(6666);
+      $client->succeed("curl http://server:6666/leaps/ | grep -i 'leaps'"); 
+    '';
+})
diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix
index e793d89567b..a6a460fb0a7 100644
--- a/nixos/tests/login.nix
+++ b/nixos/tests/login.nix
@@ -33,10 +33,11 @@ import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
 
       # Log in as alice on a virtual console.
       subtest "virtual console login", sub {
-          $machine->sleep(2); # urgh: wait for username prompt
+          $machine->waitUntilTTYMatches(2, "login: ");
           $machine->sendChars("alice\n");
+          $machine->waitUntilTTYMatches(2, "login: alice");
           $machine->waitUntilSucceeds("pgrep login");
-          $machine->sleep(2); # urgh: wait for `Password:'
+          $machine->waitUntilTTYMatches(2, "Password: ");
           $machine->sendChars("foobar\n");
           $machine->waitUntilSucceeds("pgrep -u alice bash");
           $machine->sendChars("touch done\n");
diff --git a/nixos/tests/logstash.nix b/nixos/tests/logstash.nix
index edece352caf..01f6a0358b2 100644
--- a/nixos/tests/logstash.nix
+++ b/nixos/tests/logstash.nix
@@ -25,7 +25,6 @@ import ./make-test.nix ({ pkgs, ...} : {
               '';
               outputConfig = ''
                 stdout { codec => rubydebug }
-                elasticsearch { embedded => true }
               '';
             };
           };
@@ -38,6 +37,5 @@ import ./make-test.nix ({ pkgs, ...} : {
     $one->waitForUnit("logstash.service");
     $one->waitUntilSucceeds("journalctl -n 20 _SYSTEMD_UNIT=logstash.service | grep flowers");
     $one->fail("journalctl -n 20 _SYSTEMD_UNIT=logstash.service | grep dragons");
-    $one->waitUntilSucceeds("curl -s http://127.0.0.1:9200/_status?pretty=true | grep logstash");
   '';
 })
diff --git a/nixos/tests/mongodb.nix b/nixos/tests/mongodb.nix
new file mode 100644
index 00000000000..18535f51af9
--- /dev/null
+++ b/nixos/tests/mongodb.nix
@@ -0,0 +1,34 @@
+# This test start mongodb, runs a query using mongo shell
+
+import ./make-test.nix ({ pkgs, ...} : let
+  testQuery = pkgs.writeScript "nixtest.js" ''
+    db.greetings.insert({ "greeting": "hello" });
+    print(db.greetings.findOne().greeting);
+  '';
+in {
+  name = "mongodb";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ bluescreen303 offline wkennington cstrahan rvl ];
+  };
+
+  nodes = {
+    one =
+      { config, pkgs, ... }:
+        {
+          services = {
+           mongodb.enable = true;
+           mongodb.extraConfig = ''
+             # Allow starting engine with only a small virtual disk
+             storage.journal.enabled: false
+             storage.mmapv1.smallFiles: true
+           '';
+          };
+        };
+    };
+
+  testScript = ''
+    startAll;
+    $one->waitForUnit("mongodb.service");
+    $one->succeed("mongo nixtest ${testQuery}") =~ /hello/ or die;
+  '';
+})
diff --git a/nixos/tests/mpich.nix b/nixos/tests/mpich.nix
deleted file mode 100644
index a28e41deb31..00000000000
--- a/nixos/tests/mpich.nix
+++ /dev/null
@@ -1,41 +0,0 @@
-# Simple example to showcase distributed tests using NixOS VMs.
-
-import ./make-test.nix ({ pkgs, ...} : {
-  name = "mpich";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ eelco chaoflow ];
-  };
-
-  nodes = {
-    master =
-      { config, pkgs, ... }: {
-        environment.systemPackages = [ gcc mpich2 ];
-        #boot.kernelPackages = pkgs.kernelPackages_2_6_29;
-      };
-
-    slave =
-      { config, pkgs, ... }: {
-        environment.systemPackages = [ gcc mpich2 ];
-      };
-  };
-
-  # Start master/slave MPI daemons and compile/run a program that uses both
-  # nodes.
-  testScript =
-    ''
-       startAll;
-
-       $master->succeed("echo 'MPD_SECRETWORD=secret' > /etc/mpd.conf");
-       $master->succeed("chmod 600 /etc/mpd.conf");
-       $master->succeed("mpd --daemon --ifhn=master --listenport=4444");
-
-       $slave->succeed("echo 'MPD_SECRETWORD=secret' > /etc/mpd.conf");
-       $slave->succeed("chmod 600 /etc/mpd.conf");
-       $slave->succeed("mpd --daemon --host=master --port=4444");
-
-       $master->succeed("mpicc -o example -Wall ${./mpich-example.c}");
-       $slave->succeed("mpicc -o example -Wall ${./mpich-example.c}");
-
-       $master->succeed("mpiexec -n 2 ./example >&2");
-    '';
-})
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index 35f44002699..7959b85a0cf 100644
--- a/nixos/tests/mumble.nix
+++ b/nixos/tests/mumble.nix
@@ -36,18 +36,29 @@ in
     # cancel client audio configuration
     $client1->waitForWindow(qr/Audio Tuning Wizard/);
     $client2->waitForWindow(qr/Audio Tuning Wizard/);
+    $server->sleep(5); # wait because mumble is slow to register event handlers
     $client1->sendKeys("esc");
     $client2->sendKeys("esc");
 
     # cancel client cert configuration
     $client1->waitForWindow(qr/Certificate Management/);
     $client2->waitForWindow(qr/Certificate Management/);
+    $server->sleep(5); # wait because mumble is slow to register event handlers
     $client1->sendKeys("esc");
     $client2->sendKeys("esc");
 
     # accept server certificate
     $client1->waitForWindow(qr/^Mumble$/);
     $client2->waitForWindow(qr/^Mumble$/);
+    $server->sleep(5); # wait because mumble is slow to register event handlers
+    $client1->sendChars("y");
+    $client2->sendChars("y");
+    $server->sleep(5); # wait because mumble is slow to register event handlers
+
+    # sometimes the wrong of the 2 windows is focused, we switch focus and try pressing "y" again
+    $client1->sendKeys("alt-tab");
+    $client2->sendKeys("alt-tab");
+    $server->sleep(5); # wait because mumble is slow to register event handlers
     $client1->sendChars("y");
     $client2->sendChars("y");
 
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index d5a0a9b798f..17d4a878d3a 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -41,7 +41,6 @@ let
       machine.networking.useNetworkd = networkd;
       testScript = ''
         startAll;
-        $machine->waitForUnit("network-interfaces.target");
         $machine->waitForUnit("network.target");
         $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '");
         $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '");
@@ -71,9 +70,7 @@ let
         ''
           startAll;
 
-          $client->waitForUnit("network-interfaces.target");
           $client->waitForUnit("network.target");
-          $router->waitForUnit("network-interfaces.target");
           $router->waitForUnit("network.target");
 
           # Make sure dhcpcd is not started
@@ -119,9 +116,7 @@ let
         ''
           startAll;
 
-          $client->waitForUnit("network-interfaces.target");
           $client->waitForUnit("network.target");
-          $router->waitForUnit("network-interfaces.target");
           $router->waitForUnit("network.target");
 
           # Wait until we have an ip address on each interface
@@ -164,9 +159,7 @@ let
           startAll;
 
           # Wait for networking to come up
-          $client->waitForUnit("network-interfaces.target");
           $client->waitForUnit("network.target");
-          $router->waitForUnit("network-interfaces.target");
           $router->waitForUnit("network.target");
 
           # Wait until we have an ip address on each interface
@@ -213,9 +206,7 @@ let
           startAll;
 
           # Wait for networking to come up
-          $client1->waitForUnit("network-interfaces.target");
           $client1->waitForUnit("network.target");
-          $client2->waitForUnit("network-interfaces.target");
           $client2->waitForUnit("network.target");
 
           # Test bonding
@@ -259,11 +250,8 @@ let
           startAll;
 
           # Wait for networking to come up
-          $client1->waitForUnit("network-interfaces.target");
           $client1->waitForUnit("network.target");
-          $client2->waitForUnit("network-interfaces.target");
           $client2->waitForUnit("network.target");
-          $router->waitForUnit("network-interfaces.target");
           $router->waitForUnit("network.target");
 
           # Test bridging
@@ -298,9 +286,7 @@ let
           startAll;
 
           # Wait for networking to come up
-          $client->waitForUnit("network-interfaces.target");
           $client->waitForUnit("network.target");
-          $router->waitForUnit("network-interfaces.target");
           $router->waitForUnit("network.target");
 
           # Wait until we have an ip address on each interface
@@ -348,9 +334,7 @@ let
           startAll;
 
           # Wait for networking to be configured
-          $client1->waitForUnit("network-interfaces.target");
           $client1->waitForUnit("network.target");
-          $client2->waitForUnit("network-interfaces.target");
           $client2->waitForUnit("network.target");
 
           # Print diagnostic information
@@ -391,9 +375,7 @@ let
           startAll;
 
           # Wait for networking to be configured
-          $client1->waitForUnit("network-interfaces.target");
           $client1->waitForUnit("network.target");
-          $client2->waitForUnit("network-interfaces.target");
           $client2->waitForUnit("network.target");
 
           # Test vlan is setup
diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix
index 390363b88e2..29154499688 100644
--- a/nixos/tests/openssh.nix
+++ b/nixos/tests/openssh.nix
@@ -35,6 +35,18 @@ in {
         ];
       };
 
+    server_lazy =
+      { config, pkgs, ... }:
+
+      {
+        services.openssh = { enable = true; startWhenNeeded = true; };
+        security.pam.services.sshd.limits =
+          [ { domain = "*"; item = "memlock"; type = "-"; value = 1024; } ];
+        users.extraUsers.root.openssh.authorizedKeys.keys = [
+          snakeOilPublicKey
+        ];
+      };
+
     client =
       { config, pkgs, ... }: { };
 
@@ -50,6 +62,8 @@ in {
     subtest "manual-authkey", sub {
       $server->succeed("mkdir -m 700 /root/.ssh");
       $server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
+      $server_lazy->succeed("mkdir -m 700 /root/.ssh");
+      $server_lazy->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
 
       $client->succeed("mkdir -m 700 /root/.ssh");
       $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
@@ -58,6 +72,10 @@ in {
       $client->waitForUnit("network.target");
       $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2");
       $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024");
+
+      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2");
+      $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024");
+
     };
 
     subtest "configured-authkey", sub {
@@ -66,6 +84,11 @@ in {
       $client->succeed("ssh -o UserKnownHostsFile=/dev/null" .
                        " -o StrictHostKeyChecking=no -i privkey.snakeoil" .
                        " server true");
+
+      $client->succeed("ssh -o UserKnownHostsFile=/dev/null" .
+                       " -o StrictHostKeyChecking=no -i privkey.snakeoil" .
+                       " server_lazy true");
+
     };
   '';
 })
diff --git a/nixos/tests/postgis.nix b/nixos/tests/postgis.nix
new file mode 100644
index 00000000000..1dba5c363c0
--- /dev/null
+++ b/nixos/tests/postgis.nix
@@ -0,0 +1,30 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "postgis";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lsix ];
+  };
+
+  nodes = {
+    master =
+      { pkgs, config, ... }:
+
+      {
+        services.postgresql = let mypg = pkgs.postgresql95; in {
+            enable = true;
+            package = mypg;
+            extraPlugins = [ (pkgs.postgis.override { postgresql = mypg; }).v_2_2_1 ];
+            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("sudo -u postgres psql -c 'CREATE EXTENSION postgis;'");
+  '';
+})
diff --git a/nixos/tests/prometheus.nix b/nixos/tests/prometheus.nix
new file mode 100644
index 00000000000..ade097597bb
--- /dev/null
+++ b/nixos/tests/prometheus.nix
@@ -0,0 +1,29 @@
+import ./make-test.nix {
+  name = "prometheus";
+
+  nodes = {
+    one = { config, pkgs, ... }: {
+      services.prometheus = {
+        enable = true;
+        globalConfig = {
+          labels = { foo = "bar"; };
+        };
+        scrapeConfigs = [{
+          job_name = "prometheus";
+          static_configs = [{
+            targets = [ "127.0.0.1:9090" ];
+            labels = { instance = "localhost"; };
+          }];
+        }];
+        rules = [ ''testrule = count(up{job="prometheus"})'' ];
+      };
+    };
+  };
+
+  testScript = ''
+    startAll;
+    $one->waitForUnit("prometheus.service");
+    $one->waitForOpenPort(9090);
+    $one->succeed("curl -s http://127.0.0.1:9090/metrics");
+  '';
+}
diff --git a/nixos/tests/pump.io.nix b/nixos/tests/pump.io.nix
index 89fa23c3336..18da52b5134 100644
--- a/nixos/tests/pump.io.nix
+++ b/nixos/tests/pump.io.nix
@@ -77,11 +77,11 @@ in {
            }; };
            mongodb.enable = true;
            mongodb.extraConfig = ''
-             nojournal = true
+             storage.journal.enabled: false
            '';
           };
           systemd.services.mongodb.unitConfig.Before = "pump.io.service";
-          systemd.services.mongodb.unitConfig.RequiredBy = "pump.io.service";
+          systemd.services."pump.io".unitConfig.Requires = "mongodb.service";
         };
     };
 
diff --git a/nixos/tests/quagga.nix b/nixos/tests/quagga.nix
new file mode 100644
index 00000000000..b9644b4768c
--- /dev/null
+++ b/nixos/tests/quagga.nix
@@ -0,0 +1,97 @@
+# This test runs Quagga and checks if OSPF routing works.
+#
+# Network topology:
+#   [ client ]--net1--[ router1 ]--net2--[ router2 ]--net3--[ server ]
+#
+# All interfaces are in OSPF Area 0.
+
+import ./make-test.nix ({ pkgs, ... }:
+  let
+
+    ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ip4).address;
+
+    ospfConf = ''
+      interface eth2
+        ip ospf hello-interval 1
+        ip ospf dead-interval 5
+      !
+      router ospf
+        network 192.168.0.0/16 area 0
+    '';
+
+  in
+    {
+      name = "quagga";
+
+      meta = with pkgs.stdenv.lib.maintainers; {
+        maintainers = [ tavyc ];
+      };
+
+      nodes = {
+
+        client =
+          { config, pkgs, nodes, ... }:
+          {
+            virtualisation.vlans = [ 1 ];
+            networking.defaultGateway = ifAddr nodes.router1 "eth1";
+          };
+
+        router1 =
+          { config, pkgs, nodes, ... }:
+          {
+            virtualisation.vlans = [ 1 2 ];
+            boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
+            networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
+            services.quagga.ospf = {
+              enable = true;
+              config = ospfConf;
+            };
+          };
+
+        router2 =
+          { config, pkgs, nodes, ... }:
+          {
+            virtualisation.vlans = [ 3 2 ];
+            boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
+            networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
+            services.quagga.ospf = {
+              enable = true;
+              config = ospfConf;
+            };
+          };
+
+        server =
+          { config, pkgs, nodes, ... }:
+          {
+            virtualisation.vlans = [ 3 ];
+            networking.defaultGateway = ifAddr nodes.router2 "eth1";
+            networking.firewall.allowedTCPPorts = [ 80 ];
+            networking.firewall.allowPing = true;
+            services.httpd.enable = true;
+            services.httpd.adminAddr = "foo@example.com";
+          };
+      };
+
+      testScript =
+        { nodes, ... }:
+        ''
+          startAll;
+
+          # Wait for the networking to start on all machines
+          $_->waitForUnit("network.target") foreach values %vms;
+
+          # Wait for OSPF to form adjacencies
+          for my $gw ($router1, $router2) {
+              $gw->waitForUnit("ospfd");
+              $gw->waitUntilSucceeds("vtysh -c 'show ip ospf neighbor' | grep Full");
+              $gw->waitUntilSucceeds("vtysh -c 'show ip route' | grep '^O>'");
+          }
+
+          # Test ICMP.
+          $client->succeed("ping -c 3 server >&2");
+
+          # Test whether HTTP works.
+          $server->waitForUnit("httpd");
+          $client->succeed("curl --fail http://server/ >&2");
+        '';
+    })
diff --git a/nixos/tests/samba.nix b/nixos/tests/samba.nix
new file mode 100644
index 00000000000..d6658ef0400
--- /dev/null
+++ b/nixos/tests/samba.nix
@@ -0,0 +1,48 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+{
+  name = "samba";
+
+  meta.maintainers = [ pkgs.lib.maintainers.eelco ];
+
+  nodes =
+    { client =
+        { config, pkgs, ... }:
+        { fileSystems = pkgs.lib.mkVMOverride
+            { "/public" = {
+                fsType = "cifs";
+                device = "//server/public";
+                options = [ "guest" ];
+              };
+            };
+        };
+
+      server =
+        { config, pkgs, ... }:
+        { services.samba.enable = true;
+          services.samba.shares.public =
+            { path = "/public";
+              "read only" = true;
+              browseable = "yes";
+              "guest ok" = "yes";
+              comment = "Public samba share.";
+            };
+          networking.firewall.allowedTCPPorts = [ 139 445 ];
+          networking.firewall.allowedUDPPorts = [ 137 138 ];
+        };
+    };
+
+  # client# [    4.542997] mount[777]: sh: systemd-ask-password: command not found
+
+  testScript =
+    ''
+      $server->start;
+      $server->waitForUnit("samba-smbd");
+      $server->waitForUnit("samba-nmbd");
+      $server->succeed("mkdir -p /public; echo bar > /public/foo");
+
+      $client->start;
+      $client->waitForUnit("network.target");
+      $client->succeed("[[ \$(cat /public/foo) = bar ]]");
+    '';
+})
diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix
index 22a9e1bd2c7..041d88fbeae 100644
--- a/nixos/tests/sddm.nix
+++ b/nixos/tests/sddm.nix
@@ -19,8 +19,6 @@ import ./make-test.nix ({ pkgs, ...} : {
     services.xserver.desktopManager.default = "none";
   };
 
-  enableOCR = true;
-
   testScript = { nodes, ... }: ''
     startAll;
     $machine->waitForFile("/home/alice/.Xauthority");
diff --git a/nixos/tests/smokeping.nix b/nixos/tests/smokeping.nix
index 324f83147e0..9de3030417f 100644
--- a/nixos/tests/smokeping.nix
+++ b/nixos/tests/smokeping.nix
@@ -11,9 +11,10 @@ import ./make-test.nix ({ pkgs, ...} : {
         services.smokeping = {
           enable = true;
           port = 8081;
+          mailHost = "127.0.0.2";
           probeConfig = ''
             + FPing
-            binary = ${pkgs.fping}/bin/fping
+            binary = /var/setuid-wrappers/fping
             offset = 0%
           '';
         };
@@ -27,5 +28,6 @@ import ./make-test.nix ({ pkgs, ...} : {
     $sm->waitForFile("/var/lib/smokeping/data/Local/LocalMachine.rrd");
     $sm->succeed("curl -s -f localhost:8081/smokeping.fcgi?target=Local");
     $sm->succeed("ls /var/lib/smokeping/cache/Local/LocalMachine_mini.png");
+    $sm->succeed("ls /var/lib/smokeping/cache/index.html");
   '';
 })
diff --git a/nixos/tests/test-config-examples.sh b/nixos/tests/test-config-examples.sh
deleted file mode 100755
index 1ba2f841c41..00000000000
--- a/nixos/tests/test-config-examples.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-# This script try to evaluate all configurations which are stored in
-# doc/config-examples.  This script is useful to ensure that examples are
-# working with the current system.
-
-pwd=$(pwd)
-set -xe
-for i in ../doc/config-examples/*.nix; do
-  NIXOS_CONFIG="$pwd/$i" nix-instantiate \
-      --eval-only --xml --strict > /dev/null 2>&1 \
-      ../default.nix -A system
-done
-set +xe
diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix
index 92680d82ba8..475c947e72d 100644
--- a/nixos/tests/tomcat.nix
+++ b/nixos/tests/tomcat.nix
@@ -23,9 +23,8 @@ import ./make-test.nix ({ pkgs, ...} : {
     startAll;
 
     $server->waitForUnit("tomcat");
-    $server->sleep(30); # Dirty, but it takes a while before Tomcat handles to requests properly
     $client->waitForUnit("network.target");
-    $client->succeed("curl --fail http://server/examples/servlets/servlet/HelloWorldExample");
-    $client->succeed("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp");
+    $client->waitUntilSucceeds("curl --fail http://server/examples/servlets/servlet/HelloWorldExample");
+    $client->waitUntilSucceeds("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp");
   '';
 })
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index ab4d46ab7e1..376c4f21dc0 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -11,10 +11,10 @@ let
       #!${pkgs.stdenv.shell} -xe
       export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.utillinux ]}"
 
-      mkdir -p /var/run/dbus
+      mkdir -p /run/dbus
       cat > /etc/passwd <<EOF
       root:x:0:0::/root:/bin/false
-      messagebus:x:1:1::/var/run/dbus:/bin/false
+      messagebus:x:1:1::/run/dbus:/bin/false
       EOF
       cat > /etc/group <<EOF
       root:x:0:
@@ -144,6 +144,7 @@ let
       "--uart1 0x3F8 4"
       "--uartmode1 client /run/virtualbox-log-${name}.sock"
       "--memory 768"
+      "--audio none"
     ] ++ (attrs.vmFlags or []));
 
     controllerFlags = mkFlags [
@@ -273,9 +274,12 @@ let
 
       sub shutdownVM_${name} {
         $machine->succeed(ru "touch ${sharePath}/shutdown");
-        $machine->waitUntilSucceeds(
-          "test ! -e ${sharePath}/shutdown ".
-          "  -a ! -e ${sharePath}/boot-done"
+        $machine->execute(
+          'set -e; i=0; '.
+          'while test -e ${sharePath}/shutdown '.
+          '        -o -e ${sharePath}/boot-done; do '.
+          'sleep 1; i=$(($i + 1)); [ $i -le 3600 ]; '.
+          'done'
         );
         waitForShutdown_${name};
       }
@@ -295,9 +299,9 @@ let
       -pf /run/dhclient.pid \
       -v eth0 eth1
 
-    otherIP="$(${pkgs.netcat}/bin/netcat -clp 1234 || :)"
+    otherIP="$(${pkgs.netcat}/bin/nc -l 1234 || :)"
     ${pkgs.iputils}/bin/ping -I eth1 -c1 "$otherIP"
-    echo "$otherIP reachable" | ${pkgs.netcat}/bin/netcat -clp 5678 || :
+    echo "$otherIP reachable" | ${pkgs.netcat}/bin/nc -l 5678 || :
   '';
 
   sysdDetectVirt = pkgs: ''
@@ -386,6 +390,7 @@ in mapAttrs mkVBoxTest {
     $machine->sendKeys("ctrl-q");
     $machine->sleep(5);
     $machine->screenshot("gui_manager_stopped");
+    destroyVM_simple;
   '';
 
   simple-cli = ''
@@ -403,6 +408,7 @@ in mapAttrs mkVBoxTest {
     });
 
     shutdownVM_simple;
+    destroyVM_simple;
   '';
 
   headless = ''
@@ -411,6 +417,7 @@ in mapAttrs mkVBoxTest {
     waitForStartup_headless;
     waitForVMBoot_headless;
     shutdownVM_headless;
+    destroyVM_headless;
   '';
 
   host-usb-permissions = ''
@@ -454,11 +461,11 @@ in mapAttrs mkVBoxTest {
     my $test1IP = waitForIP_test1 1;
     my $test2IP = waitForIP_test2 1;
 
-    $machine->succeed("echo '$test2IP' | netcat -c '$test1IP' 1234");
-    $machine->succeed("echo '$test1IP' | netcat -c '$test2IP' 1234");
+    $machine->succeed("echo '$test2IP' | nc '$test1IP' 1234");
+    $machine->succeed("echo '$test1IP' | nc '$test2IP' 1234");
 
-    $machine->waitUntilSucceeds("netcat -c '$test1IP' 5678 >&2");
-    $machine->waitUntilSucceeds("netcat -c '$test2IP' 5678 >&2");
+    $machine->waitUntilSucceeds("nc '$test1IP' 5678 >&2");
+    $machine->waitUntilSucceeds("nc '$test2IP' 5678 >&2");
 
     shutdownVM_test1;
     shutdownVM_test2;
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
new file mode 100644
index 00000000000..afee1f7f6dd
--- /dev/null
+++ b/nixos/tests/wordpress.nix
@@ -0,0 +1,60 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+{
+  name = "wordpress";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ grahamc ]; # under duress!
+  };
+
+  nodes =
+    { 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.httpd = {
+            enable = true;
+            logPerVirtualHost = true;
+            adminAddr="js@lastlog.de";
+            extraModules = [
+              { name = "php7"; path = "${pkgs.php}/modules/libphp7.so"; }
+            ];
+
+            virtualHosts = [
+              {
+                hostName = "wordpress";
+                extraSubservices =
+                  [
+                    {
+                      serviceType = "wordpress";
+                      dbPassword = "wordpress";
+                      wordpressUploads = "/data/uploads";
+                      languages = [ "de_DE" "en_GB" ];
+                    }
+                  ];
+              }
+            ];
+          };
+        };
+    };
+
+  testScript =
+    { nodes, ... }:
+    ''
+      startAll;
+
+      $web->waitForUnit("mysql");
+      $web->waitForUnit("httpd");
+
+      $web->succeed("curl -L 127.0.0.1:80 | grep 'Welcome to the famous'");
+
+
+    '';
+
+})