summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorMatthew Justin Bauer <mjbauer95@gmail.com>2018-04-21 14:53:23 -0500
committerGitHub <noreply@github.com>2018-04-21 14:53:23 -0500
commit1eea73eaa8185bd6f4fe2a0facc90652277ea399 (patch)
treefa5e6aaccbdf1ef9f4e71d0f74225252a5c2168c /nixos/modules
parente70d293b6b5f236d69d559ccccdafba19c6d29c3 (diff)
parent17b73ff90cf9f7631e543d880540441bdf56d719 (diff)
downloadnixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar.gz
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar.bz2
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar.lz
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar.xz
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.tar.zst
nixlib-1eea73eaa8185bd6f4fe2a0facc90652277ea399.zip
Merge branch 'master' into feat/acme/delay
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/i18n.nix2
-rw-r--r--nixos/modules/config/networking.nix4
-rw-r--r--nixos/modules/config/no-x-libs.nix16
-rw-r--r--nixos/modules/config/nsswitch.nix18
-rw-r--r--nixos/modules/config/power-management.nix3
-rw-r--r--nixos/modules/config/pulseaudio.nix13
-rw-r--r--nixos/modules/config/shells-environment.nix2
-rw-r--r--nixos/modules/config/system-path.nix1
-rw-r--r--nixos/modules/config/users-groups.nix55
-rw-r--r--nixos/modules/config/zram.nix2
-rw-r--r--nixos/modules/hardware/all-firmware.nix3
-rw-r--r--nixos/modules/hardware/digitalbitbox.nix30
-rw-r--r--nixos/modules/hardware/onlykey.nix33
-rw-r--r--nixos/modules/hardware/onlykey.udev4
-rw-r--r--nixos/modules/hardware/opengl.nix161
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix16
-rw-r--r--nixos/modules/hardware/video/nvidia.nix21
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix6
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix70
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-aarch64.nix13
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix11
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image.nix34
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball.nix6
-rw-r--r--nixos/modules/installer/netboot/netboot.nix21
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix7
-rw-r--r--nixos/modules/installer/tools/nixos-enter.sh60
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl7
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh199
-rw-r--r--nixos/modules/installer/tools/nixos-prepare-root.sh104
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-version.sh4
-rw-r--r--nixos/modules/installer/tools/tools.nix36
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/documentation.nix77
-rw-r--r--nixos/modules/misc/ids.nix44
-rw-r--r--nixos/modules/misc/label.nix72
-rw-r--r--nixos/modules/misc/locate.nix17
-rw-r--r--nixos/modules/misc/nixpkgs.nix123
-rw-r--r--nixos/modules/misc/version.nix78
-rw-r--r--nixos/modules/module-list.nix82
-rw-r--r--nixos/modules/profiles/all-hardware.nix5
-rw-r--r--nixos/modules/profiles/base.nix2
-rw-r--r--nixos/modules/profiles/clone-config.nix2
-rw-r--r--nixos/modules/profiles/demo.nix6
-rw-r--r--nixos/modules/profiles/docker-container.nix4
-rw-r--r--nixos/modules/profiles/installation-device.nix8
-rw-r--r--nixos/modules/profiles/minimal.nix5
-rw-r--r--nixos/modules/profiles/qemu-guest.nix2
-rw-r--r--nixos/modules/programs/adb.nix1
-rw-r--r--nixos/modules/programs/bash/bash.nix19
-rw-r--r--nixos/modules/programs/browserpass.nix1
-rw-r--r--nixos/modules/programs/ccache.nix83
-rw-r--r--nixos/modules/programs/chromium.nix8
-rw-r--r--nixos/modules/programs/criu.nix26
-rw-r--r--nixos/modules/programs/dconf.nix16
-rw-r--r--nixos/modules/programs/digitalbitbox/default.nix39
-rw-r--r--nixos/modules/programs/digitalbitbox/doc.xml85
-rw-r--r--nixos/modules/programs/iftop.nix18
-rw-r--r--nixos/modules/programs/info.nix30
-rw-r--r--nixos/modules/programs/less.nix131
-rw-r--r--nixos/modules/programs/man.nix31
-rw-r--r--nixos/modules/programs/plotinus.nix36
-rw-r--r--nixos/modules/programs/plotinus.xml25
-rw-r--r--nixos/modules/programs/rootston.nix103
-rw-r--r--nixos/modules/programs/shadow.nix10
-rw-r--r--nixos/modules/programs/singularity.nix20
-rw-r--r--nixos/modules/programs/ssh.nix2
-rw-r--r--nixos/modules/programs/sway.nix54
-rw-r--r--nixos/modules/programs/systemtap.nix28
-rw-r--r--nixos/modules/programs/tmux.nix18
-rw-r--r--nixos/modules/programs/udevil.nix14
-rw-r--r--nixos/modules/programs/way-cooler.nix78
-rw-r--r--nixos/modules/programs/yabar.nix149
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix16
-rw-r--r--nixos/modules/programs/zsh/zsh-autoenv.nix28
-rw-r--r--nixos/modules/programs/zsh/zsh.nix7
-rw-r--r--nixos/modules/rename.nix50
-rw-r--r--nixos/modules/security/acme.nix61
-rw-r--r--nixos/modules/security/audit.nix6
-rw-r--r--nixos/modules/security/duosec.nix4
-rw-r--r--nixos/modules/security/pam.nix50
-rw-r--r--nixos/modules/security/sudo.nix135
-rw-r--r--nixos/modules/security/wrappers/default.nix4
-rw-r--r--nixos/modules/security/wrappers/wrapper.c2
-rw-r--r--nixos/modules/services/audio/alsa.nix2
-rw-r--r--nixos/modules/services/audio/mopidy.nix14
-rw-r--r--nixos/modules/services/backup/almir.nix173
-rw-r--r--nixos/modules/services/backup/borgbackup.nix580
-rw-r--r--nixos/modules/services/backup/crashplan-small-business.nix74
-rw-r--r--nixos/modules/services/backup/duplicati.nix40
-rw-r--r--nixos/modules/services/backup/restic.nix150
-rw-r--r--nixos/modules/services/backup/tarsnap.nix69
-rw-r--r--nixos/modules/services/backup/znapzend.nix369
-rw-r--r--nixos/modules/services/cluster/kubernetes/dashboard.nix4
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix22
-rw-r--r--nixos/modules/services/computing/slurm/slurm.nix31
-rw-r--r--nixos/modules/services/continuous-integration/buildkite-agent.nix181
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix13
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix9
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix2
-rw-r--r--nixos/modules/services/databases/4store.nix2
-rw-r--r--nixos/modules/services/databases/memcached.nix57
-rw-r--r--nixos/modules/services/databases/mysql.nix23
-rw-r--r--nixos/modules/services/databases/openldap.nix52
-rw-r--r--nixos/modules/services/databases/pgmanage.nix6
-rw-r--r--nixos/modules/services/databases/postgresql.nix7
-rw-r--r--nixos/modules/services/databases/redis.nix1
-rw-r--r--nixos/modules/services/desktops/gnome3/at-spi2-core.nix21
-rw-r--r--nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix27
-rw-r--r--nixos/modules/services/desktops/gnome3/evolution-data-server.nix6
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-keyring.nix4
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix4
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix6
-rw-r--r--nixos/modules/services/desktops/gnome3/tracker-miners.nix41
-rw-r--r--nixos/modules/services/desktops/pipewire.nix23
-rw-r--r--nixos/modules/services/desktops/telepathy.nix4
-rw-r--r--nixos/modules/services/editors/emacs.nix23
-rw-r--r--nixos/modules/services/games/factorio.nix20
-rw-r--r--nixos/modules/services/games/ghost-one.nix105
-rw-r--r--nixos/modules/services/hardware/acpid.nix40
-rw-r--r--nixos/modules/services/hardware/amd-hybrid-graphics.nix46
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix12
-rw-r--r--nixos/modules/services/hardware/fwupd.nix90
-rw-r--r--nixos/modules/services/hardware/nvidia-optimus.nix2
-rw-r--r--nixos/modules/services/hardware/thinkfan.nix2
-rw-r--r--nixos/modules/services/hardware/trezord.nix2
-rw-r--r--nixos/modules/services/hardware/u2f.nix23
-rw-r--r--nixos/modules/services/hardware/udev.nix2
-rw-r--r--nixos/modules/services/hardware/usbmuxd.nix74
-rw-r--r--nixos/modules/services/logging/graylog.nix2
-rw-r--r--nixos/modules/services/logging/logcheck.nix2
-rw-r--r--nixos/modules/services/logging/logstash.nix2
-rw-r--r--nixos/modules/services/mail/clamsmtp.nix179
-rw-r--r--nixos/modules/services/mail/dkimproxy-out.nix118
-rw-r--r--nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixos/modules/services/mail/postfix.nix145
-rw-r--r--nixos/modules/services/mail/rspamd.nix268
-rw-r--r--nixos/modules/services/misc/defaultUnicornConfig.rb240
-rw-r--r--nixos/modules/services/misc/disnix.nix16
-rw-r--r--nixos/modules/services/misc/dysnomia.nix68
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix2
-rw-r--r--nixos/modules/services/misc/geoip-updater.nix2
-rw-r--r--nixos/modules/services/misc/gitea.nix56
-rw-r--r--nixos/modules/services/misc/gitit.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix26
-rw-r--r--nixos/modules/services/misc/gitolite.nix2
-rw-r--r--nixos/modules/services/misc/gitweb.nix59
-rw-r--r--nixos/modules/services/misc/gogs.nix3
-rw-r--r--nixos/modules/services/misc/gollum.nix21
-rw-r--r--nixos/modules/services/misc/home-assistant.nix144
-rw-r--r--nixos/modules/services/misc/ihaskell.nix2
-rw-r--r--nixos/modules/services/misc/logkeys.nix2
-rw-r--r--nixos/modules/services/misc/matrix-synapse.nix102
-rw-r--r--nixos/modules/services/misc/mbpfan.nix8
-rw-r--r--nixos/modules/services/misc/mesos-slave.nix4
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix69
-rw-r--r--nixos/modules/services/misc/nix-ssh-serve.nix24
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix18
-rw-r--r--nixos/modules/services/misc/novacomd.nix31
-rw-r--r--nixos/modules/services/misc/osrm.nix85
-rw-r--r--nixos/modules/services/misc/parsoid.nix4
-rw-r--r--nixos/modules/services/misc/safeeyes.nix50
-rw-r--r--nixos/modules/services/misc/serviio.nix92
-rw-r--r--nixos/modules/services/misc/ssm-agent.nix4
-rw-r--r--nixos/modules/services/misc/xmr-stak.nix73
-rw-r--r--nixos/modules/services/misc/zookeeper.nix11
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/fusion-inventory.nix3
-rw-r--r--nixos/modules/services/monitoring/grafana.nix20
-rw-r--r--nixos/modules/services/monitoring/monit.nix16
-rw-r--r--nixos/modules/services/monitoring/munin.nix68
-rw-r--r--nixos/modules/services/monitoring/netdata.nix54
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix10
-rw-r--r--nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix68
-rw-r--r--nixos/modules/services/monitoring/prometheus/collectd-exporter.nix128
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix173
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.xml135
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/collectd.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix50
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/json.nix36
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/minio.nix65
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix31
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/node.nix39
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix81
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/snmp.nix71
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unifi.nix67
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/varnish.nix21
-rw-r--r--nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix76
-rw-r--r--nixos/modules/services/monitoring/prometheus/json-exporter.nix74
-rw-r--r--nixos/modules/services/monitoring/prometheus/minio-exporter.nix117
-rw-r--r--nixos/modules/services/monitoring/prometheus/nginx-exporter.nix78
-rw-r--r--nixos/modules/services/monitoring/prometheus/node-exporter.nix87
-rw-r--r--nixos/modules/services/monitoring/prometheus/snmp-exporter.nix127
-rw-r--r--nixos/modules/services/monitoring/prometheus/unifi-exporter.nix105
-rw-r--r--nixos/modules/services/monitoring/prometheus/varnish-exporter.nix61
-rw-r--r--nixos/modules/services/monitoring/smartd.nix20
-rw-r--r--nixos/modules/services/monitoring/statsd.nix31
-rw-r--r--nixos/modules/services/network-filesystems/beegfs.nix343
-rw-r--r--nixos/modules/services/network-filesystems/ceph.nix371
-rw-r--r--nixos/modules/services/network-filesystems/davfs2.nix74
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix14
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix99
-rw-r--r--nixos/modules/services/network-filesystems/openafs/client.nix239
-rw-r--r--nixos/modules/services/network-filesystems/openafs/lib.nix28
-rw-r--r--nixos/modules/services/network-filesystems/openafs/server.nix260
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix14
-rw-r--r--nixos/modules/services/network-filesystems/xtreemfs.nix2
-rw-r--r--nixos/modules/services/network-filesystems/yandex-disk.nix4
-rw-r--r--nixos/modules/services/networking/amuled.nix2
-rw-r--r--nixos/modules/services/networking/aria2.nix10
-rw-r--r--nixos/modules/services/networking/bird.nix23
-rw-r--r--nixos/modules/services/networking/connman.nix11
-rw-r--r--nixos/modules/services/networking/dante.nix2
-rw-r--r--nixos/modules/services/networking/ddclient.nix112
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix6
-rw-r--r--nixos/modules/services/networking/dhcpd.nix1
-rw-r--r--nixos/modules/services/networking/dnscache.nix31
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix6
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix10
-rw-r--r--nixos/modules/services/networking/firefox/sync-server.nix52
-rw-r--r--nixos/modules/services/networking/firewall.nix5
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix2
-rw-r--r--nixos/modules/services/networking/freeradius.nix72
-rw-r--r--nixos/modules/services/networking/gnunet.nix2
-rw-r--r--nixos/modules/services/networking/i2pd.nix17
-rw-r--r--nixos/modules/services/networking/iwd.nix2
-rw-r--r--nixos/modules/services/networking/kresd.nix48
-rw-r--r--nixos/modules/services/networking/lldpd.nix1
-rw-r--r--nixos/modules/services/networking/monero.nix238
-rw-r--r--nixos/modules/services/networking/mosquitto.nix16
-rw-r--r--nixos/modules/services/networking/murmur.nix3
-rw-r--r--nixos/modules/services/networking/nat.nix80
-rw-r--r--nixos/modules/services/networking/networkmanager.nix48
-rw-r--r--nixos/modules/services/networking/nftables.nix2
-rw-r--r--nixos/modules/services/networking/nix-serve.nix2
-rw-r--r--nixos/modules/services/networking/nixops-dns.nix79
-rw-r--r--nixos/modules/services/networking/nsd.nix6
-rw-r--r--nixos/modules/services/networking/openvpn.nix30
-rw-r--r--nixos/modules/services/networking/prosody.nix337
-rw-r--r--nixos/modules/services/networking/quagga.nix2
-rw-r--r--nixos/modules/services/networking/radvd.nix15
-rw-r--r--nixos/modules/services/networking/rdnssd.nix2
-rw-r--r--nixos/modules/services/networking/resilio.nix9
-rw-r--r--nixos/modules/services/networking/rxe.nix63
-rw-r--r--nixos/modules/services/networking/shadowsocks.nix112
-rw-r--r--nixos/modules/services/networking/softether.nix33
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix94
-rw-r--r--nixos/modules/services/networking/strongswan.nix23
-rw-r--r--nixos/modules/services/networking/stunnel.nix221
-rw-r--r--nixos/modules/services/networking/syncthing.nix30
-rw-r--r--nixos/modules/services/networking/tcpcrypt.nix10
-rw-r--r--nixos/modules/services/networking/tinc.nix2
-rw-r--r--nixos/modules/services/networking/tox-bootstrapd.nix2
-rw-r--r--nixos/modules/services/networking/unbound.nix2
-rw-r--r--nixos/modules/services/networking/zerotierone.nix22
-rw-r--r--nixos/modules/services/printing/cupsd.nix7
-rw-r--r--nixos/modules/services/scheduling/fcron.nix28
-rw-r--r--nixos/modules/services/search/elasticsearch.nix15
-rw-r--r--nixos/modules/services/security/clamav.nix13
-rw-r--r--nixos/modules/services/security/hologram-agent.nix7
-rw-r--r--nixos/modules/services/security/hologram-server.nix38
-rw-r--r--nixos/modules/services/security/physlock.nix64
-rw-r--r--nixos/modules/services/security/tor.nix138
-rw-r--r--nixos/modules/services/security/torify.nix2
-rw-r--r--nixos/modules/services/security/torsocks.nix2
-rw-r--r--nixos/modules/services/security/usbguard.nix4
-rw-r--r--nixos/modules/services/system/localtime.nix60
-rw-r--r--nixos/modules/services/torrent/transmission.nix22
-rw-r--r--nixos/modules/services/ttys/agetty.nix4
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix2
-rw-r--r--nixos/modules/services/web-apps/matomo-doc.xml (renamed from nixos/modules/services/web-apps/piwik-doc.xml)42
-rw-r--r--nixos/modules/services/web-apps/matomo.nix (renamed from nixos/modules/services/web-apps/piwik.nix)75
-rw-r--r--nixos/modules/services/web-apps/nexus.nix4
-rw-r--r--nixos/modules/services/web-apps/nixbot.nix149
-rw-r--r--nixos/modules/services/web-apps/pump.io-configure.js23
-rw-r--r--nixos/modules/services/web-apps/pump.io.nix438
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix384
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix73
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/owncloud.nix48
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix2
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix3
-rw-r--r--nixos/modules/services/web-servers/lighttpd/gitweb.nix35
-rw-r--r--nixos/modules/services/web-servers/lighttpd/inginious.nix2
-rw-r--r--nixos/modules/services/web-servers/mighttpd2.nix132
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix28
-rw-r--r--nixos/modules/services/web-servers/nginx/gitweb.nix61
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix30
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix382
-rw-r--r--nixos/modules/services/web-servers/traefik.nix12
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix30
-rw-r--r--nixos/modules/services/x11/compton.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix24
-rw-r--r--nixos/modules/services/x11/desktop-managers/lxqt.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix50
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix36
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix146
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix45
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix13
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix35
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix23
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix2
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix9
-rw-r--r--nixos/modules/services/x11/window-managers/2bwm.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/awesome.nix11
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix5
-rw-r--r--nixos/modules/services/x11/window-managers/evilwm.nix25
-rw-r--r--nixos/modules/services/x11/xautolock.nix28
-rw-r--r--nixos/modules/services/x11/xserver.nix46
-rw-r--r--nixos/modules/system/activation/activation-script.nix11
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl13
-rw-r--r--nixos/modules/system/activation/top-level.nix19
-rw-r--r--nixos/modules/system/boot/binfmt.nix139
-rw-r--r--nixos/modules/system/boot/grow-partition.nix50
-rw-r--r--nixos/modules/system/boot/initrd-network.nix18
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix5
-rw-r--r--nixos/modules/system/boot/kernel.nix30
-rw-r--r--nixos/modules/system/boot/kexec.nix37
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix16
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl2
-rw-r--r--nixos/modules/system/boot/luksroot.nix31
-rw-r--r--nixos/modules/system/boot/modprobe.nix2
-rw-r--r--nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixos/modules/system/boot/plymouth.nix9
-rw-r--r--nixos/modules/system/boot/resolved.nix2
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh1
-rw-r--r--nixos/modules/system/boot/stage-1.nix56
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh9
-rw-r--r--nixos/modules/system/boot/stage-2.nix1
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix2
-rw-r--r--nixos/modules/system/boot/systemd.nix50
-rw-r--r--nixos/modules/tasks/filesystems/btrfs.nix113
-rw-r--r--nixos/modules/tasks/filesystems/exfat.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix33
-rw-r--r--nixos/modules/tasks/kbd.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix97
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix35
-rw-r--r--nixos/modules/tasks/network-interfaces.nix205
-rw-r--r--nixos/modules/testing/test-instrumentation.nix21
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix7
-rw-r--r--nixos/modules/virtualisation/amazon-init.nix2
-rw-r--r--nixos/modules/virtualisation/azure-agent.nix10
-rw-r--r--nixos/modules/virtualisation/brightbox-image.nix2
-rw-r--r--nixos/modules/virtualisation/container-config.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix27
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix50
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix18
-rw-r--r--nixos/modules/virtualisation/grow-partition.nix51
-rw-r--r--nixos/modules/virtualisation/hyperv-guest.nix37
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix31
-rw-r--r--nixos/modules/virtualisation/lxc.nix5
-rw-r--r--nixos/modules/virtualisation/lxcfs.nix4
-rw-r--r--nixos/modules/virtualisation/lxd.nix67
-rw-r--r--nixos/modules/virtualisation/nova-config.nix4
-rw-r--r--nixos/modules/virtualisation/nova.nix174
-rw-r--r--nixos/modules/virtualisation/openstack/common.nix84
-rw-r--r--nixos/modules/virtualisation/openstack/glance.nix245
-rw-r--r--nixos/modules/virtualisation/openstack/keystone.nix220
-rw-r--r--nixos/modules/virtualisation/openvswitch.nix2
-rw-r--r--nixos/modules/virtualisation/parallels-guest.nix74
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix20
-rw-r--r--nixos/modules/virtualisation/virtualbox-host.nix2
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix12
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix37
373 files changed, 12333 insertions, 5454 deletions
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 46b22fc12854..6bf8c653e113 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -10,7 +10,7 @@ with lib;
     i18n = {
       glibcLocales = mkOption {
         type = types.path;
-        default = pkgs.glibcLocales.override {
+        default = pkgs.buildPackages.glibcLocales.override {
           allLocales = any (x: x == "all") config.i18n.supportedLocales;
           locales = config.i18n.supportedLocales;
         };
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 619f36cd5150..4101ef82f3e1 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -290,8 +290,8 @@ in
           ln -s /run/systemd/resolve/resolv.conf /run/resolvconf/interfaces/systemd
         ''}
 
-        # Make sure resolv.conf is up to date if not managed by systemd
-        ${optionalString (!config.services.resolved.enable) ''
+        # Make sure resolv.conf is up to date if not managed manually or by systemd
+        ${optionalString (!config.environment.etc?"resolv.conf") ''
           ${pkgs.openresolv}/bin/resolvconf -u
         ''}
       '';
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index ae3e17ac27b6..a20910353f34 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -28,14 +28,14 @@ with lib;
 
     nixpkgs.config.packageOverrides = pkgs: {
       dbus = pkgs.dbus.override { x11Support = false; };
-      networkmanager_fortisslvpn = pkgs.networkmanager_fortisslvpn.override { withGnome = false; };
-      networkmanager_l2tp = pkgs.networkmanager_l2tp.override { withGnome = false; };
-      networkmanager_openconnect = pkgs.networkmanager_openconnect.override { withGnome = false; };
-      networkmanager_openvpn = pkgs.networkmanager_openvpn.override { withGnome = false; };
-      networkmanager_pptp = pkgs.networkmanager_pptp.override { withGnome = false; };
-      networkmanager_vpnc = pkgs.networkmanager_vpnc.override { withGnome = false; };
-      networkmanager_iodine = pkgs.networkmanager_iodine.override { withGnome = false; };
-      pinentry = pkgs.pinentry.override { gtk2 = null; qt4 = null; };
+      networkmanager-fortisslvpn = pkgs.networkmanager-fortisslvpn.override { withGnome = false; };
+      networkmanager-l2tp = pkgs.networkmanager-l2tp.override { withGnome = false; };
+      networkmanager-openconnect = pkgs.networkmanager-openconnect.override { withGnome = false; };
+      networkmanager-openvpn = pkgs.networkmanager-openvpn.override { withGnome = false; };
+      networkmanager-vpnc = pkgs.networkmanager-vpnc.override { withGnome = false; };
+      networkmanager-iodine = pkgs.networkmanager-iodine.override { withGnome = false; };
+      pinentry = pkgs.pinentry_ncurses;
+      gobjectIntrospection = pkgs.gobjectIntrospection.override { x11Support = false; };
     };
   };
 }
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index 7b36d4f1cbdf..c595c6932946 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -17,23 +17,23 @@ let
   resolved = canLoadExternalModules && config.services.resolved.enable;
 
   hostArray = [ "files" ]
-    ++ optionals mymachines [ "mymachines" ]
-    ++ optionals nssmdns [ "mdns_minimal [NOTFOUND=return]" ]
-    ++ optionals nsswins [ "wins" ]
-    ++ optionals resolved ["resolve [!UNAVAIL=return]"]
+    ++ optional mymachines "mymachines"
+    ++ optional nssmdns "mdns_minimal [NOTFOUND=return]"
+    ++ optional nsswins "wins"
+    ++ optional resolved "resolve [!UNAVAIL=return]"
     ++ [ "dns" ]
-    ++ optionals nssmdns [ "mdns" ]
-    ++ optionals myhostname ["myhostname" ];
+    ++ optional nssmdns "mdns"
+    ++ optional myhostname "myhostname";
 
   passwdArray = [ "files" ]
     ++ optional sssd "sss"
-    ++ optionals ldap [ "ldap" ]
-    ++ optionals mymachines [ "mymachines" ]
+    ++ optional ldap "ldap"
+    ++ optional mymachines "mymachines"
     ++ [ "systemd" ];
 
   shadowArray = [ "files" ]
     ++ optional sssd "sss"
-    ++ optionals ldap [ "ldap" ];
+    ++ optional ldap "ldap";
 
   servicesArray = [ "files" ]
     ++ optional sssd "sss";
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
index a4a4d6e1a6af..4c37e8a6208c 100644
--- a/nixos/modules/config/power-management.nix
+++ b/nixos/modules/config/power-management.nix
@@ -69,9 +69,6 @@ in
 
   config = mkIf cfg.enable {
 
-    # Leftover for old setups, should be set by nixos-generate-config now
-    powerManagement.cpuFreqGovernor = mkDefault "ondemand";
-
     systemd.targets.post-resume = {
       description = "Post-Resume Actions";
       requires = [ "post-resume.service" ];
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 8b9c3570476a..90cea47b70ae 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -45,7 +45,7 @@ let
   uid = ids.uids.pulseaudio;
   gid = ids.gids.pulseaudio;
 
-  stateDir = "/var/run/pulse";
+  stateDir = "/run/pulse";
 
   # Create pulse/client.conf even if PulseAudio is disabled so
   # that we can disable the autospawn feature in programs that
@@ -101,7 +101,8 @@ in {
           each user that tries to use the sound system. The server runs
           with user privileges. This is the recommended and most secure
           way to use PulseAudio. If true, one system-wide PulseAudio
-          server is launched on boot, running as the user "pulse".
+          server is launched on boot, running as the user "pulse", and
+          only users in the "audio" group will have access to the server.
           Please read the PulseAudio documentation for more details.
         '';
       };
@@ -213,12 +214,20 @@ in {
     (mkIf cfg.enable {
       environment.systemPackages = [ overriddenPackage ];
 
+      sound.enable = true;
+
       environment.etc = [
         { target = "asound.conf";
           source = alsaConf; }
 
         { target = "pulse/daemon.conf";
           source = writeText "daemon.conf" (lib.generators.toKeyValue {} cfg.daemon.config); }
+
+        { target = "openal/alsoft.conf";
+          source = writeText "alsoft.conf" "drivers=pulse"; }
+
+        { target = "libao.conf";
+          source = writeText "libao.conf" "default_driver=pulse"; }
       ];
 
       # Allow PulseAudio to get realtime priority using rtkit.
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 65f2e5d7af99..398660967c52 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -36,7 +36,7 @@ in
       default = {};
       description = ''
         A set of environment variables used in the global environment.
-        These variables will be set on shell initialisation.
+        These variables will be set on shell initialisation (e.g. in /etc/profile).
         The value of each variable can be either a string or a list of
         strings.  The latter is concatenated, interspersed with colon
         characters.
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index d3212d931605..361151665018 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -109,7 +109,6 @@ in
         "/sbin"
         "/share/applications"
         "/share/desktop-directories"
-        "/share/doc"
         "/share/emacs"
         "/share/icons"
         "/share/menus"
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index a4715175cc95..621ca36fb6b8 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -35,6 +35,7 @@ let
 
       name = mkOption {
         type = types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
         description = ''
           The name of the user account. If undefined, the name of the
           attribute set will be used.
@@ -91,6 +92,7 @@ let
 
       group = mkOption {
         type = types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"); x;
         default = "nogroup";
         description = "The user's primary group.";
       };
@@ -502,9 +504,6 @@ in {
       };
     };
 
-    # Install all the user shells
-    environment.systemPackages = systemShells;
-
     users.groups = {
       root.gid = ids.gids.root;
       wheel.gid = ids.gids.wheel;
@@ -529,6 +528,9 @@ in {
 
     system.activationScripts.users = stringAfter [ "stdio" ]
       ''
+        install -m 0700 -d /root
+        install -m 0755 -d /home
+
         ${pkgs.perl}/bin/perl -w \
           -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl \
           -I${pkgs.perlPackages.JSON}/lib/perl5/site_perl \
@@ -538,14 +540,29 @@ in {
     # for backwards compatibility
     system.activationScripts.groups = stringAfter [ "users" ] "";
 
-    environment.etc."subuid" = {
-      text = subuidFile;
-      mode = "0644";
-    };
-    environment.etc."subgid" = {
-      text = subgidFile;
-      mode = "0644";
-    };
+    # Install all the user shells
+    environment.systemPackages = systemShells;
+
+    environment.etc = {
+      "subuid" = {
+        text = subuidFile;
+        mode = "0644";
+      };
+      "subgid" = {
+        text = subgidFile;
+        mode = "0644";
+      };
+    } // (mapAttrs' (name: { packages, ... }: {
+      name = "profiles/per-user/${name}";
+      value.source = pkgs.buildEnv {
+        name = "user-environment";
+        paths = packages;
+        inherit (config.environment) pathsToLink extraOutputsToInstall;
+        inherit (config.system.path) ignoreCollisions postBuild;
+      };
+    }) (filterAttrs (_: u: u.packages != []) cfg.users));
+
+    environment.profiles = [ "/etc/profiles/per-user/$USER" ];
 
     assertions = [
       { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
@@ -576,20 +593,4 @@ in {
 
   };
 
-  imports =
-    [ (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
-      (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
-      {
-        environment = {
-          etc = mapAttrs' (name: { packages, ... }: {
-            name = "per-user-pkgs/${name}";
-            value.source = pkgs.symlinkJoin {
-              name = "per-user-pkgs.${name}";
-              paths = packages;
-            };
-          }) (filterAttrs (_: { packages, ... }: packages != []) cfg.users);
-          profiles = ["/etc/per-user-pkgs/$LOGNAME"];
-        };
-      }
-    ];
 }
diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix
index ad41ad4f3d7c..ae1b0a6c8e11 100644
--- a/nixos/modules/config/zram.nix
+++ b/nixos/modules/config/zram.nix
@@ -93,7 +93,7 @@ in
             serviceConfig = {
               Type = "oneshot";
               RemainAfterExit = true;
-              ExecStop = "${pkgs.stdenv.shell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
+              ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'";
             };
             script = ''
               set -u
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index afaa65f6ce35..b61acf1815d9 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -37,8 +37,9 @@ in {
       hardware.firmware = with pkgs; [
         firmwareLinuxNonfree
         intel2200BGFirmware
-        rtl8723bs-firmware
         rtl8192su-firmware
+      ] ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [
+        rtl8723bs-firmware
       ];
     })
     (mkIf cfg.enableAllFirmware {
diff --git a/nixos/modules/hardware/digitalbitbox.nix b/nixos/modules/hardware/digitalbitbox.nix
new file mode 100644
index 000000000000..0888cfbef2a8
--- /dev/null
+++ b/nixos/modules/hardware/digitalbitbox.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.digitalbitbox;
+in
+
+{
+  options.hardware.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for Digital Bitbox devices.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/hardware/onlykey.nix b/nixos/modules/hardware/onlykey.nix
new file mode 100644
index 000000000000..b6820fe01911
--- /dev/null
+++ b/nixos/modules/hardware/onlykey.nix
@@ -0,0 +1,33 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+  ####### interface
+
+  options = {
+
+    hardware.onlykey = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable OnlyKey device (https://crp.to/p/) support.
+        '';
+      };
+    };
+
+  };
+
+  ## As per OnlyKey's documentation piece (hhttps://docs.google.com/document/d/1Go_Rs218fKUx-j_JKhddbSVTqY6P0vQO831t2MKCJC8),
+  ## it is important to add udev rule for OnlyKey for it to work on Linux
+
+  ####### implementation
+
+  config = mkIf config.hardware.onlykey.enable {
+    services.udev.extraRules = builtin.readFile ./onlykey.udev;
+  };
+
+
+}
diff --git a/nixos/modules/hardware/onlykey.udev b/nixos/modules/hardware/onlykey.udev
new file mode 100644
index 000000000000..6583530e5684
--- /dev/null
+++ b/nixos/modules/hardware/onlykey.udev
@@ -0,0 +1,4 @@
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP+="plugdev"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP+="plugdev"
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
index c2c36f02a143..b371af353cf9 100644
--- a/nixos/modules/hardware/opengl.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -14,7 +14,6 @@ let
     name = "mesa-drivers+txc-${p.mesa_drivers.version}";
     paths =
       [ p.mesa_drivers
-        p.mesa_drivers.out # mainly for libGL
         (if cfg.s3tcSupport then p.libtxc_dxtn else p.libtxc_dxtn_s2tc)
       ];
   };
@@ -33,82 +32,92 @@ in
 
 {
   options = {
-    hardware.opengl.enable = mkOption {
-      description = "Whether this configuration requires OpenGL.";
-      type = types.bool;
-      default = false;
-      internal = true;
-    };
-
-    hardware.opengl.driSupport = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable accelerated OpenGL rendering through the
-        Direct Rendering Interface (DRI).
-      '';
-    };
-
-    hardware.opengl.driSupport32Bit = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        On 64-bit systems, whether to support Direct Rendering for
-        32-bit applications (such as Wine).  This is currently only
-        supported for the <literal>nvidia</literal> and 
-        <literal>ati_unfree</literal> drivers, as well as
-        <literal>Mesa</literal>.
-      '';
-    };
-
-    hardware.opengl.s3tcSupport = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Make S3TC(S3 Texture Compression) via libtxc_dxtn available
-        to OpenGL drivers instead of the patent-free S2TC replacement.
-
-        Using this library may require a patent license depending on your location.
-      '';
-    };
 
-    hardware.opengl.package = mkOption {
-      type = types.package;
-      internal = true;
-      description = ''
-        The package that provides the OpenGL implementation.
-      '';
-    };
-
-    hardware.opengl.package32 = mkOption {
-      type = types.package;
-      internal = true;
-      description = ''
-        The package that provides the 32-bit OpenGL implementation on
-        64-bit systems. Used when <option>driSupport32Bit</option> is
-        set.
-      '';
-    };
-
-    hardware.opengl.extraPackages = mkOption {
-      type = types.listOf types.package;
-      default = [];
-      example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]";
-      description = ''
-        Additional packages to add to OpenGL drivers. This can be used
-        to add OpenCL drivers, VA-API/VDPAU drivers etc.
-      '';
-    };
-
-    hardware.opengl.extraPackages32 = mkOption {
-      type = types.listOf types.package;
-      default = [];
-      example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
-      description = ''
-        Additional packages to add to 32-bit OpenGL drivers on
-        64-bit systems. Used when <option>driSupport32Bit</option> is
-        set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-      '';
+    hardware.opengl = {
+      enable = mkOption {
+        description = ''
+          Whether to enable OpenGL drivers. This is needed to enable
+          OpenGL support in X11 systems, as well as for Wayland compositors
+          like sway, way-cooler and Weston. It is enabled by default
+          by the corresponding modules, so you do not usually have to
+          set it yourself, only if there is no module for your wayland
+          compositor of choice. See services.xserver.enable,
+          programs.sway.enable, and programs.way-cooler.enable.
+        '';
+        type = types.bool;
+        default = false;
+      };
+
+      driSupport = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable accelerated OpenGL rendering through the
+          Direct Rendering Interface (DRI).
+        '';
+      };
+
+      driSupport32Bit = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          On 64-bit systems, whether to support Direct Rendering for
+          32-bit applications (such as Wine).  This is currently only
+          supported for the <literal>nvidia</literal> and
+          <literal>ati_unfree</literal> drivers, as well as
+          <literal>Mesa</literal>.
+        '';
+      };
+
+      s3tcSupport = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Make S3TC(S3 Texture Compression) via libtxc_dxtn available
+          to OpenGL drivers instead of the patent-free S2TC replacement.
+
+          Using this library may require a patent license depending on your location.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the OpenGL implementation.
+        '';
+      };
+
+      package32 = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the 32-bit OpenGL implementation on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set.
+        '';
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]";
+        description = ''
+          Additional packages to add to OpenGL drivers. This can be used
+          to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
+
+      extraPackages32 = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
+        description = ''
+          Additional packages to add to 32-bit OpenGL drivers on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
     };
 
   };
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index 5cc96d8bd074..50af022b93c8 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -15,13 +15,19 @@ let
 
   opengl = config.hardware.opengl;
 
+  kernel = pkgs.linux_4_9.override {
+    extraConfig = ''
+      KALLSYMS_ALL y
+    '';
+  };
+
 in
 
 {
 
   config = mkIf enabled {
 
-    nixpkgs.config.xorg.abiCompat = "1.18";
+    nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
       { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; };
@@ -31,6 +37,9 @@ in
 
     boot.extraModulePackages = [ package ];
 
+    boot.kernelPackages =
+      pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor kernel);
+
     boot.blacklistedKernelModules = [ "radeon" ];
 
     hardware.firmware = [ package ];
@@ -38,10 +47,15 @@ in
     system.activationScripts.setup-amdgpu-pro = ''
       mkdir -p /run/lib
       ln -sfn ${package}/lib ${package.libCompatDir}
+      ln -sfn ${package} /run/amdgpu-pro
     '' + optionalString opengl.driSupport32Bit ''
       ln -sfn ${package32}/lib ${package32.libCompatDir}
     '';
 
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "KALLSYMS_ALL")
+    ];
+
     environment.etc = {
       "amd/amdrc".source = package + "/etc/amd/amdrc";
       "amd/amdapfxx.blb".source = package + "/etc/amd/amdapfxx.blb";
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 50c085dd7ee2..eb1952280331 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -16,8 +16,6 @@ let
         kernelPackages.nvidia_x11
     else if elem "nvidiaBeta" drivers then
         kernelPackages.nvidia_x11_beta
-    else if elem "nvidiaLegacy173" drivers then
-      kernelPackages.nvidia_x11_legacy173
     else if elem "nvidiaLegacy304" drivers then
       kernelPackages.nvidia_x11_legacy304
     else if elem "nvidiaLegacy340" drivers then
@@ -27,13 +25,6 @@ let
   nvidia_x11 = nvidiaForKernel config.boot.kernelPackages;
   nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; };
 
-  nvidiaPackage = nvidia: pkgs:
-    if !nvidia.useGLVND then nvidia.out
-    else pkgs.buildEnv {
-      name = "nvidia-libs";
-      paths = [ pkgs.libglvnd nvidia.out ];
-    };
-
   enabled = nvidia_x11 != null;
 in
 
@@ -59,8 +50,8 @@ in
       source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
     };
 
-    hardware.opengl.package = nvidiaPackage nvidia_x11 pkgs;
-    hardware.opengl.package32 = nvidiaPackage nvidia_libs32 pkgs_i686;
+    hardware.opengl.package = nvidia_x11.out;
+    hardware.opengl.package32 = nvidia_libs32.out;
 
     environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
       ++ lib.filter (p: p != null) [ nvidia_x11.persistenced ];
@@ -75,10 +66,10 @@ in
     # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
     services.udev.extraRules =
       ''
-        KERNEL=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
-        KERNEL=="nvidia_modeset", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
-        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
-        KERNEL=="nvidia_uvm", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
+        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
+        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
+        KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
       '';
 
     boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index ddb00f174d1a..01cfe8a02e10 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -12,7 +12,7 @@ let
   # CD.  These are installed into the "nixos" channel of the root
   # user, as expected by nixos-rebuild/nixos-install. FIXME: merge
   # with make-channel.nix.
-  channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}"
+  channelSources = pkgs.runCommand "nixos-${config.system.nixos.version}"
     { }
     ''
       mkdir -p $out
@@ -21,7 +21,9 @@ let
       if [ ! -e $out/nixos/nixpkgs ]; then
         ln -s . $out/nixos/nixpkgs
       fi
-      echo -n ${config.system.nixosVersionSuffix} > $out/nixos/.version-suffix
+      echo -n ${config.system.nixos.revision} > $out/nixos/.git-revision
+      echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix
+      echo ${config.system.nixos.versionSuffix} | sed -e s/pre// > $out/nixos/svn-revision
     '';
 
 in
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 2569860a098f..756c8751d00e 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -16,7 +16,7 @@ with lib;
     ];
 
   # ISO naming.
-  isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixosLabel}-${pkgs.stdenv.system}.iso";
+  isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixos.label}-${pkgs.stdenv.system}.iso";
 
   isoImage.volumeID = substring 0 11 "NIXOS_ISO";
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 5725938465f5..4c4e69d60d9c 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -69,7 +69,7 @@ with lib;
   in ''
     mkdir -p /root/Desktop
     ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop
-    cp ${pkgs.gnome3.gnome_terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop
+    cp ${pkgs.gnome3.gnome-terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop
     chmod a+rx /root/Desktop/gnome-terminal.desktop
     cp ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop
     chmod a+rx /root/Desktop/gparted.desktop
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index a039f7fdcb6e..e7cbf415a223 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -39,31 +39,31 @@ let
     DEFAULT boot
 
     LABEL boot
-    MENU LABEL NixOS ${config.system.nixosLabel}${config.isoImage.appendToMenuLabel}
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with 'nomodeset'
     LABEL boot-nomodeset
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (nomodeset)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with 'copytoram'
     LABEL boot-copytoram
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (copytoram)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
 
     # A variant to boot with verbose logging to the console
     LABEL boot-nomodeset
-    MENU LABEL NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (debug)
-    LINUX /boot/bzImage
+    MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
+    LINUX /boot/${config.system.boot.loader.kernelFile}
     APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
-    INITRD /boot/initrd
+    INITRD /boot/${config.system.boot.loader.initrdFile}
   '';
 
   isolinuxMemtest86Entry = ''
@@ -82,35 +82,35 @@ let
     mkdir -p $out/loader/entries
 
     cat << EOF > $out/loader/entries/nixos-iso.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
-    linux /boot/bzImage
-    initrd /boot/initrd
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
     EOF
 
     # A variant to boot with 'nomodeset'
     cat << EOF > $out/loader/entries/nixos-iso-nomodeset.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
     version nomodeset
-    linux /boot/bzImage
-    initrd /boot/initrd
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
     EOF
 
     # A variant to boot with 'copytoram'
     cat << EOF > $out/loader/entries/nixos-iso-copytoram.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel}
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
     version copytoram
-    linux /boot/bzImage
-    initrd /boot/initrd
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
     EOF
 
     # A variant to boot with verbose logging to the console
     cat << EOF > $out/loader/entries/nixos-iso-debug.conf
-    title NixOS ${config.system.nixosVersion}${config.isoImage.appendToMenuLabel} (debug)
-    linux /boot/bzImage
-    initrd /boot/initrd
+    title NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
+    linux /boot/${config.system.boot.loader.kernelFile}
+    initrd /boot/${config.system.boot.loader.initrdFile}
     options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
     EOF
 
@@ -127,8 +127,8 @@ let
       mkdir ./contents && cd ./contents
       cp -rp "${efiDir}"/* .
       mkdir ./boot
-      cp -p "${config.boot.kernelPackages.kernel}/bzImage" \
-        "${config.system.build.initialRamdisk}/initrd" ./boot/
+      cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \
+        "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/
       touch --date=@0 ./*
 
       usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]')
@@ -331,8 +331,7 @@ in
         config.system.build.toplevel.drvPath;
 
     # Create the squashfs image that contains the Nix store.
-    system.build.squashfsStore = import ../../../lib/make-squashfs.nix {
-      inherit (pkgs) stdenv squashfsTools perl pathsFromGraph;
+    system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
       storeContents = config.isoImage.storeContents;
     };
 
@@ -346,11 +345,11 @@ in
           };
           target = "/isolinux/isolinux.cfg";
         }
-        { source = config.boot.kernelPackages.kernel + "/bzImage";
-          target = "/boot/bzImage";
+        { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
+          target = "/boot/" + config.system.boot.loader.kernelFile;
         }
-        { source = config.system.build.initialRamdisk + "/initrd";
-          target = "/boot/initrd";
+        { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
+          target = "/boot/" + config.system.boot.loader.initrdFile;
         }
         { source = config.system.build.squashfsStore;
           target = "/nix-store.squashfs";
@@ -361,7 +360,7 @@ in
         { source = config.isoImage.splashImage;
           target = "/isolinux/background.png";
         }
-        { source = pkgs.writeText "version" config.system.nixosVersion;
+        { source = pkgs.writeText "version" config.system.nixos.label;
           target = "/version.txt";
         }
       ] ++ optionals config.isoImage.makeEfiBootable [
@@ -383,11 +382,8 @@ in
     boot.loader.timeout = 10;
 
     # Create the ISO image.
-    system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
-      inherit (pkgs) stdenv perl pathsFromGraph xorriso syslinux;
-
+    system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({
       inherit (config.isoImage) isoName compressImage volumeID contents;
-
       bootable = true;
       bootImage = "/isolinux/isolinux.bin";
     } // optionalAttrs config.isoImage.makeUsbBootable {
diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
index efb9ba39bcd4..ddf91a5656c7 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix
@@ -21,12 +21,10 @@ in
       "it cannot be cross compiled";
   };
 
-  # Needed by RPi firmware
-  nixpkgs.config.allowUnfree = true;
-
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
 
   # The serial ports listed here are:
@@ -42,8 +40,17 @@ in
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
         kernel=u-boot-rpi3.bin
+
+        # Boot in 64-bit mode.
         arm_control=0x200
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
+
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
       '';
       in ''
         (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/)
diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
index 880a6bf2e1e8..891923234dda 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix
@@ -21,12 +21,10 @@ in
       "it cannot be cross compiled";
   };
 
-  # Needed by RPi firmware
-  nixpkgs.config.allowUnfree = true;
-
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_latest;
   # The serial ports listed here are:
   # - ttyS0: for Tegra (Jetson TK1)
@@ -42,11 +40,18 @@ in
   sdImage = {
     populateBootCommands = let
       configTxt = pkgs.writeText "config.txt" ''
+        # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
+        # when attempting to show low-voltage or overtemperature warnings.
+        avoid_warnings=1
+
         [pi2]
         kernel=u-boot-rpi2.bin
 
         [pi3]
         kernel=u-boot-rpi3.bin
+
+        # U-Boot used to need this to work, regardless of whether UART is actually used or not.
+        # TODO: check when/if this can be removed.
         enable_uart=1
       '';
       in ''
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
index eb676eae05e8..212013b5e289 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix
@@ -21,12 +21,10 @@ in
       "it cannot be cross compiled";
   };
 
-  # Needed by RPi firmware
-  nixpkgs.config.allowUnfree = true;
-
   boot.loader.grub.enable = false;
   boot.loader.generic-extlinux-compatible.enable = true;
 
+  boot.consoleLogLevel = lib.mkDefault 7;
   boot.kernelPackages = pkgs.linuxPackages_rpi;
 
   # FIXME: this probably should be in installation-device.nix
diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix
index 23312c073d56..c091923de60f 100644
--- a/nixos/modules/installer/cd-dvd/sd-image.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image.nix
@@ -20,6 +20,20 @@ let
 in
 {
   options.sdImage = {
+    imageName = mkOption {
+      default = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.system}.img";
+      description = ''
+        Name of the generated image file.
+      '';
+    };
+
+    imageBaseName = mkOption {
+      default = "nixos-sd-image";
+      description = ''
+        Prefix of the name of the generated image file.
+      '';
+    };
+
     storePaths = mkOption {
       type = with types; listOf package;
       example = literalExample "[ pkgs.stdenv ]";
@@ -61,19 +75,25 @@ in
     sdImage.storePaths = [ config.system.build.toplevel ];
 
     system.build.sdImage = pkgs.stdenv.mkDerivation {
-      name = "sd-image-${pkgs.stdenv.system}.img";
+      name = config.sdImage.imageName;
 
       buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ];
 
       buildCommand = ''
+        mkdir -p $out/nix-support $out/sd-image
+        export img=$out/sd-image/${config.sdImage.imageName}
+
+        echo "${pkgs.stdenv.system}" > $out/nix-support/system
+        echo "file sd-image $img" >> $out/nix-support/hydra-build-products
+
         # Create the image file sized to fit /boot and /, plus 20M of slack
         rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }')
         bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512))
         imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 20 * 1024 * 1024))
-        truncate -s $imageSize $out
+        truncate -s $imageSize $img
 
         # type=b is 'W95 FAT32', type=83 is 'Linux'.
-        sfdisk $out <<EOF
+        sfdisk $img <<EOF
             label: dos
             label-id: 0x2178694e
 
@@ -82,11 +102,11 @@ in
         EOF
 
         # Copy the rootfs into the SD image
-        eval $(partx $out -o START,SECTORS --nr 2 --pairs)
-        dd conv=notrunc if=${rootfsImage} of=$out seek=$START count=$SECTORS
+        eval $(partx $img -o START,SECTORS --nr 2 --pairs)
+        dd conv=notrunc if=${rootfsImage} of=$img seek=$START count=$SECTORS
 
         # Create a FAT32 /boot partition of suitable size into bootpart.img
-        eval $(partx $out -o START,SECTORS --nr 1 --pairs)
+        eval $(partx $img -o START,SECTORS --nr 1 --pairs)
         truncate -s $((SECTORS * 512)) bootpart.img
         faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n NIXOS_BOOT bootpart.img
 
@@ -96,7 +116,7 @@ in
 
         # Copy the populated /boot into the SD image
         (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::)
-        dd conv=notrunc if=bootpart.img of=$out seek=$START count=$SECTORS
+        dd conv=notrunc if=bootpart.img of=$img seek=$START count=$SECTORS
       '';
     };
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
index ba84cd51098f..6d4ba96dba0c 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -54,7 +54,7 @@ in
   environment.systemPackages =
     [ pkgs.w3m # needed for the manual anyway
       pkgs.testdisk # useful for repairing boot problems
-      pkgs.mssys # for writing Microsoft boot sectors / MBRs
+      pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
       pkgs.parted
       pkgs.ddrescue
       pkgs.ccrypt
diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix
index 1962a1959ead..e72d4a5b4910 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball.nix
@@ -8,7 +8,7 @@ with lib;
 
 let
 
-  versionFile = pkgs.writeText "nixos-version" config.system.nixosVersion;
+  versionFile = pkgs.writeText "nixos-label" config.system.nixos.label;
 
 in
 
@@ -58,8 +58,8 @@ in
     # Individual files to be included on the CD, outside of the Nix
     # store on the CD.
     tarball.contents =
-      [ { source = config.system.build.initialRamdisk + "/initrd";
-          target = "/boot/initrd";
+      [ { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
+          target = "/boot/" + config.system.boot.loader.initrdFile;
         }
         { source = versionFile;
           target = "/nixos-version.txt";
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index 0f6046339b37..a4eda3c52dce 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -18,17 +18,17 @@ with lib;
 
   };
 
-  config = {
-
-    boot.loader.grub.version = 2;
-
+  config = rec {
     # Don't build the GRUB menu builder script, since we don't need it
     # here and it causes a cyclic dependency.
     boot.loader.grub.enable = false;
 
     # !!! Hack - attributes expected by other modules.
-    system.boot.loader.kernelFile = "bzImage";
-    environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ];
+    environment.systemPackages = [ pkgs.grub2_efi ]
+      ++ (if pkgs.stdenv.system == "aarch64-linux"
+          then []
+          else [ pkgs.grub2 pkgs.syslinux ]);
+    system.boot.loader.kernelFile = pkgs.stdenv.platform.kernelTarget;
 
     fileSystems."/" =
       { fsType = "tmpfs";
@@ -67,7 +67,7 @@ with lib;
 
     # Create the squashfs image that contains the Nix store.
     system.build.squashfsStore = import ../../../lib/make-squashfs.nix {
-      inherit (pkgs) stdenv squashfsTools perl pathsFromGraph;
+      inherit (pkgs) stdenv squashfsTools closureInfo;
       storeContents = config.netboot.storeContents;
     };
 
@@ -84,7 +84,12 @@ with lib;
         ];
     };
 
-    system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" "#!ipxe\nkernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}\ninitrd initrd\nboot";
+    system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
+      #!ipxe
+      kernel ${pkgs.stdenv.platform.kernelTarget} init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+      initrd initrd
+      boot
+    '';
 
     boot.loader.timeout = 10;
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 321fb9a2030a..6bb556a0123c 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,5 +1,6 @@
 {
-  x86_64-linux = "/nix/store/b4s1gxiis1ryvybnjhdjvgc5sr1nq0ys-nix-1.11.15";
-  i686-linux = "/nix/store/kgb5hs7qw13bvb6icramv1ry9dard3h9-nix-1.11.15";
-  x86_64-darwin = "/nix/store/dgwz3dxdzs2wwd7pg7cdhvl8rv0qpnbj-nix-1.11.15";
+  x86_64-linux = "/nix/store/2gk7rk2sx2dkmsjr59gignrfdmya8f6s-nix-2.0.1";
+  i686-linux = "/nix/store/5160glkphiv13qggnivyidg8r0491pbl-nix-2.0.1";
+  aarch64-linux = "/nix/store/jk29zz3ns9vdkkclcyzzkpzp8dhv1x3i-nix-2.0.1";
+  x86_64-darwin = "/nix/store/4a9czmrpd4hf3r80zcmga2c2lm3hbbvv-nix-2.0.1";
 }
diff --git a/nixos/modules/installer/tools/nixos-enter.sh b/nixos/modules/installer/tools/nixos-enter.sh
new file mode 100644
index 000000000000..679391189612
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-enter.sh
@@ -0,0 +1,60 @@
+#! @shell@
+
+set -e
+
+# Re-exec ourselves in a private mount namespace so that our bind
+# mounts get cleaned up automatically.
+if [ -z "$NIXOS_ENTER_REEXEC" ]; then
+    export NIXOS_ENTER_REEXEC=1
+    if [ "$(id -u)" != 0 ]; then
+        extraFlags="-r"
+    fi
+    exec unshare --fork --mount --uts --mount-proc --pid $extraFlags -- "$0" "$@"
+else
+    mount --make-rprivate /
+fi
+
+mountPoint=/mnt
+system=/nix/var/nix/profiles/system
+command=($system/sw/bin/bash "--login")
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+        --root)
+            mountPoint="$1"; shift 1
+            ;;
+        --system)
+            system="$1"; shift 1
+            ;;
+        --help)
+            exec man nixos-enter
+            exit 1
+            ;;
+        --command|-c)
+            command=($system/sw/bin/bash "-c" "$1")
+            shift 1
+            ;;
+        --)
+            command=("$@")
+            break
+            ;;
+        *)
+            echo "$0: unknown option \`$i'"
+            exit 1
+            ;;
+    esac
+done
+
+if [[ ! -e $mountPoint/etc/NIXOS ]]; then
+    echo "$0: '$mountPoint' is not a NixOS installation" >&2
+    exit 126
+fi
+
+mkdir -m 0755 -p "$mountPoint/dev"
+mount --rbind /dev "$mountPoint/dev"
+
+# Run the activation script. Set $LOCALE_ARCHIVE to supress some Perl locale warnings.
+LOCALE_ARCHIVE=$system/sw/lib/locale/locale-archive chroot "$mountPoint" "$system/activate" >&2 || true
+
+exec chroot "$mountPoint" "${command[@]}"
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 7c737e84de0a..14c611e18bc3 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -585,7 +585,6 @@ $bootLoaderConfig
 
   # Some programs need SUID wrappers, can be configured further or are
   # started in user sessions.
-  # programs.bash.enableCompletion = true;
   # programs.mtr.enable = true;
   # programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
 
@@ -603,6 +602,10 @@ $bootLoaderConfig
   # Enable CUPS to print documents.
   # services.printing.enable = true;
 
+  # Enable sound.
+  # sound.enable = true;
+  # hardware.pulseaudio.enable = true;
+
   # Enable the X11 windowing system.
   # services.xserver.enable = true;
   # services.xserver.layout = "us";
@@ -625,7 +628,7 @@ $bootLoaderConfig
   # compatible, in order to avoid breaking some software such as database
   # servers. You should change this only after NixOS release notes say you
   # should.
-  system.stateVersion = "${\(qw(@nixosRelease@))}"; # Did you read the comment?
+  system.stateVersion = "${\(qw(@release@))}"; # Did you read the comment?
 
 }
 EOF
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index f994d5b4bde1..22c1e0fe9a34 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -1,35 +1,23 @@
 #! @shell@
 
-# - make Nix store etc.
-# - copy closure of Nix to target device
-# - register validity
-# - with a chroot to the target device:
-#   * nix-env -p /nix/var/nix/profiles/system -i <nix-expr for the configuration>
-#   * install the boot loader
+set -e
+shopt -s nullglob
+
+export PATH=@path@:$PATH
 
 # Ensure a consistent umask.
 umask 0022
 
-# Re-exec ourselves in a private mount namespace so that our bind
-# mounts get cleaned up automatically.
-if [ "$(id -u)" = 0 ]; then
-    if [ -z "$NIXOS_INSTALL_REEXEC" ]; then
-        export NIXOS_INSTALL_REEXEC=1
-        exec unshare --mount --uts -- "$0" "$@"
-    else
-        mount --make-rprivate /
-    fi
-fi
-
 # Parse the command line for the -I flag
 extraBuildFlags=()
-chrootCommand=(/run/current-system/sw/bin/bash)
-buildUsersGroup="nixbld"
+
+mountPoint=/mnt
+channelPath=
 
 while [ "$#" -gt 0 ]; do
     i="$1"; shift 1
     case "$i" in
-        --max-jobs|-j|--cores|-I)
+        --max-jobs|-j|--cores|-I|--substituters)
             j="$1"; shift 1
             extraBuildFlags+=("$i" "$j")
             ;;
@@ -41,9 +29,11 @@ while [ "$#" -gt 0 ]; do
         --root)
             mountPoint="$1"; shift 1
             ;;
-        --closure)
-            closure="$1"; shift 1
-            buildUsersGroup=""
+        --system|--closure)
+            system="$1"; shift 1
+            ;;
+        --channel)
+            channelPath="$1"; shift 1
             ;;
         --no-channel-copy)
             noChannelCopy=1
@@ -57,17 +47,13 @@ while [ "$#" -gt 0 ]; do
         --show-trace)
             extraBuildFlags+=("$i")
             ;;
-        --chroot)
-            runChroot=1
-            if [[ "$@" != "" ]]; then
-                chrootCommand=("$@")
-            fi
-            break
-            ;;
         --help)
             exec man nixos-install
             exit 1
             ;;
+        --debug)
+            set -x
+            ;;
         *)
             echo "$0: unknown option \`$i'"
             exit 1
@@ -75,132 +61,83 @@ while [ "$#" -gt 0 ]; do
     esac
 done
 
-set -e
-shopt -s nullglob
-
-if test -z "$mountPoint"; then
-    mountPoint=/mnt
-fi
-
 if ! test -e "$mountPoint"; then
     echo "mount point $mountPoint doesn't exist"
     exit 1
 fi
 
 # Get the path of the NixOS configuration file.
-if test -z "$NIXOS_CONFIG"; then
-    NIXOS_CONFIG=/etc/nixos/configuration.nix
+if [[ -z $NIXOS_CONFIG ]]; then
+    NIXOS_CONFIG=$mountPoint/etc/nixos/configuration.nix
 fi
 
-if [ ! -e "$mountPoint/$NIXOS_CONFIG" ] && [ -z "$closure" ]; then
-    echo "configuration file $mountPoint/$NIXOS_CONFIG doesn't exist"
+if [[ ${NIXOS_CONFIG:0:1} != / ]]; then
+    echo "$0: \$NIXOS_CONFIG is not an absolute path"
     exit 1
 fi
 
-
-# Builds will use users that are members of this group
-extraBuildFlags+=(--option "build-users-group" "$buildUsersGroup")
-
-# Inherit binary caches from the host
-# TODO: will this still work with Nix 1.12 now that it has no perl? Probably not...
-binary_caches="$(@perl@/bin/perl -I @nix@/lib/perl5/site_perl/*/* -e 'use Nix::Config; Nix::Config::readConfig; print $Nix::Config::config{"binary-caches"};')"
-extraBuildFlags+=(--option "binary-caches" "$binary_caches")
-
-# We only need nixpkgs in the path if we don't already have a system closure to install
-if [[ -z "$closure" ]]; then
-    nixpkgs="$(readlink -f "$(nix-instantiate --find-file nixpkgs)")"
-    export NIX_PATH="nixpkgs=$nixpkgs:nixos-config=$mountPoint/$NIXOS_CONFIG"
-fi
-unset NIXOS_CONFIG
-
-# These get created in nixos-prepare-root as well, but we want to make sure they're here in case we're
-# running with --chroot. TODO: --chroot should just be split into a separate tool.
-mkdir -m 0755 -p "$mountPoint/dev" "$mountPoint/proc" "$mountPoint/sys"
-
-# Set up some bind mounts we'll want regardless of chroot or not
-mount --rbind /dev "$mountPoint/dev"
-mount --rbind /proc "$mountPoint/proc"
-mount --rbind /sys "$mountPoint/sys"
-
-# If we asked for a chroot, that means we're not actually installing anything (yeah I was confused too)
-# and we just want to run a command in the context of a $mountPoint that we're assuming has already been
-# set up by a previous nixos-install invocation. In that case we set up some remaining bind mounts and
-# exec the requested command, skipping the rest of the installation procedure.
-if [ -n "$runChroot" ]; then
-    mount -t tmpfs -o "mode=0755" none $mountPoint/run
-    rm -rf $mountPoint/var/run
-    ln -s /run $mountPoint/var/run
-    for f in /etc/resolv.conf /etc/hosts; do rm -f $mountPoint/$f; [ -f "$f" ] && cp -Lf $f $mountPoint/etc/; done
-    for f in /etc/passwd /etc/group;      do touch $mountPoint/$f; [ -f "$f" ] && mount --rbind -o ro $f $mountPoint/$f; done
-
-    if ! [ -L $mountPoint/nix/var/nix/profiles/system ]; then
-        echo "$0: installation not finished; cannot chroot into installation directory"
-        exit 1
-    fi
-    ln -s /nix/var/nix/profiles/system $mountPoint/run/current-system
-    exec chroot $mountPoint "${chrootCommand[@]}"
+if [[ ! -e $NIXOS_CONFIG && -z $system ]]; then
+    echo "configuration file $NIXOS_CONFIG doesn't exist"
+    exit 1
 fi
 
-# A place to drop temporary closures
+# A place to drop temporary stuff.
 trap "rm -rf $tmpdir" EXIT
 tmpdir="$(mktemp -d)"
 
-# Build a closure (on the host; we then copy it into the guest)
-function closure() {
-    nix-build "${extraBuildFlags[@]}" --no-out-link -E "with import <nixpkgs> {}; runCommand \"closure\" { exportReferencesGraph = [ \"x\" (buildEnv { name = \"env\"; paths = [ ($1) stdenv ]; }) ]; } \"cp x \$out\""
-}
-
-system_closure="$tmpdir/system.closure"
-# Use a FIFO for piping nix-store --export into nix-store --import, saving disk
-# I/O and space. nix-store --import is run by nixos-prepare-root.
-mkfifo $system_closure
-
-if [ -z "$closure" ]; then
-    expr="(import <nixpkgs/nixos> {}).system"
-    system_root="$(nix-build -E "$expr")"
-    system_closure="$(closure "$expr")"
-else
-    system_root=$closure
-    # Create a temporary file ending in .closure (so nixos-prepare-root knows to --import it) to transport the store closure
-    # to the filesytem we're preparing. Also delete it on exit!
-    # Run in background to avoid blocking while trying to write to the FIFO
-    # $system_closure refers to
-    nix-store --export $(nix-store -qR $closure) > $system_closure &
-fi
-
-channel_root="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")"
-channel_closure="$tmpdir/channel.closure"
-nix-store --export $channel_root > $channel_closure
-
-# Populate the target root directory with the basics
-@prepare_root@/bin/nixos-prepare-root "$mountPoint" "$channel_root" "$system_root" @nixClosure@ "$system_closure" "$channel_closure"
-
-# nixos-prepare-root doesn't currently do anything with file ownership, so we set it up here instead
-chown @root_uid@:@nixbld_gid@ $mountPoint/nix/store
+sub="auto?trusted=1"
 
+# Build the system configuration in the target filesystem.
+if [[ -z $system ]]; then
+    echo "building the configuration in $NIXOS_CONFIG..."
+    outLink="$tmpdir/system"
+    nix build --out-link "$outLink" --store "$mountPoint" "${extraBuildFlags[@]}" \
+        --extra-substituters "$sub" \
+        -f '<nixpkgs/nixos>' system -I "nixos-config=$NIXOS_CONFIG"
+    system=$(readlink -f $outLink)
+fi
 
+# Set the system profile to point to the configuration. TODO: combine
+# this with the previous step once we have a nix-env replacement with
+# a progress bar.
+nix-env --store "$mountPoint" "${extraBuildFlags[@]}" \
+        --extra-substituters "$sub" \
+        -p $mountPoint/nix/var/nix/profiles/system --set "$system"
+
+# Copy the NixOS/Nixpkgs sources to the target as the initial contents
+# of the NixOS channel.
+if [[ -z $noChannelCopy ]]; then
+    if [[ -z $channelPath ]]; then
+        channelPath="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")"
+    fi
+    if [[ -n $channelPath ]]; then
+        echo "copying channel..."
+        mkdir -p $mountPoint/nix/var/nix/profiles/per-user/root
+        nix-env --store "$mountPoint" "${extraBuildFlags[@]}" --extra-substituters "$sub" \
+                -p $mountPoint/nix/var/nix/profiles/per-user/root/channels --set "$channelPath" --quiet
+        install -m 0700 -d $mountPoint/root/.nix-defexpr
+        ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
+    fi
+fi
 
-# Grub needs an mtab.
-ln -sfn /proc/mounts $mountPoint/etc/mtab
+# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out.
+mkdir -m 0755 -p "$mountPoint/etc"
+touch "$mountPoint/etc/NIXOS"
 
 # Switch to the new system configuration.  This will install Grub with
 # a menu default pointing at the kernel/initrd/etc of the new
 # configuration.
-echo "finalising the installation..."
-if [ -z "$noBootLoader" ]; then
-  NIXOS_INSTALL_BOOTLOADER=1 chroot $mountPoint \
-      /nix/var/nix/profiles/system/bin/switch-to-configuration boot
+if [[ -z $noBootLoader ]]; then
+    echo "installing the boot loader..."
+    # Grub needs an mtab.
+    ln -sfn /proc/mounts $mountPoint/etc/mtab
+    NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root "$mountPoint" -- /run/current-system/bin/switch-to-configuration boot
 fi
 
-# Run the activation script.
-chroot $mountPoint /nix/var/nix/profiles/system/activate
-
-
-# Ask the user to set a root password.
-if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /run/wrappers/bin/passwd ] && [ -t 0 ]; then
-    echo "setting root password..."
-    chroot $mountPoint /run/wrappers/bin/passwd
+# Ask the user to set a root password, but only if the passwd command
+# exists (i.e. when mutable user accounts are enabled).
+if [[ -z $noRootPasswd ]] && [ -t 0 ]; then
+    nixos-enter --root "$mountPoint" -c '[[ -e /nix/var/nix/profiles/system/sw/bin/passwd ]] && echo "setting root password..." && /nix/var/nix/profiles/system/sw/bin/passwd'
 fi
 
-
 echo "installation finished!"
diff --git a/nixos/modules/installer/tools/nixos-prepare-root.sh b/nixos/modules/installer/tools/nixos-prepare-root.sh
deleted file mode 100644
index ed5af234fec9..000000000000
--- a/nixos/modules/installer/tools/nixos-prepare-root.sh
+++ /dev/null
@@ -1,104 +0,0 @@
-#! @shell@
-
-# This script's goal is to perform all "static" setup of a filesystem structure from pre-built store paths. Everything
-# in here should run in a non-root context and inside a Nix builder. It's designed primarily to be called from image-
-# building scripts and from nixos-install, but because it makes very few assumptions about the context in which it runs,
-# it could be useful in other contexts as well.
-#
-# Current behavior:
-#  - set up basic filesystem structure
-#  - make Nix store etc.
-#  - copy Nix, system, channel, and misceallaneous closures to target Nix store
-#  - register validity of all paths in the target store
-#  - set up channel and system profiles
-
-# Ensure a consistent umask.
-umask 0022
-
-set -e
-
-mountPoint="$1"
-channel="$2"
-system="$3"
-shift 3
-closures="$@"
-
-PATH="@coreutils@/bin:@nix@/bin:@perl@/bin:@utillinux@/bin:@rsync@/bin"
-
-if ! test -e "$mountPoint"; then
-    echo "mount point $mountPoint doesn't exist"
-    exit 1
-fi
-
-# Create a few of the standard directories in the target root directory.
-install -m 0755 -d $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home
-install -m 01777 -d $mountPoint/tmp
-install -m 0755 -d $mountPoint/tmp/root
-install -m 0755 -d $mountPoint/var
-install -m 0700 -d $mountPoint/root
-
-ln -sf /run $mountPoint/var/run
-
-# Create the necessary Nix directories on the target device
-install -m 0755 -d \
-    $mountPoint/nix/var/nix/gcroots \
-    $mountPoint/nix/var/nix/temproots \
-    $mountPoint/nix/var/nix/userpool \
-    $mountPoint/nix/var/nix/profiles \
-    $mountPoint/nix/var/nix/db \
-    $mountPoint/nix/var/log/nix/drvs
-
-install -m 1775 -d $mountPoint/nix/store
-
-# All Nix operations below should operate on our target store, not /nix/store.
-# N.B: this relies on Nix 1.12 or higher
-export NIX_REMOTE=local?root=$mountPoint
-
-# Copy our closures to the Nix store on the target mount point, unless they're already there.
-for i in $closures; do
-    # We support closures both in the format produced by `nix-store --export` and by `exportReferencesGraph`,
-    # mostly because there doesn't seem to be a single format that can be produced outside of a nix build and
-    # inside one. See https://github.com/NixOS/nix/issues/1242 for more discussion.
-    if [[ "$i" =~ \.closure$ ]]; then
-        echo "importing serialized closure $i to $mountPoint..."
-        nix-store --import < $i
-    else
-        # There has to be a better way to do this, right?
-        echo "copying closure $i to $mountPoint..."
-        for j in $(perl @pathsFromGraph@ $i); do
-            echo "  $j... "
-            rsync -a $j $mountPoint/nix/store/
-        done
-
-        nix-store --option build-users-group root --register-validity < $i
-    fi
-done
-
-# Create the required /bin/sh symlink; otherwise lots of things
-# (notably the system() function) won't work.
-if [ ! -x $mountPoint/@shell@ ]; then
-    echo "Error: @shell@ wasn't included in the closure" >&2
-    exit 1
-fi
-install -m 0755 -d $mountPoint/bin
-ln -sf @shell@ $mountPoint/bin/sh
-
-echo "setting the system closure to '$system'..."
-nix-env "${extraBuildFlags[@]}" -p $mountPoint/nix/var/nix/profiles/system --set "$system"
-
-ln -sfn /nix/var/nix/profiles/system $mountPoint/run/current-system
-
-# Copy the NixOS/Nixpkgs sources to the target as the initial contents of the NixOS channel.
-install -m 0755 -d $mountPoint/nix/var/nix/profiles
-install -m 1777 -d $mountPoint/nix/var/nix/profiles/per-user
-install -m 0755 -d $mountPoint/nix/var/nix/profiles/per-user/root
-
-if [ -z "$noChannelCopy" ] && [ -n "$channel" ]; then
-    echo "copying channel..."
-    nix-env --option build-use-substitutes false "${extraBuildFlags[@]}" -p $mountPoint/nix/var/nix/profiles/per-user/root/channels --set "$channel" --quiet
-fi
-install -m 0700 -d $mountPoint/root/.nix-defexpr
-ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
-
-# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out.
-touch $mountPoint/etc/NIXOS
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 9ede74a54cd7..2af73519bc52 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -382,6 +382,6 @@ fi
 if [ "$action" = build-vm ]; then
     cat >&2 <<EOF
 
-Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm).
+Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm)
 EOF
 fi
diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh
index 77a1b458a342..190c49a33ec6 100644
--- a/nixos/modules/installer/tools/nixos-version.sh
+++ b/nixos/modules/installer/tools/nixos-version.sh
@@ -6,9 +6,9 @@ case "$1" in
     exit 1
     ;;
   --hash|--revision)
-    echo "@nixosRevision@"
+    echo "@revision@"
     ;;
   *)
-    echo "@nixosVersion@ (@nixosCodeName@)"
+    echo "@version@ (@codeName@)"
     ;;
 esac
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index a3bae78c0ffc..beac9e29d59c 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -1,7 +1,9 @@
 # This module generates nixos-install, nixos-rebuild,
 # nixos-generate-config, etc.
 
-{ config, pkgs, modulesPath, ... }:
+{ config, lib, pkgs, modulesPath, ... }:
+
+with lib;
 
 let
   cfg = config.installer;
@@ -16,28 +18,11 @@ let
     src = ./nixos-build-vms/nixos-build-vms.sh;
   };
 
-  nixos-prepare-root = makeProg {
-    name = "nixos-prepare-root";
-    src = ./nixos-prepare-root.sh;
-
-    nix = pkgs.nixUnstable;
-    inherit (pkgs) perl pathsFromGraph rsync utillinux coreutils;
-  };
-
   nixos-install = makeProg {
     name = "nixos-install";
     src = ./nixos-install.sh;
-
-    inherit (pkgs) perl pathsFromGraph rsync;
     nix = config.nix.package.out;
-    cacert = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
-    root_uid = config.ids.uids.root;
-    nixbld_gid = config.ids.gids.nixbld;
-    prepare_root = nixos-prepare-root;
-
-    nixClosure = pkgs.runCommand "closure"
-      { exportReferencesGraph = ["refs" config.nix.package.out]; }
-      "cp refs $out";
+    path = makeBinPath [ nixos-enter ];
   };
 
   nixos-rebuild =
@@ -55,7 +40,7 @@ let
     src = ./nixos-generate-config.pl;
     path = [ pkgs.btrfs-progs ];
     perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
-    inherit (config.system) nixosRelease;
+    inherit (config.system.nixos) release;
   };
 
   nixos-option = makeProg {
@@ -66,7 +51,12 @@ let
   nixos-version = makeProg {
     name = "nixos-version";
     src = ./nixos-version.sh;
-    inherit (config.system) nixosVersion nixosCodeName nixosRevision;
+    inherit (config.system.nixos) version codeName revision;
+  };
+
+  nixos-enter = makeProg {
+    name = "nixos-enter";
+    src = ./nixos-enter.sh;
   };
 
 in
@@ -77,16 +67,16 @@ in
 
     environment.systemPackages =
       [ nixos-build-vms
-        nixos-prepare-root
         nixos-install
         nixos-rebuild
         nixos-generate-config
         nixos-option
         nixos-version
+        nixos-enter
       ];
 
     system.build = {
-      inherit nixos-install nixos-prepare-root nixos-generate-config nixos-option nixos-rebuild;
+      inherit nixos-install nixos-prepare-root nixos-generate-config nixos-option nixos-rebuild nixos-enter;
     };
 
   };
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 5316cfce906b..13a0d7f4f6ee 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -19,4 +19,6 @@ with lib;
   # Add some more video drivers to give X11 a shot at working in
   # VMware and QEMU.
   services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ];
+
+  powerManagement.enable = false;
 }
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
new file mode 100644
index 000000000000..cea8981370bb
--- /dev/null
+++ b/nixos/modules/misc/documentation.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.documentation; in
+
+{
+
+  options = {
+
+    documentation = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install documentation of packages from
+          <option>environment.systemPackages</option> into the generated system path.
+        '';
+      };
+
+      man.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install manual pages and the <command>man</command> command.
+          This also includes "man" outputs.
+        '';
+      };
+
+      doc.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install documentation distributed in packages' <literal>/share/doc</literal>.
+          Usually plain text and/or HTML.
+          This also includes "doc" outputs.
+        '';
+      };
+
+      info.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install info pages and the <command>info</command> command.
+          This also includes "info" outputs.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+
+    (mkIf cfg.man.enable {
+      environment.systemPackages = [ pkgs.man-db ];
+      environment.pathsToLink = [ "/share/man" ];
+      environment.extraOutputsToInstall = [ "man" ];
+    })
+
+    (mkIf cfg.doc.enable {
+      # TODO(@oxij): put it here and remove from profiles?
+      # environment.systemPackages = [ pkgs.w3m ]; # w3m-nox?
+      environment.pathsToLink = [ "/share/doc" ];
+      environment.extraOutputsToInstall = [ "doc" ];
+    })
+
+    (mkIf cfg.info.enable {
+      environment.systemPackages = [ pkgs.texinfoInteractive ];
+      environment.pathsToLink = [ "/share/info" ];
+      environment.extraOutputsToInstall = [ "info" ];
+    })
+
+  ]);
+
+}
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 7d0cbf6aad02..ab3cbcab0646 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -56,7 +56,7 @@
       #dialout = 27; # unused
       polkituser = 28;
       #utmp = 29; # unused
-      ddclient = 30;
+      # ddclient = 30; # converted to DynamicUser = true
       davfs2 = 31;
       #disnix = 33; # unused
       osgi = 34;
@@ -65,7 +65,7 @@
       foldingathome = 37;
       sabnzbd = 38;
       #kdm = 39; # dropped in 17.03
-      ghostone = 40;
+      #ghostone = 40; # dropped in 18.03
       git = 41;
       fourstore = 42;
       fourstorehttp = 43;
@@ -106,7 +106,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
@@ -197,10 +197,10 @@
       #input = 174; # unused
       sddm = 175;
       tss = 176;
-      memcached = 177;
+      #memcached = 177; removed 2018-01-03
       ntp = 179;
       zabbix = 180;
-      redis = 181;
+      #redis = 181; removed 2018-01-03
       unifi = 183;
       uptimed = 184;
       zope2 = 185;
@@ -233,7 +233,7 @@
       calibre-server = 213;
       heapster = 214;
       bepasty = 215;
-      pumpio = 216;
+      # pumpio = 216; # unused, removed 2018-02-24
       nm-openvpn = 217;
       mathics = 218;
       ejabberd = 219;
@@ -281,8 +281,8 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      keystone = 265;
-      glance = 266;
+      # keystone = 265; # unused, removed 2017-12-13
+      # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
       pdns-recursor = 269;
@@ -300,6 +300,12 @@
       kanboard = 281;
       pykms = 282;
       kodi = 283;
+      restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
+      ceph = 288;
+      duplicati = 289;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -338,7 +344,7 @@
       dialout = 27;
       #polkituser = 28; # currently unused, polkitd doesn't need a group
       utmp = 29;
-      ddclient = 30;
+      # ddclient = 30; # converted to DynamicUser = true
       davfs2 = 31;
       disnix = 33;
       osgi = 34;
@@ -347,7 +353,7 @@
       #foldingathome = 37; # unused
       #sabnzd = 38; # unused
       #kdm = 39; # unused, even before 17.03
-      ghostone = 40;
+      #ghostone = 40; # dropped in 18.03
       git = 41;
       fourstore = 42;
       fourstorehttp = 43;
@@ -388,7 +394,7 @@
       freenet = 79;
       ircd = 80;
       bacula = 81;
-      almir = 82;
+      #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08
       deluge = 83;
       mysql = 84;
       rabbitmq = 85;
@@ -474,10 +480,10 @@
       input = 174;
       sddm = 175;
       tss = 176;
-      #memcached = 177; # unused
+      #memcached = 177; # unused, removed 2018-01-03
       #ntp = 179; # unused
       #zabbix = 180; # unused
-      #redis = 181; # unused
+      #redis = 181; # unused, removed 2018-01-03
       #unifi = 183; # unused
       #uptimed = 184; # unused
       #zope2 = 185; # unused
@@ -509,7 +515,7 @@
       xtreemfs = 212;
       calibre-server = 213;
       bepasty = 215;
-      pumpio = 216;
+      # pumpio = 216; # unused, removed 2018-02-24
       nm-openvpn = 217;
       mathics = 218;
       ejabberd = 219;
@@ -550,8 +556,8 @@
       stanchion = 262;
       riak-cs = 263;
       infinoted = 264;
-      keystone = 265;
-      glance = 266;
+      # keystone = 265; # unused, removed 2017-12-13
+      # glance = 266; # unused, removed 2017-12-13
       couchpotato = 267;
       gogs = 268;
       kresd = 270;
@@ -568,6 +574,12 @@
       kanboard = 281;
       pykms = 282;
       kodi = 283;
+      restya-board = 284;
+      mighttpd2 = 285;
+      hass = 286;
+      monero = 287;
+      ceph = 288;
+      duplicati = 289;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/misc/label.nix b/nixos/modules/misc/label.nix
new file mode 100644
index 000000000000..8e5e57b3b83b
--- /dev/null
+++ b/nixos/modules/misc/label.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.system.nixos;
+in
+
+{
+
+  options.system = {
+
+    nixos.label = mkOption {
+      type = types.str;
+      description = ''
+        NixOS version name to be used in the names of generated
+        outputs and boot labels.
+
+        If you ever wanted to influence the labels in your GRUB menu,
+        this is the option for you.
+
+        The default is <option>system.nixos.tags</option> separated by
+        "-" + "-" + <envar>NIXOS_LABEL_VERSION</envar> environment
+        variable (defaults to the value of
+        <option>system.nixos.version</option>).
+
+        Can be overriden by setting <envar>NIXOS_LABEL</envar>.
+
+        Useful for not loosing track of configurations built from different
+        nixos branches/revisions, e.g.:
+
+        <screen>
+        #!/bin/sh
+        today=`date +%Y%m%d`
+        branch=`(cd nixpkgs ; git branch 2>/dev/null | sed -n '/^\* / { s|^\* ||; p; }')`
+        revision=`(cd nixpkgs ; git rev-parse HEAD)`
+        export NIXOS_LABEL_VERSION="$today.$branch-''${revision:0:7}"
+        nixos-rebuild switch</screen>
+      '';
+    };
+
+    nixos.tags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "with-xen" ];
+      description = ''
+        Strings to prefix to the default
+        <option>system.nixos.label</option>.
+
+        Useful for not loosing track of configurations built with
+        different options, e.g.:
+
+        <screen>
+        {
+          system.nixos.tags = [ "with-xen" ];
+          virtualisation.xen.enable = true;
+        }
+        </screen>
+      '';
+    };
+
+  };
+
+  config = {
+    # This is set here rather than up there so that changing it would
+    # not rebuild the manual
+    system.nixos.label = mkDefault (maybeEnv "NIXOS_LABEL"
+                                             (concatStringsSep "-" ((sort (x: y: x < y) cfg.tags)
+                                              ++ [ (maybeEnv "NIXOS_LABEL_VERSION" cfg.version) ])));
+  };
+
+}
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index 51953d1110c4..ce5765cf1978 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -97,7 +97,7 @@ in {
         Whether not to index bind mounts
       '';
     };
-    
+
   };
 
   config = mkIf cfg.enable {
@@ -133,13 +133,26 @@ in {
     systemd.services.update-locatedb =
       { description = "Update Locate Database";
         path = mkIf (!isMLocate) [ pkgs.su ];
+
+        # mlocate's updatedb takes flags via a configuration file or
+        # on the command line, but not by environment variable.
         script =
+          if isMLocate
+          then let toFlags = x: optional (cfg.${x} != [])
+                                         "--${lib.toLower x} '${concatStringsSep " " cfg.${x}}'";
+                   args = concatLists (map toFlags ["pruneFS" "pruneNames" "prunePaths"]);
+               in ''
+            exec ${cfg.locate}/bin/updatedb \
+              --output ${toString cfg.output} ${concatStringsSep " " args} \
+              --prune-bind-mounts ${if cfg.pruneBindMounts then "yes" else "no"} \
+              ${concatStringsSep " " cfg.extraFlags}
           ''
+          else ''
             exec ${cfg.locate}/bin/updatedb \
               ${optionalString (cfg.localuser != null && ! isMLocate) ''--localuser=${cfg.localuser}''} \
               --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
           '';
-        environment = {
+        environment = optionalAttrs (!isMLocate) {
           PRUNEFS = concatStringsSep " " cfg.pruneFS;
           PRUNEPATHS = concatStringsSep " " cfg.prunePaths;
           PRUNENAMES = concatStringsSep " " cfg.pruneNames;
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 1793c1447d60..b8a55a24394e 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -3,11 +3,13 @@
 with lib;
 
 let
+  cfg = config.nixpkgs;
+
   isConfig = x:
-    builtins.isAttrs x || builtins.isFunction x;
+    builtins.isAttrs x || lib.isFunction x;
 
   optCall = f: x:
-    if builtins.isFunction f
+    if lib.isFunction f
     then f x
     else f;
 
@@ -38,27 +40,72 @@ let
   overlayType = mkOptionType {
     name = "nixpkgs-overlay";
     description = "nixpkgs overlay";
-    check = builtins.isFunction;
+    check = lib.isFunction;
     merge = lib.mergeOneOption;
   };
 
-  _pkgs = import ../../.. config.nixpkgs;
+  pkgsType = mkOptionType {
+    name = "nixpkgs";
+    description = "An evaluation of Nixpkgs; the top level attribute set of packages";
+    check = builtins.isAttrs;
+  };
 
 in
 
 {
   options.nixpkgs = {
+
+    pkgs = mkOption {
+      defaultText = literalExample
+        ''import "''${nixos}/.." {
+            inherit (config.nixpkgs) config overlays localSystem crossSystem;
+          }
+        '';
+      default = import ../../.. {
+        localSystem = { inherit (cfg) system; } // cfg.localSystem;
+        inherit (cfg) config overlays crossSystem;
+      };
+      type = pkgsType;
+      example = literalExample ''import <nixpkgs> {}'';
+      description = ''
+        This is the evaluation of Nixpkgs that will be provided to
+        all NixOS modules. Defining this option has the effect of
+        ignoring the other options that would otherwise be used to
+        evaluate Nixpkgs, because those are arguments to the default
+        value. The default value imports the Nixpkgs source files
+        relative to the location of this NixOS module, because
+        NixOS and Nixpkgs are distributed together for consistency,
+        so the <code>nixos</code> in the default value is in fact a
+        relative path. The <code>config</code>, <code>overlays</code>,
+        <code>localSystem</code>, and <code>crossSystem</code> come
+        from this option's siblings.
+
+        This option can be used by applications like NixOps to increase
+        the performance of evaluation, or to create packages that depend
+        on a container that should be built with the exact same evaluation
+        of Nixpkgs, for example. Applications like this should set
+        their default value using <code>lib.mkDefault</code>, so
+        user-provided configuration can override it without using
+        <code>lib</code>.
+
+        Note that using a distinct version of Nixpkgs with NixOS may
+        be an unexpected source of problems. Use this option with care.
+      '';
+    };
+
     config = mkOption {
       default = {};
       example = literalExample
         ''
-          { firefox.enableGeckoMediaPlayer = true; }
+          { allowBroken = true; allowUnfree = true; }
         '';
       type = configType;
       description = ''
         The configuration of the Nix Packages collection.  (For
         details, see the Nixpkgs documentation.)  It allows you to set
         package configuration options.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -69,7 +116,6 @@ in
           [ (self: super: {
               openssh = super.openssh.override {
                 hpnSupport = true;
-                withKerberos = true;
                 kerberos = self.libkrb5;
               };
             };
@@ -83,6 +129,47 @@ in
         takes as an argument the <emphasis>original</emphasis> Nixpkgs.
         The first argument should be used for finding dependencies, and
         the second should be used for overriding recipes.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    localSystem = mkOption {
+      type = types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = { system = builtins.currentSystem; };
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      defaultText = literalExample
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform on which NixOS should be built. When
+        <code>nixpkgs.crossSystem</code> is unset, it also specifies
+        the platform <emphasis>for</emphasis> which NixOS should be
+        built.  If this option is unset, it defaults to the platform
+        type of the machine where evaluation happens. Specifying this
+        option is useful when doing distributed multi-platform
+        deployment, or when building virtual machines. See its
+        description in the Nixpkgs manual for more details.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    crossSystem = mkOption {
+      type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = null;
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      defaultText = literalExample
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform for which NixOS should be
+        built. Specify this only if it is different from
+        <code>nixpkgs.localSystem</code>, the platform
+        <emphasis>on</emphasis> which NixOS should be built. In other
+        words, specify this to cross-compile NixOS. Otherwise it
+        should be set as null, the default. See its description in the
+        Nixpkgs manual for more details.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
 
@@ -90,18 +177,30 @@ in
       type = types.str;
       example = "i686-linux";
       description = ''
-        Specifies the Nix platform type for which NixOS should be built.
-        If unset, it defaults to the platform type of your host system.
-        Specifying this option is useful when doing distributed
-        multi-platform deployment, or when building virtual machines.
+        Specifies the Nix platform type on which NixOS should be built.
+        It is better to specify <code>nixpkgs.localSystem</code> instead.
+        <programlisting>
+        {
+          nixpkgs.system = ..;
+        }
+        </programlisting>
+        is the same as
+        <programlisting>
+        {
+          nixpkgs.localSystem.system = ..;
+        }
+        </programlisting>
+        See <code>nixpkgs.localSystem</code> for more information.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
       '';
     };
   };
 
   config = {
     _module.args = {
-      pkgs = _pkgs;
-      pkgs_i686 = _pkgs.pkgsi686Linux;
+      pkgs = cfg.pkgs;
+      pkgs_i686 = cfg.pkgs.pkgsi686Linux;
     };
   };
 }
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 48cde2ebbc8a..7519d9176982 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  cfg = config.system;
+  cfg = config.system.nixos;
 
   releaseFile  = "${toString pkgs.path}/.version";
   suffixFile   = "${toString pkgs.path}/.version-suffix";
@@ -16,51 +16,42 @@ in
 
   options.system = {
 
-    stateVersion = mkOption {
+    # XXX: Reintroduce old options to make nixops before 1.6 able to evaluate configurations
+    # XXX: Remove after nixops has been bumped to a compatible version
+    nixosVersion = mkOption {
+      readOnly = true;
+      internal = true;
       type = types.str;
-      default = cfg.nixosRelease;
-      description = ''
-        Every once in a while, a new NixOS release may change
-        configuration defaults in a way incompatible with stateful
-        data. For instance, if the default version of PostgreSQL
-        changes, the new version will probably be unable to read your
-        existing databases. To prevent such breakage, you can set the
-        value of this option to the NixOS release with which you want
-        to be compatible. The effect is that NixOS will option
-        defaults corresponding to the specified release (such as using
-        an older version of PostgreSQL).
-      '';
+      default = config.system.nixos.version;
     };
-
-    nixosLabel = mkOption {
+    nixosVersionSuffix = mkOption {
+      readOnly = true;
+      internal = true;
       type = types.str;
-      description = ''
-        Label to be used in the names of generated outputs and boot
-        labels.
-      '';
+      default = config.system.nixos.versionSuffix;
     };
 
-    nixosVersion = mkOption {
+    nixos.version = mkOption {
       internal = true;
       type = types.str;
       description = "The full NixOS version (e.g. <literal>16.03.1160.f2d4ee1</literal>).";
     };
 
-    nixosRelease = mkOption {
+    nixos.release = mkOption {
       readOnly = true;
       type = types.str;
       default = fileContents releaseFile;
       description = "The NixOS release (e.g. <literal>16.03</literal>).";
     };
 
-    nixosVersionSuffix = mkOption {
+    nixos.versionSuffix = mkOption {
       internal = true;
       type = types.str;
       default = if pathExists suffixFile then fileContents suffixFile else "pre-git";
       description = "The NixOS version suffix (e.g. <literal>1160.f2d4ee1</literal>).";
     };
 
-    nixosRevision = mkOption {
+    nixos.revision = mkOption {
       internal = true;
       type = types.str;
       default = if pathIsDirectory gitRepo then commitIdFromGitRepo gitRepo
@@ -69,12 +60,28 @@ in
       description = "The Git revision from which this NixOS configuration was built.";
     };
 
-    nixosCodeName = mkOption {
+    nixos.codeName = mkOption {
       readOnly = true;
       type = types.str;
       description = "The NixOS release code name (e.g. <literal>Emu</literal>).";
     };
 
+    stateVersion = mkOption {
+      type = types.str;
+      default = cfg.release;
+      description = ''
+        Every once in a while, a new NixOS release may change
+        configuration defaults in a way incompatible with stateful
+        data. For instance, if the default version of PostgreSQL
+        changes, the new version will probably be unable to read your
+        existing databases. To prevent such breakage, you can set the
+        value of this option to the NixOS release with which you want
+        to be compatible. The effect is that NixOS will option
+        defaults corresponding to the specified release (such as using
+        an older version of PostgreSQL).
+      '';
+    };
+
     defaultChannel = mkOption {
       internal = true;
       type = types.str;
@@ -86,16 +93,15 @@ in
 
   config = {
 
-    system = {
+    system.nixos = {
       # These defaults are set here rather than up there so that
       # changing them would not rebuild the manual
-      nixosLabel   = mkDefault cfg.nixosVersion;
-      nixosVersion = mkDefault (cfg.nixosRelease + cfg.nixosVersionSuffix);
-      nixosRevision      = mkIf (pathIsDirectory gitRepo) (mkDefault            gitCommitId);
-      nixosVersionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
+      version = mkDefault (cfg.release + cfg.versionSuffix);
+      revision      = mkIf (pathIsDirectory gitRepo) (mkDefault            gitCommitId);
+      versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId));
 
-      # Note: code names must only increase in alphabetical order.
-      nixosCodeName = "Impala";
+      # Note: the first letter is bumped on every release.  It's an animal.
+      codeName = "Jellyfish";
     };
 
     # Generate /etc/os-release.  See
@@ -105,10 +111,10 @@ in
       ''
         NAME=NixOS
         ID=nixos
-        VERSION="${config.system.nixosVersion} (${config.system.nixosCodeName})"
-        VERSION_CODENAME=${toLower config.system.nixosCodeName}
-        VERSION_ID="${config.system.nixosVersion}"
-        PRETTY_NAME="NixOS ${config.system.nixosVersion} (${config.system.nixosCodeName})"
+        VERSION="${cfg.version} (${cfg.codeName})"
+        VERSION_CODENAME=${toLower cfg.codeName}
+        VERSION_ID="${cfg.version}"
+        PRETTY_NAME="NixOS ${cfg.version} (${cfg.codeName})"
         HOME_URL="https://nixos.org/"
         SUPPORT_URL="https://nixos.org/nixos/support.html"
         BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues"
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index d63606489649..74c8c7bbc820 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -31,6 +31,7 @@
   ./hardware/ckb.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/intel-microcode.nix
+  ./hardware/digitalbitbox.nix
   ./hardware/sensor/iio.nix
   ./hardware/ksm.nix
   ./hardware/mcelog.nix
@@ -40,6 +41,7 @@
   ./hardware/pcmcia.nix
   ./hardware/raid/hpsa.nix
   ./hardware/usb-wwan.nix
+  ./hardware/onlykey.nix
   ./hardware/video/amdgpu.nix
   ./hardware/video/amdgpu-pro.nix
   ./hardware/video/ati.nix
@@ -57,9 +59,11 @@
   ./installer/tools/tools.nix
   ./misc/assertions.nix
   ./misc/crashdump.nix
+  ./misc/documentation.nix
   ./misc/extra-arguments.nix
   ./misc/ids.nix
   ./misc/lib.nix
+  ./misc/label.nix
   ./misc/locate.nix
   ./misc/meta.nix
   ./misc/nixpkgs.nix
@@ -71,44 +75,55 @@
   ./programs/bcc.nix
   ./programs/blcr.nix
   ./programs/browserpass.nix
+  ./programs/ccache.nix
   ./programs/cdemu.nix
   ./programs/chromium.nix
   ./programs/command-not-found/command-not-found.nix
+  ./programs/criu.nix
   ./programs/dconf.nix
+  ./programs/digitalbitbox/default.nix
   ./programs/environment.nix
   ./programs/fish.nix
   ./programs/freetds.nix
   ./programs/gnupg.nix
   ./programs/gphoto2.nix
-  ./programs/info.nix
+  ./programs/iftop.nix
   ./programs/java.nix
   ./programs/kbdlight.nix
+  ./programs/less.nix
   ./programs/light.nix
-  ./programs/man.nix
   ./programs/mosh.nix
   ./programs/mtr.nix
   ./programs/nano.nix
   ./programs/npm.nix
   ./programs/oblogout.nix
+  ./programs/plotinus.nix
   ./programs/qt5ct.nix
+  ./programs/rootston.nix
   ./programs/screen.nix
   ./programs/slock.nix
   ./programs/shadow.nix
   ./programs/shell.nix
   ./programs/spacefm.nix
+  ./programs/singularity.nix
   ./programs/ssh.nix
   ./programs/ssmtp.nix
   ./programs/sysdig.nix
+  ./programs/systemtap.nix
   ./programs/sway.nix
   ./programs/thefuck.nix
   ./programs/tmux.nix
+  ./programs/udevil.nix
   ./programs/venus.nix
   ./programs/vim.nix
+  ./programs/way-cooler.nix
   ./programs/wireshark.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
+  ./programs/yabar.nix
   ./programs/zsh/oh-my-zsh.nix
   ./programs/zsh/zsh.nix
+  ./programs/zsh/zsh-autoenv.nix
   ./programs/zsh/zsh-syntax-highlighting.nix
   ./rename.nix
   ./security/acme.nix
@@ -144,11 +159,14 @@
   ./services/audio/slimserver.nix
   ./services/audio/squeezelite.nix
   ./services/audio/ympd.nix
-  ./services/backup/almir.nix
   ./services/backup/bacula.nix
+  ./services/backup/borgbackup.nix
+  ./services/backup/duplicati.nix
   ./services/backup/crashplan.nix
+  ./services/backup/crashplan-small-business.nix
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
+  ./services/backup/restic.nix
   ./services/backup/rsnapshot.nix
   ./services/backup/tarsnap.nix
   ./services/backup/znapzend.nix
@@ -196,7 +214,9 @@
   ./services/desktops/dleyna-renderer.nix
   ./services/desktops/dleyna-server.nix
   ./services/desktops/geoclue2.nix
+  ./services/desktops/pipewire.nix
   ./services/desktops/gnome3/at-spi2-core.nix
+  ./services/desktops/gnome3/chrome-gnome-shell.nix
   ./services/desktops/gnome3/evolution-data-server.nix
   ./services/desktops/gnome3/gnome-disks.nix
   ./services/desktops/gnome3/gnome-documents.nix
@@ -210,22 +230,22 @@
   ./services/desktops/gnome3/seahorse.nix
   ./services/desktops/gnome3/sushi.nix
   ./services/desktops/gnome3/tracker.nix
+  ./services/desktops/gnome3/tracker-miners.nix
   ./services/desktops/profile-sync-daemon.nix
   ./services/desktops/telepathy.nix
   ./services/development/hoogle.nix
   ./services/editors/emacs.nix
   ./services/editors/infinoted.nix
   ./services/games/factorio.nix
-  ./services/games/ghost-one.nix
   ./services/games/minecraft-server.nix
   ./services/games/minetest-server.nix
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
-  ./services/hardware/amd-hybrid-graphics.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/brltty.nix
   ./services/hardware/freefall.nix
+  ./services/hardware/fwupd.nix
   ./services/hardware/illum.nix
   ./services/hardware/interception-tools.nix
   ./services/hardware/irqbalance.nix
@@ -233,13 +253,16 @@
   ./services/hardware/pcscd.nix
   ./services/hardware/pommed.nix
   ./services/hardware/sane.nix
+  ./services/hardware/sane_extra_backends/brscan4.nix
   ./services/hardware/tcsd.nix
   ./services/hardware/tlp.nix
   ./services/hardware/thinkfan.nix
   ./services/hardware/trezord.nix
+  ./services/hardware/u2f.nix
   ./services/hardware/udev.nix
   ./services/hardware/udisks2.nix
   ./services/hardware/upower.nix
+  ./services/hardware/usbmuxd.nix
   ./services/hardware/thermald.nix
   ./services/logging/SystemdJournal2Gelf.nix
   ./services/logging/awstats.nix
@@ -255,6 +278,8 @@
   ./services/logging/rsyslogd.nix
   ./services/logging/syslog-ng.nix
   ./services/logging/syslogd.nix
+  ./services/mail/clamsmtp.nix
+  ./services/mail/dkimproxy-out.nix
   ./services/mail/dovecot.nix
   ./services/mail/dspam.nix
   ./services/mail/exim.nix
@@ -301,11 +326,13 @@
   ./services/misc/geoip-updater.nix
   ./services/misc/gitea.nix
   #./services/misc/gitit.nix
-  ./services/misc/gitlab.nix
+  #./services/misc/gitlab.nix
   ./services/misc/gitolite.nix
+  ./services/misc/gitweb.nix
   ./services/misc/gogs.nix
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
+  ./services/misc/home-assistant.nix
   ./services/misc/ihaskell.nix
   ./services/misc/irkerd.nix
   ./services/misc/jackett.nix
@@ -324,8 +351,10 @@
   ./services/misc/nix-optimise.nix
   ./services/misc/nixos-manual.nix
   ./services/misc/nix-ssh-serve.nix
+  ./services/misc/novacomd.nix
   ./services/misc/nzbget.nix
   ./services/misc/octoprint.nix
+  ./services/misc/osrm.nix
   ./services/misc/packagekit.nix
   ./services/misc/parsoid.nix
   ./services/misc/phd.nix
@@ -337,6 +366,8 @@
   ./services/misc/rippled.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rogue.nix
+  ./services/misc/serviio.nix
+  ./services/misc/safeeyes.nix
   ./services/misc/siproxd.nix
   ./services/misc/snapper.nix
   ./services/misc/sonarr.nix
@@ -350,6 +381,7 @@
   ./services/misc/taskserver
   ./services/misc/tzupdate.nix
   ./services/misc/uhub.nix
+  ./services/misc/xmr-stak.nix
   ./services/misc/zookeeper.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/arbtt.nix
@@ -371,16 +403,7 @@
   ./services/monitoring/osquery.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
-  ./services/monitoring/prometheus/blackbox-exporter.nix
-  ./services/monitoring/prometheus/collectd-exporter.nix
-  ./services/monitoring/prometheus/fritzbox-exporter.nix
-  ./services/monitoring/prometheus/json-exporter.nix
-  ./services/monitoring/prometheus/minio-exporter.nix
-  ./services/monitoring/prometheus/nginx-exporter.nix
-  ./services/monitoring/prometheus/node-exporter.nix
-  ./services/monitoring/prometheus/snmp-exporter.nix
-  ./services/monitoring/prometheus/unifi-exporter.nix
-  ./services/monitoring/prometheus/varnish-exporter.nix
+  ./services/monitoring/prometheus/exporters.nix
   ./services/monitoring/riemann.nix
   ./services/monitoring/riemann-dash.nix
   ./services/monitoring/riemann-tools.nix
@@ -396,14 +419,17 @@
   ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
   ./services/monitoring/zabbix-server.nix
+  ./services/network-filesystems/beegfs.nix
   ./services/network-filesystems/cachefilesd.nix
+  ./services/network-filesystems/davfs2.nix
   ./services/network-filesystems/drbd.nix
   ./services/network-filesystems/glusterfs.nix
   ./services/network-filesystems/kbfs.nix
   ./services/network-filesystems/ipfs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
-  ./services/network-filesystems/openafs-client/default.nix
+  ./services/network-filesystems/openafs/client.nix
+  ./services/network-filesystems/openafs/server.nix
   ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/network-filesystems/tahoe.nix
@@ -411,7 +437,9 @@
   ./services/network-filesystems/u9fs.nix
   ./services/network-filesystems/yandex-disk.nix
   ./services/network-filesystems/xtreemfs.nix
+  ./services/network-filesystems/ceph.nix
   ./services/networking/amuled.nix
+  ./services/networking/aria2.nix
   ./services/networking/asterisk.nix
   ./services/networking/atftpd.nix
   ./services/networking/avahi-daemon.nix
@@ -476,6 +504,7 @@
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/mosquitto.nix
+  ./services/networking/monero.nix
   ./services/networking/miredo.nix
   ./services/networking/mstpd.nix
   ./services/networking/murmur.nix
@@ -486,6 +515,7 @@
   ./services/networking/ngircd.nix
   ./services/networking/nghttpx/default.nix
   ./services/networking/nix-serve.nix
+  ./services/networking/nixops-dns.nix
   ./services/networking/nntp-proxy.nix
   ./services/networking/nsd.nix
   ./services/networking/ntopng.nix
@@ -504,7 +534,7 @@
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
-  # ./services/networking/quagga.nix
+  ./services/networking/quagga.nix
   ./services/networking/quassel.nix
   ./services/networking/racoon.nix
   ./services/networking/radicale.nix
@@ -513,10 +543,12 @@
   ./services/networking/redsocks.nix
   ./services/networking/resilio.nix
   ./services/networking/rpcbind.nix
+  ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
   ./services/networking/searx.nix
   ./services/networking/seeks.nix
   ./services/networking/skydns.nix
+  ./services/networking/shadowsocks.nix
   ./services/networking/shairport-sync.nix
   ./services/networking/shout.nix
   ./services/networking/sniproxy.nix
@@ -528,6 +560,7 @@
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
   ./services/networking/strongswan.nix
+  ./services/networking/stunnel.nix
   ./services/networking/supplicant.nix
   ./services/networking/supybot.nix
   ./services/networking/syncthing.nix
@@ -586,6 +619,7 @@
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
   ./services/system/earlyoom.nix
+  ./services/system/localtime.nix
   ./services/system/kerberos.nix
   ./services/system/nscd.nix
   ./services/system/saslauthd.nix
@@ -603,11 +637,10 @@
   ./services/web-apps/atlassian/jira.nix
   ./services/web-apps/frab.nix
   ./services/web-apps/mattermost.nix
-  ./services/web-apps/nixbot.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/pgpkeyserver-lite.nix
-  ./services/web-apps/piwik.nix
-  ./services/web-apps/pump.io.nix
+  ./services/web-apps/matomo.nix
+  ./services/web-apps/restya-board.nix
   ./services/web-apps/tt-rss.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/quassel-webserver.nix
@@ -620,8 +653,10 @@
   ./services/web-servers/lighttpd/default.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/lighttpd/inginious.nix
+  ./services/web-servers/mighttpd2.nix
   ./services/web-servers/minio.nix
   ./services/web-servers/nginx/default.nix
+  ./services/web-servers/nginx/gitweb.nix
   ./services/web-servers/phpfpm/default.nix
   ./services/web-servers/shellinabox.nix
   ./services/web-servers/tomcat.nix
@@ -666,8 +701,10 @@
   ./services/x11/xserver.nix
   ./system/activation/activation-script.nix
   ./system/activation/top-level.nix
+  ./system/boot/binfmt.nix
   ./system/boot/coredump.nix
   ./system/boot/emergency-mode.nix
+  ./system/boot/grow-partition.nix
   ./system/boot/initrd-network.nix
   ./system/boot/initrd-ssh.nix
   ./system/boot/kernel.nix
@@ -734,6 +771,7 @@
   ./virtualisation/lxcfs.nix
   ./virtualisation/lxd.nix
   ./virtualisation/amazon-options.nix
+  ./virtualisation/hyperv-guest.nix
   ./virtualisation/openvswitch.nix
   ./virtualisation/parallels-guest.nix
   ./virtualisation/rkt.nix
@@ -742,6 +780,4 @@
   ./virtualisation/vmware-guest.nix
   ./virtualisation/xen-dom0.nix
   ./virtualisation/xe-guest-utilities.nix
-  ./virtualisation/openstack/keystone.nix
-  ./virtualisation/openstack/glance.nix
 ]
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 3c7e516c497f..f56640f19782 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -19,13 +19,12 @@
       "sata_sil" "sata_sil24" "sata_sis" "sata_svw" "sata_sx4"
       "sata_uli" "sata_via" "sata_vsc"
 
-      "pata_ali" "pata_amd" "pata_artop" "pata_atiixp"
-      "pata_cs5520" "pata_cs5530" "pata_cs5535" "pata_efar"
+      "pata_ali" "pata_amd" "pata_artop" "pata_atiixp" "pata_efar"
       "pata_hpt366" "pata_hpt37x" "pata_hpt3x2n" "pata_hpt3x3"
       "pata_it8213" "pata_it821x" "pata_jmicron" "pata_marvell"
       "pata_mpiix" "pata_netcell" "pata_ns87410" "pata_oldpiix"
       "pata_pcmcia" "pata_pdc2027x" "pata_qdi" "pata_rz1000"
-      "pata_sc1200" "pata_serverworks" "pata_sil680" "pata_sis"
+      "pata_serverworks" "pata_sil680" "pata_sis"
       "pata_sl82c105" "pata_triflex" "pata_via"
       "pata_winbond"
 
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index 39b8553976eb..3bf06a951193 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -9,7 +9,7 @@
   environment.systemPackages = [
     pkgs.w3m-nox # needed for the manual anyway
     pkgs.testdisk # useful for repairing boot problems
-    pkgs.mssys # for writing Microsoft boot sectors / MBRs
+    pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
     pkgs.efibootmgr
     pkgs.efivar
     pkgs.parted
diff --git a/nixos/modules/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix
index 77d86f8d7405..5b4e68beb6a6 100644
--- a/nixos/modules/profiles/clone-config.nix
+++ b/nixos/modules/profiles/clone-config.nix
@@ -17,7 +17,7 @@ let
   # you should use files).
   moduleFiles =
     # FIXME: use typeOf (Nix 1.6.1).
-    filter (x: !isAttrs x && !builtins.isFunction x) modules;
+    filter (x: !isAttrs x && !lib.isFunction x) modules;
 
   # Partition module files because between NixOS and non-NixOS files.  NixOS
   # files may change if the repository is updated.
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
index ef6fd77b5f8d..c3ee6e98371e 100644
--- a/nixos/modules/profiles/demo.nix
+++ b/nixos/modules/profiles/demo.nix
@@ -10,4 +10,10 @@
       password = "demo";
       uid = 1000;
     };
+
+  services.xserver.displayManager.sddm.autoLogin = {
+    enable = true;
+    relogin = true;
+    user = "demo";
+  };
 }
diff --git a/nixos/modules/profiles/docker-container.nix b/nixos/modules/profiles/docker-container.nix
index 433492b96137..7031d7d1d593 100644
--- a/nixos/modules/profiles/docker-container.nix
+++ b/nixos/modules/profiles/docker-container.nix
@@ -14,9 +14,7 @@ in {
   ];
 
   # Create the tarball
-  system.build.tarball = import ../../lib/make-system-tarball.nix {
-    inherit (pkgs) stdenv perl xz pathsFromGraph;
-
+  system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix {
     contents = [];
     extraArgs = "--owner=0";
 
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 506a6ee3eaa8..43f06c219f82 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -72,7 +72,13 @@ with lib;
 
     # To speed up installation a little bit, include the complete
     # stdenv in the Nix store on the CD.
-    system.extraDependencies = with pkgs; [ stdenv stdenvNoCC busybox ];
+    system.extraDependencies = with pkgs;
+      [
+        stdenv
+        stdenvNoCC # for runCommand
+        busybox
+        jq # for closureInfo
+      ];
 
     # Show all debug messages from the kernel but don't log refused packets
     # because we have the firewall enabled. This makes installs from the
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index e2497d04252e..40df7063a9bf 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -10,10 +10,9 @@ with lib;
 
   # This isn't perfect, but let's expect the user specifies an UTF-8 defaultLocale
   i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
-  services.nixosManual.enable = mkDefault false;
 
-  programs.man.enable = mkDefault false;
-  programs.info.enable = mkDefault false;
+  documentation.enable = mkDefault false;
+  services.nixosManual.enable = mkDefault false;
 
   sound.enable = mkDefault false;
 }
diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix
index 987eb051b98c..a1ec1d45395e 100644
--- a/nixos/modules/profiles/qemu-guest.nix
+++ b/nixos/modules/profiles/qemu-guest.nix
@@ -4,7 +4,7 @@
 { config, pkgs, ... }:
 
 {
-  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
+  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" ];
   boot.initrd.kernelModules = [ "virtio_balloon" "virtio_console" "virtio_rng" ];
 
   boot.initrd.postDeviceCommands =
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
index 18290555b79d..f648d70bd9fa 100644
--- a/nixos/modules/programs/adb.nix
+++ b/nixos/modules/programs/adb.nix
@@ -16,6 +16,7 @@ with lib;
           To grant access to a user, it must be part of adbusers group:
           <code>users.extraUsers.alice.extraGroups = ["adbusers"];</code>
         '';
+        relatedPackages = [ ["androidenv" "platformTools"] ];
       };
     };
   };
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index e23849d350b4..69a1a482d074 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -14,13 +14,16 @@ let
   bashCompletion = optionalString cfg.enableCompletion ''
     # Check whether we're running a version of Bash that has support for
     # programmable completion. If we do, enable all modules installed in
-    # the system (and user profile).
+    # the system and user profile in obsolete /etc/bash_completion.d/
+    # directories. Bash loads completions in all
+    # $XDG_DATA_DIRS/share/bash-completion/completions/
+    # on demand, so they do not need to be sourced here.
     if shopt -q progcomp &>/dev/null; then
       . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
       nullglobStatus=$(shopt -p nullglob)
       shopt -s nullglob
       for p in $NIX_PROFILES; do
-        for m in "$p/etc/bash_completion.d/"* "$p/share/bash-completion/completions/"*; do
+        for m in "$p/etc/bash_completion.d/"*; do
           . $m
         done
       done
@@ -107,7 +110,7 @@ in
       };
 
       enableCompletion = mkOption {
-        default = false;
+        default = true;
         description = ''
           Enable Bash completion for all interactive bash shells.
         '';
@@ -123,7 +126,7 @@ in
     programs.bash = {
 
       shellInit = ''
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
       '';
@@ -197,8 +200,9 @@ in
         fi
       '';
 
-    # Configuration for readline in bash.
-    environment.etc."inputrc".source = ./inputrc;
+    # Configuration for readline in bash. We use "option default"
+    # priority to allow user override using both .text and .source.
+    environment.etc."inputrc".source = mkOptionDefault ./inputrc;
 
     users.defaultUserShell = mkDefault pkgs.bashInteractive;
 
@@ -207,6 +211,9 @@ in
       "/share/bash-completion"
     ];
 
+    environment.systemPackages = optional cfg.enableCompletion
+      pkgs.nix-bash-completions;
+
     environment.shells =
       [ "/run/current-system/sw/bin/bash"
         "/var/run/current-system/sw/bin/bash"
diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix
index a073c7e66eb8..5f8a44a9848e 100644
--- a/nixos/modules/programs/browserpass.nix
+++ b/nixos/modules/programs/browserpass.nix
@@ -18,5 +18,6 @@ with lib;
       "opt/chrome/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json";
       "opt/chrome/policies/managed/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-policy.json";
     };
+    nixpkgs.config.firefox.enableBrowserpass = true;
   };
 }
diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix
new file mode 100644
index 000000000000..874774c72b47
--- /dev/null
+++ b/nixos/modules/programs/ccache.nix
@@ -0,0 +1,83 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.ccache;
+in {
+  options.programs.ccache = {
+    # host configuration
+    enable = mkEnableOption "CCache";
+    cacheDir = mkOption {
+      type = types.path;
+      description = "CCache directory";
+      default = "/var/cache/ccache";
+    };
+    # target configuration
+    packageNames = mkOption {
+      type = types.listOf types.str;
+      description = "Nix top-level packages to be compiled using CCache";
+      default = [];
+      example = [ "wxGTK30" "qt48" "ffmpeg_3_3" "libav_all" ];
+    };
+  };
+
+  config = mkMerge [
+    # host configuration
+    (mkIf cfg.enable {
+      systemd.tmpfiles.rules = [ "d ${cfg.cacheDir} 0770 root nixbld -" ];
+
+      # "nix-ccache --show-stats" and "nix-ccache --clear"
+      security.wrappers.nix-ccache = {
+        group = "nixbld";
+        setgid = true;
+        source = pkgs.writeScript "nix-ccache.pl" ''
+          #!${pkgs.perl}/bin/perl
+
+          %ENV=( CCACHE_DIR => '${cfg.cacheDir}' );
+          sub untaint {
+            my $v = shift;
+            return '-C' if $v eq '-C' || $v eq '--clear';
+            return '-V' if $v eq '-V' || $v eq '--version';
+            return '-s' if $v eq '-s' || $v eq '--show-stats';
+            return '-z' if $v eq '-z' || $v eq '--zero-stats';
+            exec('${pkgs.ccache}/bin/ccache', '-h');
+          }
+          exec('${pkgs.ccache}/bin/ccache', map { untaint $_ } @ARGV);
+        '';
+      };
+    })
+
+    # target configuration
+    (mkIf (cfg.packageNames != []) {
+      nixpkgs.overlays = [
+        (self: super: genAttrs cfg.packageNames (pn: super.${pn}.override { stdenv = builtins.trace "with ccache: ${pn}" self.ccacheStdenv; }))
+
+        (self: super: {
+          ccacheWrapper = super.ccacheWrapper.override {
+            extraConfig = ''
+              export CCACHE_COMPRESS=1
+              export CCACHE_DIR="${cfg.cacheDir}"
+              export CCACHE_UMASK=007
+              if [ ! -d "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' does not exist"
+                echo "Please create it with:"
+                echo "  sudo mkdir -m0770 '$CCACHE_DIR'"
+                echo "  sudo chown root:nixbld '$CCACHE_DIR'"
+                echo "====="
+                exit 1
+              fi
+              if [ ! -w "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
+                echo "Please verify its access permissions"
+                echo "====="
+                exit 1
+              fi
+            '';
+          };
+        })
+      ];
+    })
+  ];
+}
\ No newline at end of file
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
index 54739feab976..41c49db8c71c 100644
--- a/nixos/modules/programs/chromium.nix
+++ b/nixos/modules/programs/chromium.nix
@@ -36,6 +36,7 @@ in
             "chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet
             "mbniclmhobmnbdlbpiphghaielnnpgdp" # lightshot
             "gcbommkclmclpchllfjekcdonpmejbdp" # https everywhere
+            "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin
           ]
         '';
       };
@@ -52,8 +53,7 @@ in
         description = "Chromium default search provider url.";
         default = null;
         example =
-          "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:
-        ↪searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
+          "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
       };
 
       defaultSearchProviderSuggestURL = mkOption {
@@ -79,7 +79,11 @@ in
   ###### implementation
 
   config = lib.mkIf cfg.enable {
+    # for chromium
     environment.etc."chromium/policies/managed/default.json".text = builtins.toJSON defaultProfile;
     environment.etc."chromium/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
+    # for google-chrome https://www.chromium.org/administrators/linux-quick-start
+    environment.etc."opt/chrome/policies/managed/default.json".text = builtins.toJSON defaultProfile;
+    environment.etc."opt/chrome/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
   };
 }
diff --git a/nixos/modules/programs/criu.nix b/nixos/modules/programs/criu.nix
new file mode 100644
index 000000000000..48cf5c88a9fc
--- /dev/null
+++ b/nixos/modules/programs/criu.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.criu;
+in {
+
+  options = {
+    programs.criu = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Install <command>criu</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "CHECKPOINT_RESTORE")
+    ];
+    boot.kernel.features.criu = true;
+    environment.systemPackages = [ pkgs.criu ];
+  };
+
+}
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
index 1b7e20799819..b7d8a345e65c 100644
--- a/nixos/modules/programs/dconf.nix
+++ b/nixos/modules/programs/dconf.nix
@@ -1,7 +1,8 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
+
+with lib;
 
 let
-  inherit (lib) mkOption mkIf types mapAttrsToList;
   cfg = config.programs.dconf;
 
   mkDconfProfile = name: path:
@@ -13,6 +14,7 @@ in
 
   options = {
     programs.dconf = {
+      enable = mkEnableOption "dconf";
 
       profiles = mkOption {
         type = types.attrsOf types.path;
@@ -26,9 +28,15 @@ in
 
   ###### implementation
 
-  config = mkIf (cfg.profiles != {}) {
-    environment.etc =
+  config = mkIf (cfg.profiles != {} || cfg.enable) {
+    environment.etc = optionals (cfg.profiles != {})
       (mapAttrsToList mkDconfProfile cfg.profiles);
+
+    environment.variables.GIO_EXTRA_MODULES = optional cfg.enable
+      "${pkgs.gnome3.dconf.lib}/lib/gio/modules";
+    # https://github.com/NixOS/nixpkgs/pull/31891
+    #environment.variables.XDG_DATA_DIRS = optional cfg.enable
+    #  "$(echo ${pkgs.gnome3.gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas-*)";
   };
 
 }
diff --git a/nixos/modules/programs/digitalbitbox/default.nix b/nixos/modules/programs/digitalbitbox/default.nix
new file mode 100644
index 000000000000..7c727489c6c9
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/default.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.digitalbitbox;
+in
+
+{
+  options.programs.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Installs the Digital Bitbox application and enables the complementary hardware module.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    hardware.digitalbitbox = {
+      enable = true;
+      package = cfg.package;
+    };
+  };
+
+  meta = {
+    doc = ./doc.xml;
+    maintainers = with stdenv.lib.maintainers; [ vidbina ];
+  };
+}
diff --git a/nixos/modules/programs/digitalbitbox/doc.xml b/nixos/modules/programs/digitalbitbox/doc.xml
new file mode 100644
index 000000000000..7acbc2fc4dde
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/doc.xml
@@ -0,0 +1,85 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-digitalbitbox">
+
+  <title>Digital Bitbox</title>
+
+  <para>
+    Digital Bitbox is a hardware wallet and second-factor authenticator.
+  </para>
+
+  <para>
+    The <literal>digitalbitbox</literal> programs module may be
+    installed by setting <literal>programs.digitalbitbox</literal>
+    to <literal>true</literal> in a manner similar to
+
+    <programlisting>
+      programs.digitalbitbox.enable = true;
+    </programlisting>
+
+    and bundles the <literal>digitalbitbox</literal> package (see <xref
+      linkend="sec-digitalbitbox-package" />), which contains the
+    <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries,
+    along with the hardware module (see <xref
+      linkend="sec-digitalbitbox-hardware-module" />) which sets up the
+    necessary udev rules to access the device.
+  </para>
+
+  <para>
+    Enabling the digitalbitbox module is pretty much the easiest way to
+    get a Digital Bitbox device working on your system.
+  </para>
+
+  <para>
+    For more information, see
+    <link xlink:href="https://digitalbitbox.com/start_linux" />.
+  </para>
+
+  <section xml:id="sec-digitalbitbox-package">
+    <title>Package</title>
+
+    <para>
+      The binaries, <literal>dbb-app</literal> (a GUI tool) and
+      <literal>dbb-cli</literal> (a CLI tool), are available through the
+      <literal>digitalbitbox</literal> package which could be installed
+      as follows:
+
+      <programlisting>
+        environment.systemPackages = [
+          pkgs.digitalbitbox
+        ];
+      </programlisting>
+    </para>
+  </section>
+
+
+  <section xml:id="sec-digitalbitbox-hardware-module">
+    <title>Hardware</title>
+
+    <para>
+      The digitalbitbox hardware package enables the udev rules for
+      Digital Bitbox devices and may be installed as follows:
+
+      <programlisting>
+        hardware.digitalbitbox.enable = true;
+      </programlisting>
+    </para>
+
+    <para>
+      In order to alter the udev rules, one may provide different values for
+      the <literal>udevRule51</literal> and <literal>udevRule52</literal>
+      attributes by means of overriding as follows:
+
+      <programlisting>
+        programs.digitalbitbox = {
+          enable = true;
+          package = pkgs.digitalbitbox.override {
+            udevRule51 = "something else";
+          };
+        };
+      </programlisting>
+    </para>
+  </section>
+</chapter>
diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix
new file mode 100644
index 000000000000..a98a9a8187d4
--- /dev/null
+++ b/nixos/modules/programs/iftop.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.iftop;
+in {
+  options = {
+    programs.iftop.enable = mkEnableOption "iftop + setcap wrapper";
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.iftop ];
+    security.wrappers.iftop = {
+      source = "${pkgs.iftop}/bin/iftop";
+      capabilities = "cap_net_raw+p";
+    };
+  };
+}
diff --git a/nixos/modules/programs/info.nix b/nixos/modules/programs/info.nix
deleted file mode 100644
index be6439dca5ad..000000000000
--- a/nixos/modules/programs/info.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    programs.info.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable info pages and the <command>info</command> command.
-      '';
-    };
-
-  };
-
-
-  config = mkIf config.programs.info.enable {
-
-    environment.systemPackages = [ pkgs.texinfoInteractive ];
-
-    environment.pathsToLink = [ "/info" "/share/info" ];
-
-    environment.extraOutputsToInstall = [ "info" ];
-
-  };
-
-}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
new file mode 100644
index 000000000000..d39103a58057
--- /dev/null
+++ b/nixos/modules/programs/less.nix
@@ -0,0 +1,131 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.less;
+
+  configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else ''
+    #command
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
+    }
+    ${if cfg.clearDefaultCommands then "#stop" else ""}
+
+    #line-edit
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
+    }
+
+    #env
+    ${concatStringsSep "\n"
+      (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
+    }
+  '';
+
+  lessKey = pkgs.runCommand "lesskey"
+            { src = pkgs.writeText "lessconfig" configText; }
+            "${pkgs.less}/bin/lesskey -o $out $src";
+
+in
+
+{
+  options = {
+
+    programs.less = {
+
+      enable = mkEnableOption "less";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = literalExample "$${pkgs.my-configs}/lesskey";
+        description = ''
+          Path to lesskey configuration file.
+
+          <option>configFile</option> takes precedence over <option>commands</option>,
+          <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and
+          <option>envVariables</option>.
+        '';
+      };
+
+      commands = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "h" = "noaction 5\e(";
+          "l" = "noaction 5\e)";
+        };
+        description = "Defines new command keys.";
+      };
+
+      clearDefaultCommands = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Clear all default commands.
+          You should remember to set the quit key.
+          Otherwise you will not be able to leave less without killing it.
+        '';
+      };
+
+      lineEditingKeys = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          "\e" = "abort";
+        };
+        description = "Defines new line-editing keys.";
+      };
+
+      envVariables = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          LESS = "--quit-if-one-screen";
+        };
+        description = "Defines environment variables.";
+      };
+
+      lessopen = mkOption {
+        type = types.nullOr types.str;
+        default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
+        description = ''
+          Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
+        '';
+      };
+
+      lessclose = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may  perform  any  desired  clean-up  action (such  as deleting the replacement file created by LESSOPEN).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.less ];
+
+    environment.variables = {
+      "LESSKEY_SYSTEM" = toString lessKey;
+    } // optionalAttrs (cfg.lessopen != null) {
+      "LESSOPEN" = cfg.lessopen;
+    } // optionalAttrs (cfg.lessclose != null) {
+      "LESSCLOSE" = cfg.lessclose;
+    };
+
+    warnings = optional (
+      cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands))
+    ) ''
+      config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting.
+      Consider adding a binding for 'quit'.
+    '';
+  };
+
+  meta.maintainers = with maintainers; [ johnazoidberg ];
+
+}
diff --git a/nixos/modules/programs/man.nix b/nixos/modules/programs/man.nix
deleted file mode 100644
index 5b20a38d8856..000000000000
--- a/nixos/modules/programs/man.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    programs.man.enable = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether to enable manual pages and the <command>man</command> command.
-        This also includes "man" outputs of all <literal>systemPackages</literal>.
-      '';
-    };
-
-  };
-
-
-  config = mkIf config.programs.man.enable {
-
-    environment.systemPackages = [ pkgs.man-db ];
-
-    environment.pathsToLink = [ "/share/man" ];
-
-    environment.extraOutputsToInstall = [ "man" ];
-
-  };
-
-}
diff --git a/nixos/modules/programs/plotinus.nix b/nixos/modules/programs/plotinus.nix
new file mode 100644
index 000000000000..065e72d6c374
--- /dev/null
+++ b/nixos/modules/programs/plotinus.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.plotinus;
+in
+{
+  meta = {
+    maintainers = pkgs.plotinus.meta.maintainers;
+    doc = ./plotinus.xml;
+  };
+
+  ###### interface
+
+  options = {
+    programs.plotinus = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Plotinus GTK+3 plugin.  Plotinus provides a
+          popup (triggered by Ctrl-Shift-P) to search the menus of a
+          compatible application.
+        '';
+        type = types.bool;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.variables.XDG_DATA_DIRS = [ "${pkgs.plotinus}/share/gsettings-schemas/${pkgs.plotinus.name}" ];
+    environment.variables.GTK3_MODULES = [ "${pkgs.plotinus}/lib/libplotinus.so" ];
+  };
+}
diff --git a/nixos/modules/programs/plotinus.xml b/nixos/modules/programs/plotinus.xml
new file mode 100644
index 000000000000..85b0e023e6c1
--- /dev/null
+++ b/nixos/modules/programs/plotinus.xml
@@ -0,0 +1,25 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-program-plotinus">
+
+<title>Plotinus</title>
+
+<para><emphasis>Source:</emphasis> <filename>modules/programs/plotinus.nix</filename></para>
+
+<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://github.com/p-e-w/plotinus"/></para>
+
+<para>Plotinus is a searchable command palette in every modern GTK+ application.</para>
+
+<para>When in a GTK+3 application and Plotinus is enabled, you can press <literal>Ctrl+Shift+P</literal> to open the command palette.  The command palette provides a searchable list of of all menu items in the application.</para>
+
+<para>To enable Plotinus, add the following to your <filename>configuration.nix</filename>:
+
+<programlisting>
+programs.plotinus.enable = true;
+</programlisting>
+
+</para>
+
+</chapter>
diff --git a/nixos/modules/programs/rootston.nix b/nixos/modules/programs/rootston.nix
new file mode 100644
index 000000000000..842d9e6cfb48
--- /dev/null
+++ b/nixos/modules/programs/rootston.nix
@@ -0,0 +1,103 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.rootston;
+
+  rootstonWrapped = pkgs.writeScriptBin "rootston" ''
+    #! ${pkgs.runtimeShell}
+    if [[ "$#" -ge 1 ]]; then
+      exec ${pkgs.rootston}/bin/rootston "$@"
+    else
+      ${cfg.extraSessionCommands}
+      exec ${pkgs.rootston}/bin/rootston -C ${cfg.configFile}
+    fi
+  '';
+in {
+  options.programs.rootston = {
+    enable = mkEnableOption ''
+      rootston, the reference compositor for wlroots. The purpose of rootston
+      is to test and demonstrate the features of wlroots (if you want a real
+      Wayland compositor you should e.g. use Sway instead). You can manually
+      start the compositor by running "rootston" from a terminal'';
+
+    extraSessionCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # Define a keymap (US QWERTY is the default)
+        export XKB_DEFAULT_LAYOUT=de,us
+        export XKB_DEFAULT_VARIANT=nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,caps:escape
+      '';
+      description = ''
+        Shell commands executed just before rootston is started.
+      '';
+    };
+
+    extraPackages = mkOption {
+      type = with types; listOf package;
+      default = with pkgs; [
+        westonLite xwayland rofi
+      ];
+      defaultText = literalExample ''
+        with pkgs; [
+          westonLite xwayland rofi
+        ]
+      '';
+      example = literalExample "[ ]";
+      description = ''
+        Extra packages to be installed system wide.
+      '';
+    };
+
+    config = mkOption {
+      type = types.str;
+      default = ''
+        [keyboard]
+        meta-key = Logo
+
+        # Sway/i3 like Keybindings
+        # Maps key combinations with commands to execute
+        # Commands include:
+        # - "exit" to stop the compositor
+        # - "exec" to execute a shell command
+        # - "close" to close the current view
+        # - "next_window" to cycle through windows
+        [bindings]
+        Logo+Shift+e = exit
+        Logo+q = close
+        Logo+m = maximize
+        Alt+Tab = next_window
+        Logo+Return = exec weston-terminal
+        Logo+d = exec rofi -show run
+      '';
+      description = ''
+        Default configuration for rootston (used when called without any
+        parameters).
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.path;
+      default = "/etc/rootston.ini";
+      example = literalExample "${pkgs.rootston}/etc/rootston.ini";
+      description = ''
+        Path to the default rootston configuration file (the "config" option
+        will have no effect if you change the path).
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."rootston.ini".text = cfg.config;
+    environment.systemPackages = [ rootstonWrapped ] ++ cfg.extraPackages;
+
+    hardware.opengl.enable = mkDefault true;
+    fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
+  };
+
+  meta.maintainers = with lib.maintainers; [ primeos gnidorah ];
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 0f3f42901bab..8ec4169207db 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -26,8 +26,9 @@ let
       # Ensure privacy for newly created home directories.
       UMASK        077
 
-      # Uncomment this to allow non-root users to change their account
-      #information.  This should be made configurable.
+      # Uncomment this and install chfn SUID to allow non-root
+      # users to change their account GECOS information.
+      # This should be made configurable.
       #CHFN_RESTRICT frwh
 
     '';
@@ -103,13 +104,12 @@ in
 
     security.wrappers = {
       su.source        = "${pkgs.shadow.su}/bin/su";
-      chfn.source      = "${pkgs.shadow.out}/bin/chfn";
+      sg.source        = "${pkgs.shadow.out}/bin/sg";
+      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
       newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap";
       newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap";
     } // (if config.users.mutableUsers then {
       passwd.source    = "${pkgs.shadow.out}/bin/passwd";
-      sg.source        = "${pkgs.shadow.out}/bin/sg";
-      newgrp.source    = "${pkgs.shadow.out}/bin/newgrp";
     } else {});
   };
 }
diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix
new file mode 100644
index 000000000000..86153d933855
--- /dev/null
+++ b/nixos/modules/programs/singularity.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.singularity;
+in {
+  options.programs.singularity = {
+    enable = mkEnableOption "Singularity";
+  };
+
+  config = mkIf cfg.enable {
+      environment.systemPackages = [ pkgs.singularity ];
+      systemd.tmpfiles.rules = [ "d /var/singularity/mnt/session 0770 root root -"
+                                 "d /var/singularity/mnt/final 0770 root root -"
+                                 "d /var/singularity/mnt/overlay 0770 root root -"
+                                 "d /var/singularity/mnt/container 0770 root root -"
+                                 "d /var/singularity/mnt/source 0770 root root -"];
+  };
+
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index 0935bf0cae71..36289080a82a 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -13,7 +13,7 @@ let
 
   askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
       exec ${askPassword}
     '';
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
index 9070722c770b..d9503d6004ff 100644
--- a/nixos/modules/programs/sway.nix
+++ b/nixos/modules/programs/sway.nix
@@ -4,33 +4,42 @@ with lib;
 
 let
   cfg = config.programs.sway;
-  sway = pkgs.sway;
+  swayPackage = pkgs.sway;
 
-  swayWrapped = pkgs.writeScriptBin "sway" ''
-    #! ${pkgs.stdenv.shell}
-    ${cfg.extraSessionCommands}
-    PATH="${sway}/bin:$PATH"
-    exec ${pkgs.dbus.dbus-launch} --exit-with-session sway-setcap
+  swayWrapped = pkgs.writeShellScriptBin "sway" ''
+    if [[ "$#" -ge 1 ]]; then
+      exec sway-setcap "$@"
+    else
+      ${cfg.extraSessionCommands}
+      exec ${pkgs.dbus.dbus-launch} --exit-with-session sway-setcap
+    fi
   '';
   swayJoined = pkgs.symlinkJoin {
-    name = "sway-wrapped";
-    paths = [ swayWrapped sway ];
+    name = "sway-joined";
+    paths = [ swayWrapped swayPackage ];
   };
-in
-{
+in {
   options.programs.sway = {
-    enable = mkEnableOption "sway";
+    enable = mkEnableOption ''
+      the tiling Wayland compositor Sway. After adding yourself to the "sway"
+      group you can manually launch Sway by executing "sway" from a terminal.
+      If you call "sway" with any parameters the extraSessionCommands won't be
+      executed and Sway won't be launched with dbus-launch'';
 
     extraSessionCommands = mkOption {
-      default     = "";
-      type        = types.lines;
+      type = types.lines;
+      default = "";
       example = ''
-        export XKB_DEFAULT_LAYOUT=us,de
-        export XKB_DEFAULT_VARIANT=,nodeadkeys
-        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,
+        # Define a keymap (US QWERTY is the default)
+        export XKB_DEFAULT_LAYOUT=de,us
+        export XKB_DEFAULT_VARIANT=nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:alt_shift_toggle,caps:escape
+        # Change the Keyboard repeat delay and rate
+        export WLC_REPEAT_DELAY=660
+        export WLC_REPEAT_RATE=25
       '';
       description = ''
-        Shell commands executed just before sway is started.
+        Shell commands executed just before Sway is started.
       '';
     };
 
@@ -39,9 +48,12 @@ in
       default = with pkgs; [
         i3status xwayland rxvt_unicode dmenu
       ];
+      defaultText = literalExample ''
+        with pkgs; [ i3status xwayland rxvt_unicode dmenu ];
+      '';
       example = literalExample ''
         with pkgs; [
-          i3status xwayland rxvt_unicode dmenu
+          i3lock light termite
         ]
       '';
       description = ''
@@ -54,7 +66,7 @@ in
     environment.systemPackages = [ swayJoined ] ++ cfg.extraPackages;
     security.wrappers.sway = {
       program = "sway-setcap";
-      source = "${sway}/bin/sway";
+      source = "${swayPackage}/bin/sway";
       capabilities = "cap_sys_ptrace,cap_sys_tty_config=eip";
       owner = "root";
       group = "sway";
@@ -62,8 +74,12 @@ in
     };
 
     users.extraGroups.sway = {};
+    security.pam.services.swaylock = {};
 
     hardware.opengl.enable = mkDefault true;
     fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
   };
+
+  meta.maintainers = with lib.maintainers; [ gnidorah primeos ];
 }
diff --git a/nixos/modules/programs/systemtap.nix b/nixos/modules/programs/systemtap.nix
new file mode 100644
index 000000000000..fd84732cd412
--- /dev/null
+++ b/nixos/modules/programs/systemtap.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.systemtap;
+in {
+
+  options = {
+    programs.systemtap = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Install <command>systemtap</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "DEBUG")
+    ];
+    boot.kernel.features.debug = true;
+    environment.systemPackages = [
+      config.boot.kernelPackages.systemtap
+    ];
+  };
+
+}
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
index ed1d88a420a2..4a60403a2827 100644
--- a/nixos/modules/programs/tmux.nix
+++ b/nixos/modules/programs/tmux.nix
@@ -61,7 +61,12 @@ in {
   options = {
     programs.tmux = {
 
-      enable = mkEnableOption "<command>tmux</command> - a <command>screen</command> replacement.";
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whenever to configure <command>tmux</command> system-wide.";
+        relatedPackages = [ "tmux" ];
+      };
 
       aggressiveResize = mkOption {
         default = false;
@@ -151,6 +156,15 @@ in {
         type = types.str;
         description = "Set the $TERM variable.";
       };
+
+      secureSocket = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Store tmux socket under /run, which is more secure than /tmp, but as a
+          downside it doesn't survive user logout.
+        '';
+      };
     };
   };
 
@@ -163,7 +177,7 @@ in {
       systemPackages = [ pkgs.tmux ];
 
       variables = {
-        TMUX_TMPDIR = ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
+        TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
       };
     };
   };
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
new file mode 100644
index 000000000000..ba5670f9dfe9
--- /dev/null
+++ b/nixos/modules/programs/udevil.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.udevil;
+
+in {
+  options.programs.udevil.enable = mkEnableOption "udevil";
+
+  config = mkIf cfg.enable {
+    security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil";
+  };
+}
diff --git a/nixos/modules/programs/way-cooler.nix b/nixos/modules/programs/way-cooler.nix
new file mode 100644
index 000000000000..633e959be9f3
--- /dev/null
+++ b/nixos/modules/programs/way-cooler.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.way-cooler;
+  way-cooler = pkgs.way-cooler;
+
+  wcWrapped = pkgs.writeShellScriptBin "way-cooler" ''
+    ${cfg.extraSessionCommands}
+    exec ${pkgs.dbus.dbus-launch} --exit-with-session ${way-cooler}/bin/way-cooler
+  '';
+  wcJoined = pkgs.symlinkJoin {
+    name = "way-cooler-wrapped";
+    paths = [ wcWrapped way-cooler ];
+  };
+  configFile = readFile "${way-cooler}/etc/way-cooler/init.lua";
+  spawnBar = ''
+    util.program.spawn_at_startup("lemonbar");
+  '';
+in
+{
+  options.programs.way-cooler = {
+    enable = mkEnableOption "way-cooler";
+
+    extraSessionCommands = mkOption {
+      default     = "";
+      type        = types.lines;
+      example = ''
+        export XKB_DEFAULT_LAYOUT=us,de
+        export XKB_DEFAULT_VARIANT=,nodeadkeys
+        export XKB_DEFAULT_OPTIONS=grp:caps_toggle,
+      '';
+      description = ''
+        Shell commands executed just before way-cooler is started.
+      '';
+    };
+
+    extraPackages = mkOption {
+      type = with types; listOf package;
+      default = with pkgs; [
+        westonLite xwayland dmenu
+      ];
+      example = literalExample ''
+        with pkgs; [
+          westonLite xwayland dmenu
+        ]
+      '';
+      description = ''
+        Extra packages to be installed system wide.
+      '';
+    };
+
+    enableBar = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable an unofficial bar.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ wcJoined ] ++ cfg.extraPackages;
+
+    security.pam.services.wc-lock = {};
+    environment.etc."way-cooler/init.lua".text = ''
+      ${configFile}
+      ${optionalString cfg.enableBar spawnBar}
+    '';
+
+    hardware.opengl.enable = mkDefault true;
+    fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+}
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
new file mode 100644
index 000000000000..a01083c3ace9
--- /dev/null
+++ b/nixos/modules/programs/yabar.nix
@@ -0,0 +1,149 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.yabar;
+
+  mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList (
+    key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};"
+  ) v);
+
+  listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r));
+
+  configFile = let
+    bars = mapAttrsToList (
+      name: cfg: ''
+        ${name}: {
+          font: "${cfg.font}";
+          position: "${cfg.position}";
+
+          ${mapExtra cfg.extra}
+
+          block-list: [${listKeys cfg.indicators}]
+
+          ${concatStringsSep "\n" (mapAttrsToList (
+            name: cfg: ''
+              ${name}: {
+                exec: "${cfg.exec}";
+                align: "${cfg.align}";
+                ${mapExtra cfg.extra}
+              };
+            ''
+          ) cfg.indicators)}
+        };
+      ''
+    ) cfg.bars;
+  in pkgs.writeText "yabar.conf" ''
+    bar-list = [${listKeys cfg.bars}];
+    ${concatStringsSep "\n" bars}
+  '';
+in
+  {
+    options.programs.yabar = {
+      enable = mkEnableOption "yabar";
+
+      package = mkOption {
+        default = pkgs.yabar;
+        example = literalExample "pkgs.yabar-unstable";
+        type = types.package;
+
+        description = ''
+          The package which contains the `yabar` binary.
+
+          Nixpkgs provides the `yabar` and `yabar-unstable`
+          derivations since 18.03, so it's possible to choose.
+        '';
+      };
+
+      bars = mkOption {
+        default = {};
+        type = types.attrsOf(types.submodule {
+          options = {
+            font = mkOption {
+              default = "sans bold 9";
+              example = "Droid Sans, FontAwesome Bold 9";
+              type = types.string;
+
+              description = ''
+                The font that will be used to draw the status bar.
+              '';
+            };
+
+            position = mkOption {
+              default = "top";
+              example = "bottom";
+              type = types.enum [ "top" "bottom" ];
+
+              description = ''
+                The position where the bar will be rendered.
+              '';
+            };
+
+            extra = mkOption {
+              default = {};
+              type = types.attrsOf types.string;
+
+              description = ''
+                An attribute set which contains further attributes of a bar.
+              '';
+            };
+
+            indicators = mkOption {
+              default = {};
+              type = types.attrsOf(types.submodule {
+                options.exec = mkOption {
+                  example = "YABAR_DATE";
+                  type = types.string;
+                  description = ''
+                     The type of the indicator to be executed.
+                  '';
+                };
+
+                options.align = mkOption {
+                  default = "left";
+                  example = "right";
+                  type = types.enum [ "left" "center" "right" ];
+
+                  description = ''
+                    Whether to align the indicator at the left or right of the bar.
+                  '';
+                };
+
+                options.extra = mkOption {
+                  default = {};
+                  type = types.attrsOf (types.either types.string types.int);
+
+                  description = ''
+                    An attribute set which contains further attributes of a indicator.
+                  '';
+                };
+              });
+
+              description = ''
+                Indicators that should be rendered by yabar.
+              '';
+            };
+          };
+        });
+
+        description = ''
+          List of bars that should be rendered by yabar.
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      systemd.user.services.yabar = {
+        description = "yabar service";
+        wantedBy = [ "graphical-session.target" ];
+        partOf = [ "graphical-session.target" ];
+
+        script = ''
+          ${cfg.package}/bin/yabar -c ${configFile}
+        '';
+
+        serviceConfig.Restart = "always";
+      };
+    };
+  }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
index 9077643c4440..b995d390b279 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.nix
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -48,6 +48,15 @@ in
             Name of the theme to be used by oh-my-zsh.
           '';
         };
+
+        cacheDir = mkOption {
+          default = "$HOME/.cache/oh-my-zsh";
+          type = types.str;
+          description = ''
+            Cache directory to be used by `oh-my-zsh`.
+            Without this option it would default to the read-only nix store.
+          '';
+        };
       };
     };
 
@@ -74,6 +83,13 @@ in
           "ZSH_THEME=\"${cfg.theme}\""
         }
 
+        ${optionalString (cfg.cacheDir != null) ''
+          if [[ ! -d "${cfg.cacheDir}" ]]; then
+            mkdir -p "${cfg.cacheDir}"
+          fi
+          ZSH_CACHE_DIR=${cfg.cacheDir}
+        ''}
+
         source $ZSH/oh-my-zsh.sh
       '';
     };
diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix
new file mode 100644
index 000000000000..630114bcda9f
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autoenv.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.zsh-autoenv;
+in {
+  options = {
+    programs.zsh.zsh-autoenv = {
+      enable = mkEnableOption "zsh-autoenv";
+      package = mkOption {
+        default = pkgs.zsh-autoenv;
+        defaultText = "pkgs.zsh-autoenv";
+        description = ''
+          Package to install for `zsh-autoenv` usage.
+        '';
+
+        type = types.package;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.zsh.interactiveShellInit = ''
+      source ${cfg.package}/share/zsh-autoenv/autoenv.zsh
+    '';
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 6fb1346bbb33..f689250dc61f 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -36,8 +36,9 @@ in
       shellAliases = mkOption {
         default = config.environment.shellAliases;
         description = ''
-          Set of aliases for zsh shell. See <option>environment.shellAliases</option>
-          for an option format description.
+          Set of aliases for zsh shell. Overrides the default value taken from
+           <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
         '';
         type = types.attrs; # types.attrsOf types.stringOrPath;
       };
@@ -107,7 +108,7 @@ in
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
         export __ETC_ZSHENV_SOURCED=1
 
-        . ${config.system.build.setEnvironment}
+        ${config.system.build.setEnvironment.text}
 
         ${cfge.shellInit}
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index f30cbe427f09..d8bea6267320 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -4,6 +4,7 @@ with lib;
 
 {
   imports = [
+    (mkRenamedOptionModule [ "dysnomia" ] [ "services" "dysnomia" ])
     (mkRenamedOptionModule [ "environment" "x11Packages" ] [ "environment" "systemPackages" ])
     (mkRenamedOptionModule [ "environment" "enableBashCompletion" ] [ "programs" "bash" "enableCompletion" ])
     (mkRenamedOptionModule [ "environment" "nix" ] [ "nix" "package" ])
@@ -22,6 +23,8 @@ with lib;
       (config:
         let enabled = getAttrFromPath [ "services" "printing" "gutenprint" ] config;
         in if enabled then [ pkgs.gutenprint ] else [ ]))
+    (mkRenamedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ])
+    (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
     (mkRenamedOptionModule [ "services" "elasticsearch" "host" ] [ "services" "elasticsearch" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "api" "host" ] [ "services" "graphite" "api" "listenAddress" ])
     (mkRenamedOptionModule [ "services" "graphite" "web" "host" ] [ "services" "graphite" "web" "listenAddress" ])
@@ -48,6 +51,10 @@ with lib;
     (mkRemovedOptionModule [ "services" "rmilter" "bindInetSockets" ] "Use services.rmilter.bindSocket.* instead")
     (mkRemovedOptionModule [ "services" "rmilter" "bindUnixSockets" ] "Use services.rmilter.bindSocket.* instead")
 
+    # Xsession script
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ])
+
     # Old Grub-related options.
     (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ])
     (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ])
@@ -78,6 +85,10 @@ with lib;
     (mkRenamedOptionModule [ "services" "virtualboxHost" "addNetworkInterface" ] [ "virtualisation" "virtualbox" "host" "addNetworkInterface" ])
     (mkRenamedOptionModule [ "services" "virtualboxHost" "enableHardening" ] [ "virtualisation" "virtualbox" "host" "enableHardening" ])
 
+    # libvirtd
+    (mkRemovedOptionModule [ "virtualisation" "libvirtd" "enableKVM" ]
+      "Set the option `virtualisation.libvirtd.qemuPackage' instead.")
+
     # Tarsnap
     (mkRenamedOptionModule [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ])
 
@@ -140,6 +151,12 @@ with lib;
     # parsoid
     (mkRemovedOptionModule [ "services" "parsoid" "interwikis" ] [ "services" "parsoid" "wikis" ])
 
+    # piwik was renamed to matomo
+    (mkRenamedOptionModule [ "services" "piwik" "enable" ] [ "services" "matomo" "enable" ])
+    (mkRenamedOptionModule [ "services" "piwik" "webServerUser" ] [ "services" "matomo" "webServerUser" ])
+    (mkRenamedOptionModule [ "services" "piwik" "phpfpmProcessManagerConfig" ] [ "services" "matomo" "phpfpmProcessManagerConfig" ])
+    (mkRenamedOptionModule [ "services" "piwik" "nginx" ] [ "services" "matomo" "nginx" ])
+
     # tarsnap
     (mkRemovedOptionModule [ "services" "tarsnap" "cachedir" ] "Use services.tarsnap.archives.<name>.cachedir")
 
@@ -178,6 +195,21 @@ with lib;
     (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "config" "fonts" "fontconfig" "forceAutohint" ])
     (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "config" "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
 
+    # Profile splitting
+    (mkRenamedOptionModule [ "virtualization" "growPartition" ] [ "boot" "growPartition" ])
+
+    # misc/version.nix
+    #(mkRenamedOptionModule [ "config" "system" "nixosVersion" ] [ "config" "system" "nixos" "version" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosRelease" ] [ "config" "system" "nixos" "release" ])
+    #(mkRenamedOptionModule [ "config" "system" "nixosVersionSuffix" ] [ "config" "system" "nixos" "versionSuffix" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosRevision" ] [ "config" "system" "nixos" "revision" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosCodeName" ] [ "config" "system" "nixos" "codeName" ])
+    (mkRenamedOptionModule [ "config" "system" "nixosLabel" ] [ "config" "system" "nixos" "label" ])
+
+    # Users
+    (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
+    (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
     (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
@@ -194,11 +226,14 @@ with lib;
       "See the 16.09 release notes for more information.")
     (mkRemovedOptionModule [ "services" "phpfpm" "phpIni" ] "")
     (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "")
+    (mkRemovedOptionModule [ "services" "firefox" "syncserver" "user" ] "")
+    (mkRemovedOptionModule [ "services" "firefox" "syncserver" "group" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "")
     (mkRemovedOptionModule [ "services" "xserver" "displayManager" "sddm" "themes" ]
       "Set the option `services.xserver.displayManager.sddm.package' instead.")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.")
 
     # ZSH
     (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
@@ -209,5 +244,18 @@ with lib;
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
     (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
-  ];
+
+    # Xen
+    (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ])
+
+    (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
+    (mkRenamedOptionModule [ "programs" "man"  "enable" ] [ "documentation" "man"  "enable" ])
+
+  ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
+                   "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
+                   "snmpExporter" "unifiExporter" "varnishExporter" ]
+       (opt: mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
+         The prometheus exporters are now configured using `services.prometheus.exporters'.
+         See the 18.03 release notes for more information.
+       '' ));
 }
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index df0b4986eb82..eb705007d028 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -6,10 +6,11 @@ let
 
   cfg = config.security.acme;
 
-  certOpts = { ... }: {
+  certOpts = { name, ... }: {
     options = {
       webroot = mkOption {
         type = types.str;
+        example = "/var/lib/acme/acme-challenges";
         description = ''
           Where the webroot of the HTTP vhost is located.
           <filename>.well-known/acme-challenge/</filename> directory
@@ -20,8 +21,8 @@ let
       };
 
       domain = mkOption {
-        type = types.nullOr types.str;
-        default = null;
+        type = types.str;
+        default = name;
         description = "Domain to fetch certificate for (defaults to the entry name)";
       };
 
@@ -48,7 +49,7 @@ let
         default = false;
         description = ''
           Give read permissions to the specified group
-          (<option>security.acme.group</option>) to read SSL private certificates.
+          (<option>security.acme.cert.&lt;name&gt;.group</option>) to read SSL private certificates.
         '';
       };
 
@@ -110,7 +111,7 @@ let
           }
         '';
         description = ''
-          Extra domain names for which certificates are to be issued, with their
+          A list of extra domain names, which are included in the one certificate to be issued, with their
           own server roots if needed.
         '';
       };
@@ -211,8 +212,9 @@ in
                 domain = if data.domain != null then data.domain else cert;
                 cpath = lpath + optionalString (data.activationDelay != null) ".staging";
                 lpath = "${cfg.directory}/${cert}";
+                cpath = "${cfg.directory}/${cert}";
                 rights = if data.allowKeysForGroup then "750" else "700";
-                cmdline = [ "-v" "-d" domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
+                cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
                           ++ optionals (data.email != null) [ "--email" data.email ]
                           ++ concatMap (p: [ "-f" p ]) data.plugins
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
@@ -284,6 +286,7 @@ in
                 };
                 selfsignedService = {
                   description = "Create preliminary self-signed certificate for ${cert}";
+                  path = [ pkgs.openssl ];
                   preStart = ''
                       if [ ! -d '${cpath}' ]
                       then
@@ -294,37 +297,41 @@ in
                   '';
                   script = 
                     ''
-                      # Create self-signed key
-                      workdir="/run/acme-selfsigned-${cert}"
-                      ${pkgs.openssl.bin}/bin/openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048
-                      ${pkgs.openssl.bin}/bin/openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key
-                      ${pkgs.openssl.bin}/bin/openssl req -new -key $workdir/server.key -out $workdir/server.csr \
+                      workdir="$(mktemp -d)"
+
+                      # Create CA
+                      openssl genrsa -des3 -passout pass:x -out $workdir/ca.pass.key 2048
+                      openssl rsa -passin pass:x -in $workdir/ca.pass.key -out $workdir/ca.key
+                      openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
+                        -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
+                      openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
+
+                      # Create key
+                      openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048
+                      openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key
+                      openssl req -new -key $workdir/server.key -out $workdir/server.csr \
                         -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
-                      ${pkgs.openssl.bin}/bin/openssl x509 -req -days 1 -in $workdir/server.csr -signkey $workdir/server.key -out $workdir/server.crt
+                      openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
+                        -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
+                        -out $workdir/server.crt
 
-                      # Move key to destination
-                      mv $workdir/server.key ${cpath}/key.pem
-                      mv $workdir/server.crt ${cpath}/fullchain.pem
+                      # Copy key to destination
+                      cp $workdir/server.key ${cpath}/key.pem
 
-                      # Create full.pem for e.g. lighttpd (same format as "simp_le ... -f full.pem" creates)
-                      cat "${cpath}/key.pem" "${cpath}/fullchain.pem" > "${cpath}/full.pem"
+                      # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
+                      cat $workdir/{server.crt,ca.crt} > "${cpath}/fullchain.pem"
 
-                      # Clean up working directory
-                      rm $workdir/server.csr
-                      rm $workdir/server.pass.key
+                      # Create full.pem for e.g. lighttpd
+                      cat $workdir/{server.key,server.crt,ca.crt} > "${cpath}/full.pem"
 
                       # Give key acme permissions
-                      chmod ${rights} '${cpath}/key.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/key.pem'
-                      chmod ${rights} '${cpath}/fullchain.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/fullchain.pem'
-                      chmod ${rights} '${cpath}/full.pem'
-                      chown '${data.user}:${data.group}' '${cpath}/full.pem'
+                      chown '${data.user}:${data.group}' "${cpath}/"{key,fullchain,full}.pem
+                      chmod ${rights} "${cpath}/"{key,fullchain,full}.pem
                     '';
                   serviceConfig = {
                     Type = "oneshot";
-                    RuntimeDirectory = "acme-selfsigned-${cert}";
                     PermissionsStartOnly = true;
+                    PrivateTmp = true;
                     User = data.user;
                     Group = data.group;
                   };
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix
index 7ac21fd96507..2b22bdd9f0ae 100644
--- a/nixos/modules/security/audit.nix
+++ b/nixos/modules/security/audit.nix
@@ -13,7 +13,7 @@ let
   };
 
   disableScript = pkgs.writeScript "audit-disable" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Explicitly disable everything, as otherwise journald might start it.
     auditctl -D
     auditctl -e 0 -a task,never
@@ -23,7 +23,7 @@ let
   # put in the store like this. At the same time, it doesn't feel like a huge deal and working
   # around that is a pain so I'm leaving it like this for now.
   startScript = pkgs.writeScript "audit-start" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear out any rules we may start with
     auditctl -D
 
@@ -43,7 +43,7 @@ let
   '';
 
   stopScript = pkgs.writeScript "audit-stop" ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
     # Clear the rules
     auditctl -D
 
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
index 9ca818e86ffa..df6108dede7c 100644
--- a/nixos/modules/security/duosec.nix
+++ b/nixos/modules/security/duosec.nix
@@ -25,14 +25,14 @@ let
   loginCfgFile = optional cfg.ssh.enable
     { source = pkgs.writeText "login_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/login_duo.conf";
     };
 
   pamCfgFile = optional cfg.pam.enable
     { source = pkgs.writeText "pam_duo.conf" configFile;
       mode   = "0600";
-      uid    = config.ids.uids.sshd;
+      user   = "sshd";
       target = "duo/pam_duo.conf";
     };
 in
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 2d6713311a45..48998285d89d 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -46,6 +46,18 @@ let
         '';
       };
 
+      googleAuthenticator = {
+        enable = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            If set, users with enabled Google Authenticator (created
+            <filename>~/.google_authenticator</filename>) will be required
+            to provide Google Authenticator token to log in.
+          '';
+        };
+      };
+
       usbAuth = mkOption {
         default = config.security.pam.usb.enable;
         type = types.bool;
@@ -222,6 +234,22 @@ let
           password, KDE will prompt separately after login.
         '';
       };
+      sssdStrictAccess = mkOption {
+        default = false;
+        type = types.bool;
+        description = "enforce sssd access control";
+      };
+
+      enableGnomeKeyring = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, pam_gnome_keyring will attempt to automatically unlock the
+          user's default Gnome keyring upon login. If the user login password does
+          not match their keyring password, Gnome Keyring will prompt separately
+          after login.
+        '';
+      };
 
       text = mkOption {
         type = types.nullOr types.lines;
@@ -241,11 +269,13 @@ let
       text = mkDefault
         (''
           # Account management.
-          account sufficient pam_unix.so
+          account ${if cfg.sssdStrictAccess then "required" else "sufficient"} pam_unix.so
           ${optionalString use_ldap
               "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
-          ${optionalString config.services.sssd.enable
+          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false)
               "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
+          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess)
+              "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"}
           ${optionalString config.krb5.enable
               "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
 
@@ -273,7 +303,12 @@ let
           # prompts the user for password so we run it once with 'required' at an
           # earlier point and it will run again with 'sufficient' further down.
           # We use try_first_pass the second time to avoid prompting password twice
-          (optionalString (cfg.unixAuth && (config.security.pam.enableEcryptfs || cfg.pamMount || cfg.enableKwallet)) ''
+          (optionalString (cfg.unixAuth &&
+          (config.security.pam.enableEcryptfs
+            || cfg.pamMount
+            || cfg.enableKwallet
+            || cfg.enableGnomeKeyring
+            || cfg.googleAuthenticator.enable)) ''
               auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth
               ${optionalString config.security.pam.enableEcryptfs
                 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -282,6 +317,10 @@ let
               ${optionalString cfg.enableKwallet
                 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
                  " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
+              ${optionalString cfg.enableGnomeKeyring
+                ("auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so")}
+              ${optionalString cfg.googleAuthenticator.enable
+                  "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
             '') + ''
           ${optionalString cfg.unixAuth
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
@@ -351,6 +390,10 @@ let
           ${optionalString (cfg.enableKwallet)
               ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
                " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
+          ${optionalString (cfg.enableGnomeKeyring)
+              "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
+          ${optionalString (config.virtualisation.lxc.lxcfs.enable)
+               "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
         '');
     };
 
@@ -519,7 +562,6 @@ in
         ftp = {};
         i3lock = {};
         i3lock-color = {};
-        swaylock = {};
         screen = {};
         vlock = {};
         xlock = {};
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index cfd0595e63b7..24283e1d6165 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -8,6 +8,22 @@ let
 
   inherit (pkgs) sudo;
 
+  toUserString = user: if (isInt user) then "#${toString user}" else "${user}";
+  toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}";
+
+  toCommandOptionsString = options:
+    "${concatStringsSep ":" options}${optionalString (length options != 0) ":"} ";
+
+  toCommandsString = commands:
+    concatStringsSep ", " (
+      map (command:
+        if (isString command) then
+          command
+        else
+          "${toCommandOptionsString command.options}${command.command}"
+      ) commands
+    );
+
 in
 
 {
@@ -31,8 +47,8 @@ in
       default = true;
       description =
         ''
-          Whether users of the <code>wheel</code> group can execute
-          commands as super user without entering a password.
+          Whether users of the <code>wheel</code> group must
+          provide a password to run commands as super user via <command>sudo</command>.
         '';
       };
 
@@ -47,6 +63,97 @@ in
         '';
     };
 
+    security.sudo.extraRules = mkOption {
+      description = ''
+        Define specific rules to be in the <filename>sudoers</filename> file.
+      '';
+      default = [];
+      example = [
+        # Allow execution of any command by all users in group sudo,
+        # requiring a password.
+        { groups = [ "sudo" ]; commands = [ "ALL" ]; }
+
+        # Allow execution of "/home/root/secret.sh" by user `backup`, `database`
+        # and the group with GID `1006` without a password.
+        { users = [ "backup" ]; groups = [ 1006 ];
+          commands = [ { command = "/home/root/secret.sh"; options = [ "SETENV" "NOPASSWD" ]; } ]; }
+
+        # Allow all users of group `bar` to run two executables as user `foo`
+        # with arguments being pre-set.
+        { groups = [ "bar" ]; runAs = "foo";
+          commands =
+            [ "/home/baz/cmd1.sh hello-sudo"
+              { command = ''/home/baz/cmd2.sh ""''; options = [ "SETENV" ]; } ]; }
+      ];
+      type = with types; listOf (submodule {
+        options = {
+          users = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The usernames / UIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          groups = mkOption {
+            type = with types; listOf (either string int);
+            description = ''
+              The groups / GIDs this rule should apply for.
+            '';
+            default = [];
+          };
+
+          host = mkOption {
+            type = types.string;
+            default = "ALL";
+            description = ''
+              For what host this rule should apply.
+            '';
+          };
+
+          runAs = mkOption {
+            type = with types; string;
+            default = "ALL:ALL";
+            description = ''
+              Under which user/group the specified command is allowed to run.
+
+              A user can be specified using just the username: <code>"foo"</code>.
+              It is also possible to specify a user/group combination using <code>"foo:bar"</code>
+              or to only allow running as a specific group with <code>":bar"</code>.
+            '';
+          };
+
+          commands = mkOption {
+            description = ''
+              The commands for which the rule should apply.
+            '';
+            type = with types; listOf (either string (submodule {
+
+              options = {
+                command = mkOption {
+                  type = with types; string;
+                  description = ''
+                    A command being either just a path to a binary to allow any arguments,
+                    the full command with arguments pre-set or with <code>""</code> used as the argument,
+                    not allowing arguments to the command at all.
+                  '';
+                };
+
+                options = mkOption {
+                  type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]);
+                  description = ''
+                    Options for running the command. Refer to the <a href="https://www.sudo.ws/man/1.7.10/sudoers.man.html">sudo manual</a>.
+                  '';
+                  default = [];
+                };
+              };
+
+            }));
+          };
+        };
+      });
+    };
+
     security.sudo.extraConfig = mkOption {
       type = types.lines;
       default = "";
@@ -61,10 +168,16 @@ in
 
   config = mkIf cfg.enable {
 
+    security.sudo.extraRules = [
+      { groups = [ "wheel" ];
+        commands = [ { command = "ALL"; options = (if cfg.wheelNeedsPassword then [ "SETENV" ] else [ "NOPASSWD" "SETENV" ]); } ];
+      }
+    ];
+
     security.sudo.configFile =
       ''
         # Don't edit this file. Set the NixOS options ‘security.sudo.configFile’
-        # or ‘security.sudo.extraConfig’ instead.
+        # or ‘security.sudo.extraRules’ instead.
 
         # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
         Defaults env_keep+=SSH_AUTH_SOCK
@@ -72,8 +185,18 @@ in
         # "root" is allowed to do anything.
         root        ALL=(ALL:ALL) SETENV: ALL
 
-        # Users in the "wheel" group can do anything.
-        %wheel      ALL=(ALL:ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
+        # extraRules
+        ${concatStringsSep "\n" (
+          lists.flatten (
+            map (
+              rule: if (length rule.commands != 0) then [
+                (map (user: "${toUserString user}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.users)
+                (map (group: "${toGroupString group}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.groups)
+              ] else []
+            ) cfg.extraRules
+          )
+        )}
+
         ${cfg.extraConfig}
       '';
 
@@ -92,7 +215,7 @@ in
           { src = pkgs.writeText "sudoers-in" cfg.configFile; }
           # Make sure that the sudoers file is syntactically valid.
           # (currently disabled - NIXOS-66)
-          "${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out";
+          "${pkgs.buildPackages.sudo}/sbin/visudo -f $src -c && cp $src $out";
         target = "sudoers";
         mode = "0440";
       };
diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
index 1f64213accd4..77e4b2a616d8 100644
--- a/nixos/modules/security/wrappers/default.nix
+++ b/nixos/modules/security/wrappers/default.nix
@@ -17,7 +17,7 @@ let
     hardeningEnable = [ "pie" ];
     installPhase = ''
       mkdir -p $out/bin
-      gcc -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \
+      $CC -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \
           -lcap-ng -lcap ${./wrapper.c} -o $out/bin/security-wrapper
     '';
   };
@@ -79,7 +79,7 @@ let
                  ({ owner = "root";
                     group = "root";
                   } // s)
-          else if 
+          else if
              (s ? "setuid" && s.setuid) ||
              (s ? "setgid" && s.setgid) ||
              (s ? "permissions")
diff --git a/nixos/modules/security/wrappers/wrapper.c b/nixos/modules/security/wrappers/wrapper.c
index 7091e314bb22..494e9e93ac22 100644
--- a/nixos/modules/security/wrappers/wrapper.c
+++ b/nixos/modules/security/wrappers/wrapper.c
@@ -10,8 +10,8 @@
 #include <errno.h>
 #include <linux/capability.h>
 #include <sys/capability.h>
-#include <linux/prctl.h>
 #include <sys/prctl.h>
+#include <limits.h>
 #include <cap-ng.h>
 
 // Make sure assertions are not compiled out, we use them to codify
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index acf48d3c3d03..e3e8bb28c58b 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -21,7 +21,7 @@ in
 
       enable = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
         description = ''
           Whether to enable ALSA sound.
         '';
diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix
index c0a0f0374294..52613d450b51 100644
--- a/nixos/modules/services/audio/mopidy.nix
+++ b/nixos/modules/services/audio/mopidy.nix
@@ -4,17 +4,22 @@ with pkgs;
 with lib;
 
 let
-
   uid = config.ids.uids.mopidy;
   gid = config.ids.gids.mopidy;
   cfg = config.services.mopidy;
 
   mopidyConf = writeText "mopidy.conf" cfg.configuration;
 
-  mopidyEnv = python.buildEnv.override {
-    extraLibs = [ mopidy ] ++ cfg.extensionPackages;
+  mopidyEnv = buildEnv {
+    name = "mopidy-with-extensions-${mopidy.version}";
+    paths = closePropagation cfg.extensionPackages;
+    pathsToLink = [ "/${python.sitePackages}" ];
+    buildInputs = [ makeWrapper ];
+    postBuild = ''
+      makeWrapper ${mopidy}/bin/mopidy $out/bin/mopidy \
+        --prefix PYTHONPATH : $out/${python.sitePackages}
+    '';
   };
-
 in {
 
   options = {
@@ -61,7 +66,6 @@ in {
 
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix
deleted file mode 100644
index fbb4ff4034f1..000000000000
--- a/nixos/modules/services/backup/almir.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.almir;
-
-  bconsoleconf = pkgs.writeText "bconsole.conf"
-    ''
-      Director {
-        Name = ${cfg.director_name}
-        DIRport = ${toString cfg.director_port}
-        address = ${cfg.director_address}
-        Password = "${cfg.director_password}"
-      }
-    '';
-
-  productionini = pkgs.writeText "production.ini"
-    ''
-[app:main]
-use = egg:almir
-
-pyramid.reload_templates = false
-pyramid.debug_authorization = false
-pyramid.debug_notfound = false
-pyramid.debug_routematch = false
-pyramid.debug_templates = false
-pyramid.default_locale_name = en
-pyramid.includes =
-    pyramid_exclog
-exclog.extra_info = true
-
-sqlalchemy.url = ${cfg.sqlalchemy_engine_url}
-timezone = ${cfg.timezone}
-bconsole_config = ${bconsoleconf}
-
-[server:main]
-use = egg:waitress#main
-host = 127.0.0.1
-port = ${toString cfg.port}
-
-
-# Begin logging configuration
-
-[loggers]
-keys = root, almir, sqlalchemy, exc_logger
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-
-[logger_almir]
-level = WARN
-handlers =
-qualname = almir
-
-[logger_exc_logger]
-level = ERROR
-handlers =
-qualname = exc_logger
-
-[logger_sqlalchemy]
-level = WARN
-handlers =
-qualname = sqlalchemy.engine
-# "level = INFO" logs SQL queries.
-# "level = DEBUG" logs SQL queries and results.
-# "level = WARN" logs neither.  (Recommended for production systems.)
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-    '';
-in {
-  options = {
-    services.almir = {
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Enable Almir web server. Also configures postgresql database and installs bacula.
-        '';
-      };
-
-      port = mkOption {
-        default = 35000;
-        type = types.int;
-        description = ''
-          Port for Almir web server to listen on.
-        '';
-      };
-
-      timezone = mkOption {
-	description = ''
-         Timezone as specified in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-        '';
-        example = "Europe/Ljubljana";
-      };
-
-      sqlalchemy_engine_url = mkOption {
-        default = "postgresql:///bacula";
-        example = ''
-          postgresql://bacula:bacula@localhost:5432/bacula
-          mysql+mysqlconnector://<user>:<password>@<hostname>/<database>'
-          sqlite:////var/lib/bacula/bacula.db'
-        '';
-	description = ''
-         Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
-        '';
-      };
-
-      director_name = mkOption {
-        description = ''
-          Name of the Director to connect with bconsole.
-        '';
-      };
-
-      director_password = mkOption {
-        description = ''
-          Password for Director to connect with bconsole.
-        '';
-      };
-
-      director_port = mkOption {
-        default = 9101;
-        type = types.int;
-        description = ''
-          Port for Director to connect with bconsole.
-        '';
-      };
-
-      director_address = mkOption {
-        default = "127.0.0.1";
-        description = ''
-          IP/Hostname for Director to connect with bconsole.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.almir = {
-      after = [ "network.target" "postgresql.service" ];
-      description = "Almir web app";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.pythonPackages.almir ];
-      environment.PYTHONPATH = "${pkgs.pythonPackages.almir}/lib/${pkgs.pythonPackages.python.libPrefix}/site-packages";
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.pyramid}/bin/pserve ${productionini}";
-    };
-
-    environment.systemPackages = [ pkgs.pythonPackages.almir ];
-
-    users.extraUsers.almir = {
-      group = "almir";
-      uid = config.ids.uids.almir;
-      createHome = true;
-      shell = "${pkgs.bash}/bin/bash";
-    };
-
-    users.extraGroups.almir.gid = config.ids.gids.almir;
-  };
-}
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
new file mode 100644
index 000000000000..1b730e0c2b76
--- /dev/null
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -0,0 +1,580 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  isLocalPath = x:
+    builtins.substring 0 1 x == "/"      # absolute path
+    || builtins.substring 0 1 x == "."   # relative path
+    || builtins.match "[.*:.*]" == null; # not machine:path
+ 
+  mkExcludeFile = cfg:
+    # Write each exclude pattern to a new line
+    pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
+
+  mkKeepArgs = cfg:
+    # If cfg.prune.keep e.g. has a yearly attribute,
+    # its content is passed on as --keep-yearly
+    concatStringsSep " "
+      (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
+
+  mkBackupScript = cfg: ''
+    on_exit()
+    {
+      exitStatus=$?
+      # Reset the EXIT handler, or else we're called again on 'exit' below
+      trap - EXIT
+      ${cfg.postHook}
+      exit $exitStatus
+    }
+    trap 'on_exit' INT TERM QUIT EXIT
+
+    archiveName="${cfg.archiveBaseName}-$(date ${cfg.dateFormat})"
+    archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
+    ${cfg.preHook}
+  '' + optionalString cfg.doInit ''
+    # Run borg init if the repo doesn't exist yet
+    if ! borg list > /dev/null; then
+      borg init \
+        --encryption ${cfg.encryption.mode} \
+        $extraInitArgs
+      ${cfg.postInit}
+    fi
+  '' + ''
+    borg create \
+      --compression ${cfg.compression} \
+      --exclude-from ${mkExcludeFile cfg} \
+      $extraCreateArgs \
+      "::$archiveName$archiveSuffix" \
+      ${escapeShellArgs cfg.paths}
+  '' + optionalString cfg.appendFailedSuffix ''
+    borg rename "::$archiveName$archiveSuffix" "$archiveName"
+  '' + ''
+    ${cfg.postCreate}
+  '' + optionalString (cfg.prune.keep != { }) ''
+    borg prune \
+      ${mkKeepArgs cfg} \
+      --prefix ${escapeShellArg cfg.prune.prefix} \
+      $extraPruneArgs
+    ${cfg.postPrune}
+  '';
+
+  mkPassEnv = cfg: with cfg.encryption;
+    if passCommand != null then
+      { BORG_PASSCOMMAND = passCommand; }
+    else if passphrase != null then
+      { BORG_PASSPHRASE = passphrase; }
+    else { };
+
+  mkBackupService = name: cfg: 
+    let
+      userHome = config.users.users.${cfg.user}.home;
+    in nameValuePair "borgbackup-job-${name}" {
+      description = "BorgBackup job ${name}";
+      path = with pkgs; [
+        borgbackup openssh
+      ];
+      script = mkBackupScript cfg;
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        # Only run when no other process is using CPU or disk
+        CPUSchedulingPolicy = "idle";
+        IOSchedulingClass = "idle";
+        ProtectSystem = "strict";
+        ReadWritePaths =
+          [ "${userHome}/.config/borg" "${userHome}/.cache/borg" ]
+          # Borg needs write access to repo if it is not remote
+          ++ optional (isLocalPath cfg.repo) cfg.repo;
+        PrivateTmp = true;
+      };
+      environment = {
+        BORG_REPO = cfg.repo;
+        inherit (cfg) extraInitArgs extraCreateArgs extraPruneArgs;
+      } // (mkPassEnv cfg) // cfg.environment;
+      inherit (cfg) startAt;
+    };
+
+  # Paths listed in ReadWritePaths must exist before service is started
+  mkActivationScript = name: cfg:
+    let
+      install = "install -o ${cfg.user} -g ${cfg.group}";
+    in
+      nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] (''
+        # Eensure that the home directory already exists
+        # We can't assert createHome == true because that's not the case for root
+        cd "${config.users.users.${cfg.user}.home}"                                                                                                         
+        ${install} -d .config/borg
+        ${install} -d .cache/borg
+      '' + optionalString (isLocalPath cfg.repo) ''
+        ${install} -d ${escapeShellArg cfg.repo}
+      ''));
+
+  mkPassAssertion = name: cfg: {
+    assertion = with cfg.encryption;
+      mode != "none" -> passCommand != null || passphrase != null;
+    message =
+      "passCommand or passphrase has to be specified because"
+      + '' borgbackup.jobs.${name}.encryption != "none"'';
+  };
+
+  mkRepoService = name: cfg:
+    nameValuePair "borgbackup-repo-${name}" {
+      description = "Create BorgBackup repository ${name} directory";
+      script = ''
+        mkdir -p ${escapeShellArg cfg.path}
+        chown ${cfg.user}:${cfg.group} ${escapeShellArg cfg.path}
+      '';
+      serviceConfig = {
+        # The service's only task is to ensure that the specified path exists
+        Type = "oneshot";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+  mkAuthorizedKey = cfg: appendOnly: key:
+    let
+      # Because of the following line, clients do not need to specify an absolute repo path
+      cdCommand = "cd ${escapeShellArg cfg.path}";
+      restrictedArg = "--restrict-to-${if cfg.allowSubRepos then "path" else "repository"} .";
+      appendOnlyArg = optionalString appendOnly "--append-only";
+      quotaArg = optionalString (cfg.quota != null) "--storage-quota ${cfg.quota}";
+      serveCommand = "borg serve ${restrictedArg} ${appendOnlyArg} ${quotaArg}";
+    in
+      ''command="${cdCommand} && ${serveCommand}",restrict ${key}'';
+
+  mkUsersConfig = name: cfg: {
+    users.${cfg.user} = {
+      openssh.authorizedKeys.keys =
+        (map (mkAuthorizedKey cfg false) cfg.authorizedKeys
+        ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
+      useDefaultShell = true;
+    };
+    groups.${cfg.group} = { };
+  };
+
+  mkKeysAssertion = name: cfg: {
+    assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ];
+    message =
+      "borgbackup.repos.${name} does not make sense"
+      + " without at least one public key";
+  };
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  ###### interface
+
+  options.services.borgbackup.jobs = mkOption {
+    description = "Deduplicating backups using BorgBackup.";
+    default = { };
+    example = literalExample ''
+      {
+        rootBackup = {
+          paths = "/";
+          exclude = [ "/nix" ];
+          repo = "/path/to/local/repo";
+          encryption = {
+            mode = "repokey";
+            passphrase = "secret";
+          };
+          compression = "auto,lzma";
+          startAt = "weekly";
+        };
+      }
+    '';
+    type = types.attrsOf (types.submodule (let globalConfig = config; in
+      { name, config, ... }: {
+        options = {
+
+          paths = mkOption {
+            type = with types; either path (nonEmptyListOf path);
+            description = "Path(s) to back up.";
+            example = "/home/user";
+            apply = x: if isList x then x else [ x ];
+          };
+
+          repo = mkOption {
+            type = types.str;
+            description = "Remote or local repository to back up to.";
+            example = "user@machine:/path/to/repo";
+          };
+
+          archiveBaseName = mkOption {
+            type = types.strMatching "[^/{}]+";
+            default = "${globalConfig.networking.hostName}-${name}";
+            defaultText = "\${config.networking.hostName}-<name>";
+            description = ''
+              How to name the created archives. A timestamp, whose format is
+              determined by <option>dateFormat</option>, will be appended. The full
+              name can be modified at runtime (<literal>$archiveName</literal>).
+              Placeholders like <literal>{hostname}</literal> must not be used.
+            '';
+          };
+
+          dateFormat = mkOption {
+            type = types.str;
+            description = ''
+              Arguments passed to <command>date</command>
+              to create a timestamp suffix for the archive name.
+            '';
+            default = "+%Y-%m-%dT%H:%M:%S";
+            example = "-u +%s";
+          };
+
+          startAt = mkOption {
+            type = with types; either str (listOf str);
+            default = "daily";
+            description = ''
+              When or how often the backup should run.
+              Must be in the format described in
+              <citerefentry><refentrytitle>systemd.time</refentrytitle>
+              <manvolnum>7</manvolnum></citerefentry>.
+              If you do not want the backup to start
+              automatically, use <literal>[ ]</literal>.
+            '';
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg</command> is run as.
+              User or group need read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group borg is run as. User or group needs read permission
+              for the specified <option>paths</option>.
+            '';
+            default = "root";
+          };
+
+          encryption.mode = mkOption {
+            type = types.enum [
+              "repokey" "keyfile"
+              "repokey-blake2" "keyfile-blake2"
+              "authenticated" "authenticated-blake2"
+              "none"
+            ];
+            description = ''
+              Encryption mode to use. Setting a mode
+              other than <literal>"none"</literal> requires
+              you to specify a <option>passCommand</option>
+              or a <option>passphrase</option>.
+            '';
+          };
+
+          encryption.passCommand = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              A command which prints the passphrase to stdout.
+              Mutually exclusive with <option>passphrase</option>.
+            '';
+            default = null;
+            example = "cat /path/to/passphrase_file";
+          };
+
+          encryption.passphrase = mkOption {
+            type = with types; nullOr str;
+            description = ''
+              The passphrase the backups are encrypted with.
+              Mutually exclusive with <option>passCommand</option>.
+              If you do not want the passphrase to be stored in the
+              world-readable Nix store, use <option>passCommand</option>.
+            '';
+            default = null;
+          };
+
+          compression = mkOption {
+            # "auto" is optional,
+            # compression mode must be given,
+            # compression level is optional
+            type = types.strMatching "none|(auto,)?(lz4|zstd|zlib|lzma)(,[[:digit:]]{1,2})?";
+            description = ''
+              Compression method to use. Refer to
+              <command>borg help compression</command>
+              for all available options.
+            '';
+            default = "lz4";
+            example = "auto,lzma";
+          };
+
+          exclude = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Exclude paths matching any of the given patterns. See
+              <command>borg help patterns</command> for pattern syntax.
+            '';
+            default = [ ];
+            example = [
+              "/home/*/.cache"
+              "/nix"
+            ];
+          };
+
+          doInit = mkOption {
+            type = types.bool;
+            description = ''
+              Run <command>borg init</command> if the
+              specified <option>repo</option> does not exist.
+              You should set this to <literal>false</literal>
+              if the repository is located on an external drive
+              that might not always be mounted.
+            '';
+            default = true;
+          };
+
+          appendFailedSuffix = mkOption {
+            type = types.bool;
+            description = ''
+              Append a <literal>.failed</literal> suffix
+              to the archive name, which is only removed if
+              <command>borg create</command> has a zero exit status.
+            '';
+            default = true;
+          };
+
+          prune.keep = mkOption {
+            # Specifying e.g. `prune.keep.yearly = -1`
+            # means there is no limit of yearly archives to keep
+            # The regex is for use with e.g. --keep-within 1y
+            type = with types; attrsOf (either int (strMatching "[[:digit:]]+[Hdwmy]"));
+            description = ''
+              Prune a repository by deleting all archives not matching any of the
+              specified retention options. See <command>borg help prune</command>
+              for the available options.
+            '';
+            default = { };
+            example = literalExample ''
+              {
+                within = "1d"; # Keep all archives from the last day
+                daily = 7;
+                weekly = 4;
+                monthly = -1;  # Keep at least one archive for each month
+              }
+            '';
+          };
+
+          prune.prefix = mkOption {
+            type = types.str;
+            description = ''
+              Only consider archive names starting with this prefix for pruning.
+              By default, only archives created by this job are considered.
+              Use <literal>""</literal> to consider all archives.
+            '';
+            default = config.archiveBaseName;
+            defaultText = "\${archiveBaseName}";
+          };
+
+          environment = mkOption {
+            type = with types; attrsOf str;
+            description = ''
+              Environment variables passed to the backup script.
+              You can for example specify which SSH key to use.
+            '';
+            default = { };
+            example = { BORG_RSH = "ssh -i /path/to/key"; };
+          };
+
+          preHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run before the backup.
+              This can for example be used to mount file systems.
+            '';
+            default = "";
+            example = ''
+              # To add excluded paths at runtime
+              extraCreateArgs="$extraCreateArgs --exclude /some/path"
+            '';
+          };
+
+          postInit = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg init</command>.
+            '';
+            default = "";
+          };
+
+          postCreate = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg create</command>. The name
+              of the created archive is stored in <literal>$archiveName</literal>.
+            '';
+            default = "";
+          };
+
+          postPrune = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run after <command>borg prune</command>.
+            '';
+            default = "";
+          };
+
+          postHook = mkOption {
+            type = types.lines;
+            description = ''
+              Shell commands to run just before exit. They are executed
+              even if a previous command exits with a non-zero exit code.
+              The latter is available as <literal>$exitStatus</literal>.
+            '';
+            default = "";
+          };
+
+          extraInitArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg init</command>.
+              Can also be set at runtime using <literal>$extraInitArgs</literal>.
+            '';
+            default = "";
+            example = "--append-only";
+          };
+
+          extraCreateArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg create</command>.
+              Can also be set at runtime using <literal>$extraCreateArgs</literal>.
+            '';
+            default = "";
+            example = "--stats --checkpoint-interval 600";
+          };
+
+          extraPruneArgs = mkOption {
+            type = types.str;
+            description = ''
+              Additional arguments for <command>borg prune</command>.
+              Can also be set at runtime using <literal>$extraPruneArgs</literal>.
+            '';
+            default = "";
+            example = "--save-space";
+          };
+
+        };
+      }
+    ));
+  };
+
+  options.services.borgbackup.repos = mkOption {
+    description = ''
+      Serve BorgBackup repositories to given public SSH keys,
+      restricting their access to the repository only.
+      Also, clients do not need to specify the absolute path when accessing the repository,
+      i.e. <literal>user@machine:.</literal> is enough. (Note colon and dot.)
+    '';
+    default = { };
+    type = types.attrsOf (types.submodule (
+      { name, config, ... }: {
+        options = {
+          
+          path = mkOption {
+            type = types.path;
+            description = ''
+              Where to store the backups. Note that the directory
+              is created automatically, with correct permissions.
+            '';
+            default = "/var/lib/borgbackup";
+          };
+
+          user = mkOption {
+            type = types.str;
+            description = ''
+              The user <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          group = mkOption {
+            type = types.str;
+            description = ''
+              The group <command>borg serve</command> is run as.
+              User or group needs write permission
+              for the specified <option>path</option>.
+            '';
+            default = "borg";
+          };
+
+          authorizedKeys = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that are given full write access to this repository.
+              You should use a different SSH key for each repository you write to, because
+              the specified keys are restricted to running <command>borg serve</command>
+              and can only access this single repository.
+            '';
+            default = [ ];
+          };
+
+          authorizedKeysAppendOnly = mkOption {
+            type = with types; listOf str;
+            description = ''
+              Public SSH keys that can only be used to append new data (archives) to the repository.
+              Note that archives can still be marked as deleted and are subsequently removed from disk
+              upon accessing the repo with full write access, e.g. when pruning.
+            '';
+            default = [ ];
+          };
+
+          allowSubRepos = mkOption {
+            type = types.bool;
+            description = ''
+              Allow clients to create repositories in subdirectories of the
+              specified <option>path</option>. These can be accessed using
+              <literal>user@machine:path/to/subrepo</literal>. Note that a
+              <option>quota</option> applies to repositories independently.
+              Therefore, if this is enabled, clients can create multiple
+              repositories and upload an arbitrary amount of data.
+            '';
+            default = false;
+          };
+
+          quota = mkOption {
+            # See the definition of parse_file_size() in src/borg/helpers/parseformat.py
+            type = with types; nullOr (strMatching "[[:digit:].]+[KMGTP]?");
+            description = ''
+              Storage quota for the repository. This quota is ensured for all
+              sub-repositories if <option>allowSubRepos</option> is enabled
+              but not for the overall storage space used.
+            '';
+            default = null;
+            example = "100G";
+          };
+
+        };
+      }
+    ));
+  };
+
+  ###### implementation
+
+  config = mkIf (with config.services.borgbackup; jobs != { } || repos != { })
+    (with config.services.borgbackup; {
+      assertions =
+        mapAttrsToList mkPassAssertion jobs
+        ++ mapAttrsToList mkKeysAssertion repos;
+
+      system.activationScripts = mapAttrs' mkActivationScript jobs;
+
+      systemd.services =
+        # A job named "foo" is mapped to systemd.services.borgbackup-job-foo
+        mapAttrs' mkBackupService jobs
+        # A repo named "foo" is mapped to systemd.services.borgbackup-repo-foo
+        // mapAttrs' mkRepoService repos;
+
+      users = mkMerge (mapAttrsToList mkUsersConfig repos);
+
+      environment.systemPackages = with pkgs; [ borgbackup ];
+    });
+}
diff --git a/nixos/modules/services/backup/crashplan-small-business.nix b/nixos/modules/services/backup/crashplan-small-business.nix
new file mode 100644
index 000000000000..9497d8c18bb7
--- /dev/null
+++ b/nixos/modules/services/backup/crashplan-small-business.nix
@@ -0,0 +1,74 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.crashplansb;
+  crashplansb = pkgs.crashplansb.override { maxRam = cfg.maxRam; };
+  varDir = "/var/lib/crashplan";
+in
+
+with lib;
+
+{
+  options = {
+    services.crashplansb = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Starts crashplan for small business background service.
+        '';
+      };
+      maxRam = mkOption {
+        default = "1024m";
+        example = "2G";
+        type = types.str;
+        description = ''
+          Maximum amount of ram that the crashplan engine should use.
+        '';
+      };
+      openPorts = mkOption {
+        description = "Open ports in the firewall for crashplan.";
+        default = true;
+        type = types.bool;
+      };
+      ports =  mkOption {
+        # https://support.code42.com/Administrator/6/Planning_and_installing/TCP_and_UDP_ports_used_by_the_Code42_platform
+        # used ports can also be checked in the desktop app console using the command connection.info
+        description = "which ports to open.";
+        default = [ 4242 4243 4244 4247 ];
+        type = types.listOf types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ crashplansb ];
+    networking.firewall.allowedTCPPorts = mkIf cfg.openPorts cfg.ports;
+
+    systemd.services.crashplansb = {
+      description = "CrashPlan Backup Engine";
+
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network.target" "local-fs.target" ];
+
+      preStart = ''
+        install -d -m 755 ${crashplansb.vardir}
+        install -d -m 700 ${crashplansb.vardir}/conf
+        install -d -m 700 ${crashplansb.manifestdir}
+        install -d -m 700 ${crashplansb.vardir}/cache
+        install -d -m 700 ${crashplansb.vardir}/backupArchives
+        install -d -m 777 ${crashplansb.vardir}/log
+        cp -avn ${crashplansb}/conf.template/* ${crashplansb.vardir}/conf
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        EnvironmentFile = "${crashplansb}/bin/run.conf";
+        ExecStart = "${crashplansb}/bin/CrashPlanEngine start";
+        ExecStop = "${crashplansb}/bin/CrashPlanEngine stop";
+        PIDFile = "${crashplansb.vardir}/CrashPlanEngine.pid";
+        WorkingDirectory = crashplansb;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/backup/duplicati.nix b/nixos/modules/services/backup/duplicati.nix
new file mode 100644
index 000000000000..9772ca4d20a7
--- /dev/null
+++ b/nixos/modules/services/backup/duplicati.nix
@@ -0,0 +1,40 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.duplicati;
+in
+{
+  options = {
+    services.duplicati = {
+      enable = mkEnableOption "Duplicati";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.duplicati ];
+
+    systemd.services.duplicati = {
+      description = "Duplicati backup";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "duplicati";
+        Group = "duplicati";
+        ExecStart = "${pkgs.duplicati}/bin/duplicati-server --webservice-interface=any --webservice-port=8200 --server-datafolder=/var/lib/duplicati";
+        Restart = "on-failure";
+      };
+    };
+
+    users.extraUsers.duplicati = {
+      uid = config.ids.uids.duplicati;
+      home = "/var/lib/duplicati";
+      createHome = true;
+      group = "duplicati";
+    };
+    users.extraGroups.duplicati.gid = config.ids.gids.duplicati;
+
+  };
+}
+
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
new file mode 100644
index 000000000000..21d82469c605
--- /dev/null
+++ b/nixos/modules/services/backup/restic.nix
@@ -0,0 +1,150 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options.services.restic.backups = mkOption {
+    description = ''
+      Periodic backups to create with Restic.
+    '';
+    type = types.attrsOf (types.submodule ({ name, config, ... }: {
+      options = {
+        passwordFile = mkOption {
+          type = types.str;
+          description = ''
+            Read the repository password from a file.
+          '';
+          example = "/etc/nixos/restic-password";
+
+        };
+
+        repository = mkOption {
+          type = types.str;
+          description = ''
+            repository to backup to.
+          '';
+          example = "sftp:backup@192.168.1.100:/backups/${name}";
+        };
+
+        paths = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Which paths to backup.
+          '';
+          example = [
+            "/var/lib/postgresql"
+            "/home/user/backup"
+          ];
+        };
+
+        timerConfig = mkOption {
+          type = types.attrsOf types.str;
+          default = {
+            OnCalendar = "daily";
+          };
+          description = ''
+            When to run the backup. See man systemd.timer for details.
+          '';
+          example = {
+            OnCalendar = "00:05";
+            RandomizedDelaySec = "5h";
+          };
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "root";
+          description = ''
+            As which user the backup should run.
+          '';
+          example = "postgresql";
+        };
+
+        extraBackupArgs = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Extra arguments passed to restic backup.
+          '';
+          example = [
+            "--exclude-file=/etc/nixos/restic-ignore"
+          ];
+        };
+
+        extraOptions = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          description = ''
+            Extra extended options to be passed to the restic --option flag.
+          '';
+          example = [
+            "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
+          ];
+        };
+
+        initialize = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Create the repository if it doesn't exist.
+          '';
+        };
+      };
+    }));
+    default = {};
+    example = {
+      localbackup = {
+        paths = [ "/home" ];
+        repository = "/mnt/backup-hdd";
+        passwordFile = "/etc/nixos/secrets/restic-password";
+        initialize = true;
+      };
+      remotebackup = {
+        paths = [ "/home" ];
+        repository = "sftp:backup@host:/backups/home";
+        passwordFile = "/etc/nixos/secrets/restic-password";
+        extraOptions = [
+          "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'"
+        ];
+        timerConfig = {
+          OnCalendar = "00:05";
+          RandomizedDelaySec = "5h";
+        };
+      };
+    };
+  };
+
+  config = {
+    systemd.services =
+      mapAttrs' (name: backup:
+        let
+          extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
+          connectTo = elemAt (splitString ":" backup.repository) 1;
+          resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
+        in nameValuePair "restic-backups-${name}" ({
+          environment = {
+            RESTIC_PASSWORD_FILE = backup.passwordFile;
+            RESTIC_REPOSITORY = backup.repository;
+          };
+          path = with pkgs; [
+            openssh
+          ];
+          restartIfChanged = false;
+          serviceConfig = {
+            Type = "oneshot";
+            ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}";
+            User = backup.user;
+          };
+        } // optionalAttrs backup.initialize {
+          preStart = ''
+            ${resticCmd} snapshots || ${resticCmd} init
+          '';
+        })
+      ) config.services.restic.backups;
+    systemd.timers =
+      mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" {
+        wantedBy = [ "timers.target" ];
+        timerConfig = backup.timerConfig;
+      }) config.services.restic.backups;
+  };
+}
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 7c9dedb67ad2..4fc7c24813a5 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -115,7 +115,7 @@ in
                 description = ''
                   Print global archive statistics upon completion.
                   The output is available via
-                  <command>systemctl status tarsnap@archive-name</command>.
+                  <command>systemctl status tarsnap-archive-name</command>.
                 '';
               };
 
@@ -238,6 +238,20 @@ in
                   Whether to produce verbose logging output.
                 '';
               };
+              explicitSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow symlinks specified as archives.
+                '';
+              };
+              followSymlinks = mkOption {
+                type = types.bool;
+                default = false;
+                description = ''
+                  Whether to follow all symlinks in archive trees.
+                '';
+              };
             };
           }
         ));
@@ -285,12 +299,12 @@ in
         }) gcfg.archives);
 
     systemd.services =
-      mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
         description = "Tarsnap archive '${name}'";
         requires    = [ "network-online.target" ];
         after       = [ "network-online.target" ];
 
-        path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ];
+        path = with pkgs; [ iputils tarsnap utillinux ];
 
         # In order for the persistent tarsnap timer to work reliably, we have to
         # make sure that the tarsnap server is reachable after systemd starts up
@@ -300,10 +314,12 @@ in
           while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done
         '';
 
-        script =
-          let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \
-                        -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
                         ${optionalString cfg.verbose "-v"} \
+                        ${optionalString cfg.explicitSymlinks "-H"} \
+                        ${optionalString cfg.followSymlinks "-L"} \
                         ${concatStringsSep " " cfg.directories}'';
           in if (cfg.cachedir != null) then ''
             mkdir -p ${cfg.cachedir}
@@ -313,7 +329,7 @@ in
               if [ ! -e ${cfg.cachedir}/firstrun ]; then
                 ( flock 10
                   flock -u 9
-                  tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck
+                  ${tarsnap} --fsck
                   flock 9
                 ) 10>${cfg.cachedir}/firstrun
               fi
@@ -329,7 +345,44 @@ in
           CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
           PermissionsStartOnly = "true";
         };
-      }) gcfg.archives;
+      }) gcfg.archives) //
+
+      (mapAttrs' (name: cfg: nameValuePair "tarsnap-restore-${name}"{
+        description = "Tarsnap restore '${name}'";
+        requires    = [ "network-online.target" ];
+
+        path = with pkgs; [ iputils tarsnap utillinux ];
+
+        script = let
+          tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
+          lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)'';
+          run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
+
+        in if (cfg.cachedir != null) then ''
+          mkdir -p ${cfg.cachedir}
+          chmod 0700 ${cfg.cachedir}
+
+          ( flock 9
+            if [ ! -e ${cfg.cachedir}/firstrun ]; then
+              ( flock 10
+                flock -u 9
+                ${tarsnap} --fsck
+                flock 9
+              ) 10>${cfg.cachedir}/firstrun
+            fi
+          ) 9>${cfg.cachedir}/lockf
+
+           exec flock ${cfg.cachedir}/firstrun ${run}
+        '' else "exec ${run}";
+
+        serviceConfig = {
+          Type = "oneshot";
+          IOSchedulingClass = "idle";
+          NoNewPrivileges = "true";
+          CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
+          PermissionsStartOnly = "true";
+        };
+      }) gcfg.archives);
 
     # Note: the timer must be Persistent=true, so that systemd will start it even
     # if e.g. your laptop was asleep while the latest interval occurred.
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index baf99930e3eb..3d133f82d204 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -1,39 +1,372 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with types;
 
 let
+
+  # Converts a plan like
+  #   { "1d" = "1h"; "1w" = "1d"; }
+  # into
+  #   "1d=>1h,1w=>1d"
+  attrToPlan = attrs: concatStringsSep "," (builtins.attrValues (
+    mapAttrs (n: v: "${n}=>${v}") attrs));
+
+  planDescription = ''
+      The znapzend backup plan to use for the source.
+    </para>
+    <para>
+      The plan specifies how often to backup and for how long to keep the
+      backups. It consists of a series of retention periodes to interval
+      associations:
+    </para>
+    <para>
+      <literal>
+        retA=>intA,retB=>intB,...
+      </literal>
+    </para>
+    <para>
+    Both intervals and retention periods are expressed in standard units
+    of time or multiples of them. You can use both the full name or a
+    shortcut according to the following listing:
+    </para>
+    <para>
+      <literal>
+        second|sec|s, minute|min, hour|h, day|d, week|w, month|mon|m, year|y
+      </literal>
+    </para>
+    <para>
+      See <citerefentry><refentrytitle>znapzendzetup</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more info.
+  '';
+  planExample = "1h=>10min,1d=>1h,1w=>1d,1m=>1w,1y=>1m";
+
+  # A type for a string of the form number{b|k|M|G}
+  mbufferSizeType = str // {
+    check = x: str.check x && builtins.isList (builtins.match "^[0-9]+[bkMG]$" x);
+    description = "string of the form number{b|k|M|G}";
+  };
+
+  # Type for a string that must contain certain other strings (the list parameter).
+  # Note that these would need regex escaping.
+  stringContainingStrings = list: let
+    matching = s: map (str: builtins.match ".*${str}.*" s) list;
+  in str // {
+    check = x: str.check x && all isList (matching x);
+    description = "string containing all of the characters ${concatStringsSep ", " list}";
+  };
+
+  timestampType = stringContainingStrings [ "%Y" "%m" "%d" "%H" "%M" "%S" ];
+
+  destType = srcConfig: submodule ({ name, ... }: {
+    options = {
+
+      label = mkOption {
+        type = str;
+        description = "Label for this destination. Defaults to the attribute name.";
+      };
+
+      plan = mkOption {
+        type = str;
+        description = planDescription;
+        example = planExample;
+      };
+
+      dataset = mkOption {
+        type = str;
+        description = "Dataset name to send snapshots to.";
+        example = "tank/main";
+      };
+
+      host = mkOption {
+        type = nullOr str;
+        description = ''
+          Host to use for the destination dataset. Can be prefixed with
+          <literal>user@</literal> to specify the ssh user.
+        '';
+        default = null;
+        example = "john@example.com";
+      };
+
+      presend = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run before sending the snapshot to the destination.
+          Intended to run a remote script via <command>ssh</command> on the
+          destination, e.g. to bring up a backup disk or server or to put a
+          zpool online/offline. See also <option>postsend</option>.
+        '';
+        default = null;
+        example = "ssh root@bserv zpool import -Nf tank";
+      };
+
+      postsend = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run after sending the snapshot to the destination.
+          Intended to run a remote script via <command>ssh</command> on the
+          destination, e.g. to bring up a backup disk or server or to put a
+          zpool online/offline. See also <option>presend</option>.
+        '';
+        default = null;
+        example = "ssh root@bserv zpool export tank";
+      };
+    };
+
+    config = {
+      label = mkDefault name;
+      plan = mkDefault srcConfig.plan;
+    };
+  });
+
+
+
+  srcType = submodule ({ name, config, ... }: {
+    options = {
+
+      enable = mkOption {
+        type = bool;
+        description = "Whether to enable this source.";
+        default = true;
+      };
+
+      recursive = mkOption {
+        type = bool;
+        description = "Whether to do recursive snapshots.";
+        default = false;
+      };
+
+      mbuffer = {
+        enable = mkOption {
+          type = bool;
+          description = "Whether to use <command>mbuffer</command>.";
+          default = false;
+        };
+
+        port = mkOption {
+          type = nullOr ints.u16;
+          description = ''
+              Port to use for <command>mbuffer</command>.
+            </para>
+            <para>
+              If this is null, it will run <command>mbuffer</command> through
+              ssh.
+            </para>
+            <para>
+              If this is not null, it will run <command>mbuffer</command>
+              directly through TCP, which is not encrypted but faster. In that
+              case the given port needs to be open on the destination host.
+          '';
+          default = null;
+        };
+
+        size = mkOption {
+          type = mbufferSizeType;
+          description = ''
+            The size for <command>mbuffer</command>.
+            Supports the units b, k, M, G.
+          '';
+          default = "1G";
+          example = "128M";
+        };
+      };
+
+      presnap = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run before snapshots are taken on the source dataset,
+          e.g. for database locking/flushing. See also
+          <option>postsnap</option>.
+        '';
+        default = null;
+        example = literalExample ''
+          ''${pkgs.mariadb}/bin/mysql -e "set autocommit=0;flush tables with read lock;\\! ''${pkgs.coreutils}/bin/sleep 600" &  ''${pkgs.coreutils}/bin/echo $! > /tmp/mariadblock.pid ; sleep 10
+        '';
+      };
+
+      postsnap = mkOption {
+        type = nullOr str;
+        description = ''
+          Command to run after snapshots are taken on the source dataset,
+          e.g. for database unlocking. See also <option>presnap</option>.
+        '';
+        default = null;
+        example = literalExample ''
+          ''${pkgs.coreutils}/bin/kill `''${pkgs.coreutils}/bin/cat /tmp/mariadblock.pid`;''${pkgs.coreutils}/bin/rm /tmp/mariadblock.pid
+        '';
+      };
+
+      timestampFormat = mkOption {
+        type = timestampType;
+        description = ''
+          The timestamp format to use for constructing snapshot names.
+          The syntax is <literal>strftime</literal>-like. The string must
+          consist of the mandatory <literal>%Y %m %d %H %M %S</literal>.
+          Optionally  <literal>- _ . :</literal>  characters as well as any
+          alphanumeric character are allowed. If suffixed by a
+          <literal>Z</literal>, times will be in UTC.
+        '';
+        default = "%Y-%m-%d-%H%M%S";
+        example = "znapzend-%m.%d.%Y-%H%M%SZ";
+      };
+
+      sendDelay = mkOption {
+        type = int;
+        description = ''
+          Specify delay (in seconds) before sending snaps to the destination.
+          May be useful if you want to control sending time.
+        '';
+        default = 0;
+        example = 60;
+      };
+
+      plan = mkOption {
+        type = str;
+        description = planDescription;
+        example = planExample;
+      };
+
+      dataset = mkOption {
+        type = str;
+        description = "The dataset to use for this source.";
+        example = "tank/home";
+      };
+
+      destinations = mkOption {
+        type = loaOf (destType config);
+        description = "Additional destinations.";
+        default = {};
+        example = literalExample ''
+          {
+            local = {
+              dataset = "btank/backup";
+              presend = "zpool import -N btank";
+              postsend = "zpool export btank";
+            };
+            remote = {
+              host = "john@example.com";
+              dataset = "tank/john";
+            };
+          };
+        '';
+      };
+    };
+
+    config = {
+      dataset = mkDefault name;
+    };
+
+  });
+
+  ### Generating the configuration from here
+
   cfg = config.services.znapzend;
+
+  onOff = b: if b then "on" else "off";
+  nullOff = b: if isNull b then "off" else toString b;
+  stripSlashes = replaceStrings [ "/" ] [ "." ];
+
+  attrsToFile = config: concatStringsSep "\n" (builtins.attrValues (
+    mapAttrs (n: v: "${n}=${v}") config));
+
+  mkDestAttrs = dst: with dst;
+    mapAttrs' (n: v: nameValuePair "dst_${label}${n}" v) ({
+      "" = optionalString (! isNull host) "${host}:" + dataset;
+      _plan = plan;
+    } // optionalAttrs (presend != null) {
+      _precmd = presend;
+    } // optionalAttrs (postsend != null) {
+      _pstcmd = postsend;
+    });
+
+  mkSrcAttrs = srcCfg: with srcCfg; {
+    enabled = onOff enable;
+    mbuffer = with mbuffer; if enable then "${pkgs.mbuffer}/bin/mbuffer"
+        + optionalString (port != null) ":${toString port}" else "off";
+    mbuffer_size = mbuffer.size;
+    post_znap_cmd = nullOff postsnap;
+    pre_znap_cmd = nullOff presnap;
+    recursive = onOff recursive;
+    src = dataset;
+    src_plan = plan;
+    tsformat = timestampFormat;
+    zend_delay = toString sendDelay;
+  } // fold (a: b: a // b) {} (
+    map mkDestAttrs (builtins.attrValues destinations)
+  );
+
+  files = mapAttrs' (n: srcCfg: let
+    fileText = attrsToFile (mkSrcAttrs srcCfg);
+  in {
+    name = srcCfg.dataset;
+    value = pkgs.writeText (stripSlashes srcCfg.dataset) fileText;
+  }) cfg.zetup;
+
 in
 {
   options = {
     services.znapzend = {
-      enable = mkEnableOption "ZnapZend daemon";
+      enable = mkEnableOption "ZnapZend ZFS backup daemon";
 
       logLevel = mkOption {
         default = "debug";
         example = "warning";
-        type = lib.types.enum ["debug" "info" "warning" "err" "alert"];
-        description = "The log level when logging to file. Any of debug, info, warning, err, alert. Default in daemonized form is debug.";
+        type = enum ["debug" "info" "warning" "err" "alert"];
+        description = ''
+          The log level when logging to file. Any of debug, info, warning, err,
+          alert. Default in daemonized form is debug.
+        '';
       };
 
       logTo = mkOption {
-        type = types.str;
+        type = str;
         default = "syslog::daemon";
         example = "/var/log/znapzend.log";
-        description = "Where to log to (syslog::&lt;facility&gt; or &lt;filepath&gt;).";
+        description = ''
+          Where to log to (syslog::&lt;facility&gt; or &lt;filepath&gt;).
+        '';
       };
 
       noDestroy = mkOption {
-        type = types.bool;
+        type = bool;
         default = false;
         description = "Does all changes to the filesystem except destroy.";
       };
 
       autoCreation = mkOption {
-        type = types.bool;
+        type = bool;
+        default = false;
+        description = "Automatically create the destination dataset if it does not exists.";
+      };
+
+      zetup = mkOption {
+        type = loaOf srcType;
+        description = "Znapzend configuration.";
+        default = {};
+        example = literalExample ''
+          {
+            "tank/home" = {
+              # Make snapshots of tank/home every hour, keep those for 1 day,
+              # keep every days snapshot for 1 month, etc.
+              plan = "1d=>1h,1m=>1d,1y=>1m";
+              recursive = true;
+              # Send all those snapshots to john@example.com:rtank/john as well
+              destinations.remote = {
+                host = "john@example.com";
+                dataset = "rtank/john";
+              };
+            };
+          };
+        '';
+      };
+
+      pure = mkOption {
+        type = bool;
+        description = ''
+          Do not persist any stateful znapzend setups. If this option is
+          enabled, your previously set znapzend setups will be cleared and only
+          the ones defined with this module will be applied.
+        '';
         default = false;
-        description = "Automatically create the dataset on dest if it does not exists.";
       };
     };
   };
@@ -49,12 +382,30 @@ in
 
         path = with pkgs; [ zfs mbuffer openssh ];
 
+        preStart = optionalString cfg.pure ''
+          echo Resetting znapzend zetups
+          ${pkgs.znapzend}/bin/znapzendzetup list \
+            | grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \
+            | xargs -I{} ${pkgs.znapzend}/bin/znapzendzetup delete "{}"
+        '' + concatStringsSep "\n" (mapAttrsToList (dataset: config: ''
+          echo Importing znapzend zetup ${config} for dataset ${dataset}
+          ${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config}
+        '') files);
+
         serviceConfig = {
-          ExecStart = "${pkgs.znapzend}/bin/znapzend --logto=${cfg.logTo} --loglevel=${cfg.logLevel} ${optionalString cfg.noDestroy "--nodestroy"} ${optionalString cfg.autoCreation "--autoCreation"}";
+          ExecStart = let
+            args = concatStringsSep " " [
+              "--logto=${cfg.logTo}"
+              "--loglevel=${cfg.logLevel}"
+              (optionalString cfg.noDestroy "--nodestroy")
+              (optionalString cfg.autoCreation "--autoCreation")
+            ]; in "${pkgs.znapzend}/bin/znapzend ${args}";
           ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
           Restart = "on-failure";
         };
       };
     };
   };
+
+  meta.maintainers = with maintainers; [ infinisil ];
 }
diff --git a/nixos/modules/services/cluster/kubernetes/dashboard.nix b/nixos/modules/services/cluster/kubernetes/dashboard.nix
index 75d71fccfda4..e331889b9dd5 100644
--- a/nixos/modules/services/cluster/kubernetes/dashboard.nix
+++ b/nixos/modules/services/cluster/kubernetes/dashboard.nix
@@ -6,12 +6,12 @@ let
   cfg = config.services.kubernetes.addons.dashboard;
 
   name = "gcr.io/google_containers/kubernetes-dashboard-amd64";
-	version = "v1.6.3";
+	version = "v1.8.2";
 
   image = pkgs.dockerTools.pullImage {
     imageName = name;
     imageTag = version;
-    sha256 = "1sf54d96nkgic9hir9c6p14gw24ns1k5d5a0r1sg414kjrvic0b4";
+    sha256 = "11h0fz3wxp0f10fsyqaxjm7l2qg7xws50dv5iwlck5gb1fjmajad";
   };
 in {
   options.services.kubernetes.addons.dashboard = {
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index 077953e4d4f8..aeb0a0d2432d 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -279,7 +279,7 @@ in {
       tokenAuthFile = mkOption {
         description = ''
           Kubernetes apiserver token authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = null;
         type = types.nullOr types.path;
@@ -288,7 +288,7 @@ in {
       basicAuthFile = mkOption {
         description = ''
           Kubernetes apiserver basic authentication file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/>
         '';
         default = pkgs.writeText "users" ''
           kubernetes,admin,0
@@ -299,16 +299,16 @@ in {
       authorizationMode = mkOption {
         description = ''
           Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/RBAC). See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
-        default = ["RBAC"];
-        type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC"]);
+        default = ["RBAC" "Node"];
+        type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC" "Node"]);
       };
 
       authorizationPolicy = mkOption {
         description = ''
           Kubernetes apiserver authorization policy file. See
-          <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/>
         '';
         default = [];
         type = types.listOf types.attrs;
@@ -332,7 +332,7 @@ in {
       runtimeConfig = mkOption {
         description = ''
           Api runtime configuration. See
-          <link xlink:href="http://kubernetes.io/docs/admin/cluster-management.html"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/cluster-management.html"/>
         '';
         default = "authentication.k8s.io/v1beta1=true";
         example = "api/all=false,api/v1=true";
@@ -342,9 +342,9 @@ in {
       admissionControl = mkOption {
         description = ''
           Kubernetes admission control plugins to use. See
-          <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/>
+          <link xlink:href="https://kubernetes.io/docs/admin/admission-controllers/"/>
         '';
-        default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds"];
+        default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds" "NodeRestriction"];
         example = [
           "NamespaceLifecycle" "NamespaceExists" "LimitRanger"
           "SecurityContextDeny" "ServiceAccount" "ResourceQuota"
@@ -766,7 +766,7 @@ in {
           rm /opt/cni/bin/* || true
           ${concatMapStrings (package: ''
             echo "Linking cni package: ${package}"
-            ln -fs ${package.plugins}/* /opt/cni/bin
+            ln -fs ${package}/bin/* /opt/cni/bin
           '') cfg.kubelet.cni.packages}
         '';
         serviceConfig = {
@@ -828,7 +828,7 @@ in {
       };
 
       # Allways include cni plugins
-      services.kubernetes.kubelet.cni.packages = [pkgs.cni];
+      services.kubernetes.kubelet.cni.packages = [pkgs.cni-plugins];
 
       boot.kernelModules = ["br_netfilter"];
 
diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix
index fb91a29a4000..45d34f5b76f5 100644
--- a/nixos/modules/services/computing/slurm/slurm.nix
+++ b/nixos/modules/services/computing/slurm/slurm.nix
@@ -6,14 +6,20 @@ let
 
   cfg = config.services.slurm;
   # configuration file can be generated by http://slurm.schedmd.com/configurator.html
-  configFile = pkgs.writeText "slurm.conf" 
+  configFile = pkgs.writeText "slurm.conf"
     ''
       ${optionalString (cfg.controlMachine != null) ''controlMachine=${cfg.controlMachine}''}
       ${optionalString (cfg.controlAddr != null) ''controlAddr=${cfg.controlAddr}''}
       ${optionalString (cfg.nodeName != null) ''nodeName=${cfg.nodeName}''}
       ${optionalString (cfg.partitionName != null) ''partitionName=${cfg.partitionName}''}
+      PlugStackConfig=${plugStackConfig}
       ${cfg.extraConfig}
     '';
+
+  plugStackConfig = pkgs.writeText "plugstack.conf"
+    ''
+      ${optionalString cfg.enableSrunX11 ''optional ${pkgs.slurm-spank-x11}/lib/x11.so''}
+    '';
 in
 
 {
@@ -28,7 +34,7 @@ in
         enable = mkEnableOption "slurm control daemon";
 
       };
-      
+
       client = {
         enable = mkEnableOption "slurm rlient daemon";
 
@@ -86,8 +92,19 @@ in
         '';
       };
 
+      enableSrunX11 = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled srun will accept the option "--x11" to allow for X11 forwarding
+          from within an interactive session or a batch job. This activates the
+          slurm-spank-x11 module. Note that this requires 'services.openssh.forwardX11'
+          to be enabled on the compute nodes.
+        '';
+      };
+
       extraConfig = mkOption {
-        default = ""; 
+        default = "";
         type = types.lines;
         description = ''
           Extra configuration options that will be added verbatim at
@@ -134,7 +151,8 @@ in
     environment.systemPackages = [ wrappedSlurm ];
 
     systemd.services.slurmd = mkIf (cfg.client.enable) {
-      path = with pkgs; [ wrappedSlurm coreutils ];
+      path = with pkgs; [ wrappedSlurm coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
 
       wantedBy = [ "multi-user.target" ];
       after = [ "systemd-tmpfiles-clean.service" ];
@@ -152,8 +170,9 @@ in
     };
 
     systemd.services.slurmctld = mkIf (cfg.server.enable) {
-      path = with pkgs; [ wrappedSlurm munge coreutils ];
-      
+      path = with pkgs; [ wrappedSlurm munge coreutils ]
+        ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
+
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" "munged.service" ];
       requires = [ "munged.service" ];
diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix
index 9c06e1d43bbe..03af9a7859ec 100644
--- a/nixos/modules/services/continuous-integration/buildkite-agent.nix
+++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix
@@ -4,14 +4,31 @@ with lib;
 
 let
   cfg = config.services.buildkite-agent;
-  configFile = pkgs.writeText "buildkite-agent.cfg"
-    ''
-      token="${cfg.token}"
-      name="${cfg.name}"
-      meta-data="${cfg.meta-data}"
-      hooks-path="${cfg.package}/share/hooks"
-      build-path="${cfg.dataDir}"
+
+  mkHookOption = { name, description, example ? null }: {
+    inherit name;
+    value = mkOption {
+      default = null;
+      inherit description;
+      type = types.nullOr types.lines;
+    } // (if example == null then {} else { inherit example; });
+  };
+  mkHookOptions = hooks: listToAttrs (map mkHookOption hooks);
+
+  hooksDir = let
+    mkHookEntry = name: value: ''
+      cat > $out/${name} <<EOF
+      #! ${pkgs.runtimeShell}
+      set -e
+      ${value}
+      EOF
+      chmod 755 $out/${name}
     '';
+  in pkgs.runCommand "buildkite-agent-hooks" {} ''
+    mkdir $out
+    ${concatStringsSep "\n" (mapAttrsToList mkHookEntry (filterAttrs (n: v: v != null) cfg.hooks))}
+  '';
+
 in
 
 {
@@ -39,15 +56,19 @@ in
         type = types.listOf types.package;
       };
 
-      token = mkOption {
-        type = types.str;
+      tokenPath = mkOption {
+        type = types.path;
         description = ''
           The token from your Buildkite "Agents" page.
+
+          A run-time path to the token file, which is supposed to be provisioned
+          outside of Nix store.
         '';
       };
 
       name = mkOption {
         type = types.str;
+        default = "%hostname-%n";
         description = ''
           The name of the agent.
         '';
@@ -56,25 +77,110 @@ in
       meta-data = mkOption {
         type = types.str;
         default = "";
+        example = "queue=default,docker=true,ruby2=true";
+        description = ''
+          Meta data for the agent. This is a comma-separated list of
+          <code>key=value</code> pairs.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "debug=true";
         description = ''
-          Meta data for the agent.
+          Extra lines to be added verbatim to the configuration file.
         '';
       };
 
       openssh =
-        { privateKey = mkOption {
-            type = types.str;
+        { privateKeyPath = mkOption {
+            type = types.path;
             description = ''
               Private agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
-          publicKey = mkOption {
-            type = types.str;
+          publicKeyPath = mkOption {
+            type = types.path;
             description = ''
               Public agent key.
+
+              A run-time path to the key file, which is supposed to be provisioned
+              outside of Nix store.
             '';
           };
         };
+
+      hooks = mkHookOptions [
+        { name = "checkout";
+          description = ''
+            The `checkout` hook script will replace the default checkout routine of the
+            bootstrap.sh script. You can use this hook to do your own SCM checkout
+            behaviour
+          ''; }
+        { name = "command";
+          description = ''
+            The `command` hook script will replace the default implementation of running
+            the build command.
+          ''; }
+        { name = "environment";
+          description = ''
+            The `environment` hook will run before all other commands, and can be used
+            to set up secrets, data, etc. Anything exported in hooks will be available
+            to the build script.
+
+            Note: the contents of this file will be copied to the world-readable
+            Nix store.
+          '';
+          example = ''
+            export SECRET_VAR=`head -1 /run/keys/secret`
+          ''; }
+        { name = "post-artifact";
+          description = ''
+            The `post-artifact` hook will run just after artifacts are uploaded
+          ''; }
+        { name = "post-checkout";
+          description = ''
+            The `post-checkout` hook will run after the bootstrap script has checked out
+            your projects source code.
+          ''; }
+        { name = "post-command";
+          description = ''
+            The `post-command` hook will run after the bootstrap script has run your
+            build commands
+          ''; }
+        { name = "pre-artifact";
+          description = ''
+            The `pre-artifact` hook will run just before artifacts are uploaded
+          ''; }
+        { name = "pre-checkout";
+          description = ''
+            The `pre-checkout` hook will run just before your projects source code is
+            checked out from your SCM provider
+          ''; }
+        { name = "pre-command";
+          description = ''
+            The `pre-command` hook will run just before your build command runs
+          ''; }
+        { name = "pre-exit";
+          description = ''
+            The `pre-exit` hook will run just before your build job finishes
+          ''; }
+      ];
+
+      hooksPath = mkOption {
+        type = types.path;
+        default = hooksDir;
+        defaultText = "generated from services.buildkite-agent.hooks";
+        description = ''
+          Path to the directory storing the hooks.
+          Consider using <option>services.buildkite-agent.hooks.&lt;name&gt;</option>
+          instead.
+        '';
+      };
     };
   };
 
@@ -84,6 +190,7 @@ in
         home = cfg.dataDir;
         createHome = true;
         description = "Buildkite agent user";
+        extraGroups = [ "keys" ];
       };
 
     environment.systemPackages = [ cfg.package ];
@@ -92,28 +199,54 @@ in
       { description = "Buildkite Agent";
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
-        path = cfg.runtimePackages;
+        path = cfg.runtimePackages ++ [ pkgs.coreutils ];
         environment = config.networking.proxy.envVars // {
           HOME = cfg.dataDir;
           NIX_REMOTE = "daemon";
         };
-        preStart = ''
-          ${pkgs.coreutils}/bin/mkdir -m 0700 -p ${cfg.dataDir}/.ssh
-
-          echo "${cfg.openssh.privateKey}" > ${cfg.dataDir}/.ssh/id_rsa
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa
 
-          echo "${cfg.openssh.publicKey}" > ${cfg.dataDir}/.ssh/id_rsa.pub
-          ${pkgs.coreutils}/bin/chmod 600 ${cfg.dataDir}/.ssh/id_rsa.pub
-        '';
+        ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
+        ##     don't end up in the Nix store.
+        preStart = let
+          sshDir = "${cfg.dataDir}/.ssh";
+        in
+          ''
+            mkdir -m 0700 -p "${sshDir}"
+            cp -f "${toString cfg.openssh.privateKeyPath}" "${sshDir}/id_rsa"
+            cp -f "${toString cfg.openssh.publicKeyPath}"  "${sshDir}/id_rsa.pub"
+            chmod 600 "${sshDir}"/id_rsa*
+
+            cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
+            token="$(cat ${toString cfg.tokenPath})"
+            name="${cfg.name}"
+            meta-data="${cfg.meta-data}"
+            build-path="${cfg.dataDir}/builds"
+            hooks-path="${cfg.hooksPath}"
+            ${cfg.extraConfig}
+            EOF
+          '';
 
         serviceConfig =
-          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config ${configFile}";
+          { ExecStart = "${pkgs.buildkite-agent}/bin/buildkite-agent start --config /var/lib/buildkite-agent/buildkite-agent.cfg";
             User = "buildkite-agent";
             RestartSec = 5;
             Restart = "on-failure";
             TimeoutSec = 10;
           };
       };
+
+    assertions = [
+      { assertion = cfg.hooksPath == hooksDir || all isNull (attrValues cfg.hooks);
+        message = ''
+          Options `services.buildkite-agent.hooksPath' and
+          `services.buildkite-agent.hooks.<name>' are mutually exclusive.
+        '';
+      }
+    ];
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "token" ]                [ "services" "buildkite-agent" "tokenPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "privateKey" ] [ "services" "buildkite-agent" "openssh" "privateKeyPath" ])
+    (mkRenamedOptionModule [ "services" "buildkite-agent" "openssh" "publicKey" ]  [ "services" "buildkite-agent" "openssh" "publicKeyPath" ])
+  ];
 }
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 43fec5ff5bb2..2fa7c59a965d 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -28,6 +28,7 @@ let
 
   serverEnv = env //
     { HYDRA_TRACKER = cfg.tracker;
+      XDG_CACHE_HOME = "${baseDir}/www/.cache";
       COLUMNS = "80";
       PGPASSFILE = "${baseDir}/pgpass-www"; # grrr
     } // (optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
@@ -225,14 +226,14 @@ in
 
     services.hydra.extraConfig =
       ''
-        using_frontend_proxy 1
-        base_uri ${cfg.hydraURL}
-        notification_sender ${cfg.notificationSender}
-        max_servers 25
+        using_frontend_proxy = 1
+        base_uri = ${cfg.hydraURL}
+        notification_sender = ${cfg.notificationSender}
+        max_servers = 25
         ${optionalString (cfg.logo != null) ''
-          hydra_logo ${cfg.logo}
+          hydra_logo = ${cfg.logo}
         ''}
-        gc_roots_dir ${cfg.gcRootsDir}
+        gc_roots_dir = ${cfg.gcRootsDir}
         use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
       '';
 
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index 0dd59e4fb444..c2f4e9c0c5a7 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -145,6 +145,11 @@ in {
   };
 
   config = mkIf cfg.enable {
+    # server references the dejavu fonts
+    environment.systemPackages = [
+      pkgs.dejavu_fonts
+    ];
+
     users.extraGroups = optional (cfg.group == "jenkins") {
       name = "jenkins";
       gid = config.ids.gids.jenkins;
@@ -200,15 +205,17 @@ in {
           ${replacePlugins}
         '';
 
+      # For reference: https://wiki.jenkins.io/display/JENKINS/JenkinsLinuxStartupScript
       script = ''
         ${pkgs.jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOptions} -jar ${cfg.package}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \
                                                   --httpPort=${toString cfg.port} \
                                                   --prefix=${cfg.prefix} \
+                                                  -Djava.awt.headless=true \
                                                   ${concatStringsSep " " cfg.extraOptions}
       '';
 
       postStart = ''
-        until [[ $(${pkgs.curl.bin}/bin/curl -s --head -w '\n%{http_code}' http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix} | tail -n1) =~ ^(200|403)$ ]]; do
+        until [[ $(${pkgs.curl.bin}/bin/curl -L -s --head -w '\n%{http_code}' http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix} | tail -n1) =~ ^(200|403)$ ]]; do
           sleep 1
         done
       '';
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
index 906cb320df98..d528355671f6 100644
--- a/nixos/modules/services/databases/4store-endpoint.nix
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -2,7 +2,7 @@
 let
   cfg = config.services.fourStoreEndpoint;
   endpointUser = "fourstorehttp";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${endpointUser} -c";
 in
 with lib;
 {
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
index 62856822f906..abb62e1f2637 100644
--- a/nixos/modules/services/databases/4store.nix
+++ b/nixos/modules/services/databases/4store.nix
@@ -3,7 +3,7 @@ let
   cfg = config.services.fourStore;
   stateDir = "/var/lib/4store";
   fourStoreUser = "fourstore";
-  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}";
+  run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fourStoreUser}";
 in
 with lib;
 {
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
index c6875af506d3..46bc6fc5c132 100644
--- a/nixos/modules/services/databases/memcached.nix
+++ b/nixos/modules/services/databases/memcached.nix
@@ -40,11 +40,7 @@ in
         description = "The port to bind to";
       };
 
-      socket = mkOption {
-        default = "";
-        description = "Unix socket path to listen on. Setting this will disable network support";
-        example = "/var/run/memcached";
-      };
+      enableUnixSocket = mkEnableOption "unix socket at /run/memcached/memcached.sock";
 
       maxMemory = mkOption {
         default = 64;
@@ -68,31 +64,40 @@ in
 
   config = mkIf config.services.memcached.enable {
 
-    users.extraUsers.memcached =
-      { name = cfg.user;
-        uid = config.ids.uids.memcached;
-        description = "Memcached server user";
-      };
+    users.extraUsers = optional (cfg.user == "memcached") {
+      name = "memcached";
+      description = "Memcached server user";
+    };
 
     environment.systemPackages = [ memcached ];
 
-    systemd.services.memcached =
-      { description = "Memcached server";
-
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-
-        serviceConfig = {
-          ExecStart =
-            let
-              networking = if cfg.socket != ""
-                then "-s ${cfg.socket}"
-                else "-l ${cfg.listen} -p ${toString cfg.port}";
-            in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
-
-          User = cfg.user;
-        };
+    systemd.services.memcached = {
+      description = "Memcached server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        ExecStartPre = optionals cfg.enableUnixSocket [
+          "${pkgs.coreutils}/bin/install -d -o ${cfg.user} /run/memcached/"
+          "${pkgs.coreutils}/bin/chown -R ${cfg.user} /run/memcached/"
+        ];
+        ExecStart =
+        let
+          networking = if cfg.enableUnixSocket
+          then "-s /run/memcached/memcached.sock"
+          else "-l ${cfg.listen} -p ${toString cfg.port}";
+        in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
+
+        User = cfg.user;
       };
+    };
   };
+  imports = [
+    (mkRemovedOptionModule ["services" "memcached" "socket"] ''
+      This option was replaced by a fixed unix socket path at /run/memcached/memcached.sock enabled using services.memached.enableUnixSocket.
+    '')
+  ];
 
 }
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index a3bf4f9ba925..21a131b90a81 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -7,14 +7,12 @@ let
   cfg = config.services.mysql;
 
   mysql = cfg.package;
-  
-  isMariaDB = 
+
+  isMariaDB =
     let
       pName = _p: (builtins.parseDrvName (_p.name)).name;
     in pName mysql == pName pkgs.mariadb;
 
-  atLeast55 = versionAtLeast mysql.mysqlVersion "5.5";
-
   pidFile = "${cfg.pidDir}/mysqld.pid";
 
   mysqldOptions =
@@ -28,13 +26,6 @@ let
     ${optionalString (cfg.bind != null) "bind-address = ${cfg.bind}" }
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
-    ${optionalString (cfg.replication.role == "slave" && !atLeast55)
-    ''
-      master-host = ${cfg.replication.masterHost}
-      master-user = ${cfg.replication.masterUser}
-      master-password = ${cfg.replication.masterPassword}
-      master-port = ${toString cfg.replication.masterPort}
-    ''}
     ${optionalString (cfg.ensureUsers != [])
     ''
       plugin-load-add = auth_socket.so
@@ -142,7 +133,7 @@ in
         '';
         example = [
           "nextcloud"
-          "piwik"
+          "matomo"
         ];
       };
 
@@ -298,10 +289,10 @@ in
                     # Create initial databases
                     if ! test -e "${cfg.dataDir}/${database.name}"; then
                         echo "Creating initial database: ${database.name}"
-                        ( echo "create database ${database.name};"
+                        ( echo 'create database `${database.name}`;'
 
                           ${optionalString (database ? "schema") ''
-                          echo "use ${database.name};"
+                          echo 'use `${database.name}`;'
 
                           if [ -f "${database.schema}" ]
                           then
@@ -315,7 +306,7 @@ in
                     fi
                   '') cfg.initialDatabases}
 
-                ${optionalString (cfg.replication.role == "master" && atLeast55)
+                ${optionalString (cfg.replication.role == "master")
                   ''
                     # Set up the replication master
 
@@ -326,7 +317,7 @@ in
                     ) | ${mysql}/bin/mysql -u root -N
                   ''}
 
-                ${optionalString (cfg.replication.role == "slave" && atLeast55)
+                ${optionalString (cfg.replication.role == "slave")
                   ''
                     # Set up the replication slave
 
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index e884098cb08d..a67c61eb9949 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -7,8 +7,10 @@ let
   cfg = config.services.openldap;
   openldap = pkgs.openldap;
 
+  dataFile = pkgs.writeText "ldap-contents.ldif" cfg.declarativeContents;
   configFile = pkgs.writeText "slapd.conf" cfg.extraConfig;
-
+  configOpts = if cfg.configDir == null then "-f ${configFile}"
+               else "-F ${cfg.configDir}";
 in
 
 {
@@ -81,6 +83,34 @@ in
             '''
           '';
       };
+
+      declarativeContents = mkOption {
+        type = with types; nullOr lines;
+        default = null;
+        description = ''
+          Declarative contents for the LDAP database, in LDIF format.
+
+          Note a few facts when using it. First, the database
+          <emphasis>must</emphasis> be stored in the directory defined by
+          <code>dataDir</code>. Second, all <code>dataDir</code> will be erased
+          when starting the LDAP server. Third, modifications to the database
+          are not prevented, they are just dropped on the next reboot of the
+          server. Finally, performance-wise the database and indexes are rebuilt
+          on each server startup, so this will slow down server startup,
+          especially with large databases.
+        '';
+        example = ''
+          dn: dc=example,dc=org
+          objectClass: domain
+          dc: example
+
+          dn: ou=users,dc=example,dc=org
+          objectClass = organizationalUnit
+          ou: users
+
+          # ...
+        '';
+      };
     };
 
   };
@@ -88,7 +118,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.openldap.enable {
+  config = mkIf cfg.enable {
 
     environment.systemPackages = [ openldap ];
 
@@ -98,11 +128,21 @@ in
       after = [ "network.target" ];
       preStart = ''
         mkdir -p /var/run/slapd
-        chown -R ${cfg.user}:${cfg.group} /var/run/slapd
-        mkdir -p ${cfg.dataDir}
-        chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}
+        chown -R "${cfg.user}:${cfg.group}" /var/run/slapd
+        ${optionalString (cfg.declarativeContents != null) ''
+          rm -Rf "${cfg.dataDir}"
+        ''}
+        mkdir -p "${cfg.dataDir}"
+        ${optionalString (cfg.declarativeContents != null) ''
+          ${openldap.out}/bin/slapadd ${configOpts} -l ${dataFile}
+        ''}
+        chown -R "${cfg.user}:${cfg.group}" "${cfg.dataDir}"
       '';
-      serviceConfig.ExecStart = "${openldap.out}/libexec/slapd -u ${cfg.user} -g ${cfg.group} -d 0 -h \"${concatStringsSep " " cfg.urlList}\" ${if cfg.configDir == null then "-f "+configFile else "-F "+cfg.configDir}";
+      serviceConfig.ExecStart =
+        "${openldap.out}/libexec/slapd -d 0 " +
+          "-u '${cfg.user}' -g '${cfg.group}' " +
+          "-h '${concatStringsSep " " cfg.urlList}' " +
+          "${configOpts}";
     };
 
     users.extraUsers.openldap =
diff --git a/nixos/modules/services/databases/pgmanage.nix b/nixos/modules/services/databases/pgmanage.nix
index 86733a3e5a07..d1b48c06440e 100644
--- a/nixos/modules/services/databases/pgmanage.nix
+++ b/nixos/modules/services/databases/pgmanage.nix
@@ -22,7 +22,7 @@ let
 
       web_root = ${cfg.package}/etc/pgmanage/web_root
 
-      data_root = ${cfg.dataRoot}
+      sql_root = ${cfg.sqlRoot}
 
       ${optionalString (!isNull cfg.tls) ''
       tls_cert = ${cfg.tls.cert}
@@ -130,7 +130,7 @@ let
       '';
     };
 
-    dataRoot = mkOption {
+    sqlRoot = mkOption {
       type = types.str;
       default = "/var/lib/pgmanage";
       description = ''
@@ -210,7 +210,7 @@ in {
         users."${pgmanage}" = {
           name  = pgmanage;
           group = pgmanage;
-          home  = cfg.dataRoot;
+          home  = cfg.sqlRoot;
           createHome = true;
         };
         groups."${pgmanage}" = {
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 9b5e3735239f..f022e0863dfd 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -36,9 +36,6 @@ let
       ${cfg.extraConfig}
     '';
 
-  pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4";
-
-
 in
 
 {
@@ -122,7 +119,7 @@ in
       extraPlugins = mkOption {
         type = types.listOf types.path;
         default = [];
-        example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql94; }).v_2_1_4 ]";
+        example = literalExample "[ (pkgs.postgis.override { postgresql = pkgs.postgresql94; }) ]";
         description = ''
           When this list contains elements a new store path is created.
           PostgreSQL and the elements are symlinked into it. Then pg_config,
@@ -182,7 +179,7 @@ in
     services.postgresql.authentication = mkAfter
       ''
         # Generated file; do not edit!
-        local all all              ident ${optionalString pre84 "sameuser"}
+        local all all              ident
         host  all all 127.0.0.1/32 md5
         host  all all ::1/128      md5
       '';
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index a039ad138f6f..e4e38a4364a0 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -219,7 +219,6 @@ in
 
     users.extraUsers.redis =
       { name = cfg.user;
-        uid = config.ids.uids.redis;
         description = "Redis database user";
       };
 
diff --git a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
index 55ed2d9ee21b..cca98c43dc7a 100644
--- a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
+++ b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
@@ -28,14 +28,15 @@ with lib;
 
   ###### implementation
 
-  config = mkIf config.services.gnome3.at-spi2-core.enable {
-
-    environment.systemPackages = [ pkgs.at_spi2_core ];
-
-    services.dbus.packages = [ pkgs.at_spi2_core ];
-
-    systemd.packages = [ pkgs.at_spi2_core ];
-
-  };
-
+  config = mkMerge [
+    (mkIf config.services.gnome3.at-spi2-core.enable {
+      environment.systemPackages = [ pkgs.at-spi2-core ];
+      services.dbus.packages = [ pkgs.at-spi2-core ];
+      systemd.packages = [ pkgs.at-spi2-core ];
+    })
+
+    (mkIf (!config.services.gnome3.at-spi2-core.enable) {
+      environment.variables.NO_AT_BRIDGE = "1";
+    })
+  ];
 }
diff --git a/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
new file mode 100644
index 000000000000..2740a22c7ca0
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix
@@ -0,0 +1,27 @@
+# Chrome GNOME Shell native host connector.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.gnome3.chrome-gnome-shell.enable = mkEnableOption ''
+      Chrome GNOME Shell native host connector, a DBus service
+      allowing to install GNOME Shell extensions from a web browser.
+    '';
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.gnome3.chrome-gnome-shell.enable {
+    environment.etc = {
+      "chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+      "opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json";
+    };
+
+    environment.systemPackages = [ pkgs.chrome-gnome-shell ];
+
+    services.dbus.packages = [ pkgs.chrome-gnome-shell ];
+  };
+}
diff --git a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
index 86a47488d865..7e312a1b81eb 100644
--- a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
+++ b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
@@ -30,11 +30,11 @@ with lib;
 
   config = mkIf config.services.gnome3.evolution-data-server.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.evolution_data_server ];
+    environment.systemPackages = [ pkgs.gnome3.evolution-data-server ];
 
-    services.dbus.packages = [ pkgs.gnome3.evolution_data_server ];
+    services.dbus.packages = [ pkgs.gnome3.evolution-data-server ];
 
-    systemd.packages = [ pkgs.gnome3.evolution_data_server ];
+    systemd.packages = [ pkgs.gnome3.evolution-data-server ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-keyring.nix b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
index 2a68af5a7dd8..aa1165ab3bba 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
@@ -31,9 +31,9 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-keyring.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_keyring ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-keyring ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_keyring pkgs.gnome3.gcr ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-keyring pkgs.gnome3.gcr ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
index 0da4aca73ecb..4286251357f7 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
@@ -30,9 +30,9 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-online-accounts.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_online_accounts ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-online-accounts ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_online_accounts ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-online-accounts ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
index 3ac767bfa00d..fd14efee5f2e 100644
--- a/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
+++ b/nixos/modules/services/desktops/gnome3/gnome-terminal-server.nix
@@ -30,11 +30,11 @@ with lib;
 
   config = mkIf config.services.gnome3.gnome-terminal-server.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome_terminal ];
+    environment.systemPackages = [ pkgs.gnome3.gnome-terminal ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome_terminal ];
+    services.dbus.packages = [ pkgs.gnome3.gnome-terminal ];
 
-    systemd.packages = [ pkgs.gnome3.gnome_terminal ];
+    systemd.packages = [ pkgs.gnome3.gnome-terminal ];
 
   };
 
diff --git a/nixos/modules/services/desktops/gnome3/tracker-miners.nix b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
new file mode 100644
index 000000000000..20154fc2fed3
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/tracker-miners.nix
@@ -0,0 +1,41 @@
+# Tracker Miners daemons.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.tracker-miners = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Tracker miners, indexing services for Tracker
+          search engine and metadata storage system.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.tracker-miners.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.tracker-miners ];
+
+    services.dbus.packages = [ pkgs.gnome3.tracker-miners ];
+
+    systemd.packages = [ pkgs.gnome3.tracker-miners ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/pipewire.nix b/nixos/modules/services/desktops/pipewire.nix
new file mode 100644
index 000000000000..263a06156f84
--- /dev/null
+++ b/nixos/modules/services/desktops/pipewire.nix
@@ -0,0 +1,23 @@
+# pipewire service.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+  options = {
+    services.pipewire = {
+      enable = mkEnableOption "pipewire service";
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf config.services.pipewire.enable {
+    environment.systemPackages = [ pkgs.pipewire ];
+
+    systemd.packages = [ pkgs.pipewire ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ jtojnar ];
+}
diff --git a/nixos/modules/services/desktops/telepathy.nix b/nixos/modules/services/desktops/telepathy.nix
index 2554f3a1666f..f5401c180984 100644
--- a/nixos/modules/services/desktops/telepathy.nix
+++ b/nixos/modules/services/desktops/telepathy.nix
@@ -30,9 +30,9 @@ with lib;
 
   config = mkIf config.services.telepathy.enable {
 
-    environment.systemPackages = [ pkgs.telepathy_mission_control ];
+    environment.systemPackages = [ pkgs.telepathy-mission-control ];
 
-    services.dbus.packages = [ pkgs.telepathy_mission_control ];
+    services.dbus.packages = [ pkgs.telepathy-mission-control ];
 
   };
 
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 2c5a0c4849ef..ba7ec967919e 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -7,7 +7,7 @@ let
   cfg = config.services.emacs;
 
   editorScript = pkgs.writeScriptBin "emacseditor" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     if [ -z "$1" ]; then
       exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs
     else
@@ -15,6 +15,25 @@ let
     fi
   '';
 
+desktopApplicationFile = pkgs.writeTextFile {
+  name = "emacsclient.desktop";
+  destination = "/share/applications/emacsclient.desktop";
+  text = ''
+[Desktop Entry]
+Name=Emacsclient
+GenericName=Text Editor
+Comment=Edit text
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+Exec=emacseditor %F
+Icon=emacs
+Type=Application
+Terminal=false
+Categories=Development;TextEditor;
+StartupWMClass=Emacs
+Keywords=Text;Editor;
+'';
+};
+
 in {
 
   options.services.emacs = {
@@ -74,7 +93,7 @@ in {
       };
     } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; };
 
-    environment.systemPackages = [ cfg.package editorScript ];
+    environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ];
 
     environment.variables = {
       # This is required so that GTK applications launched from Emacs
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 1dc8ce93a0e5..3f6bf9de8931 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.factorio;
   factorio = pkgs.factorio-headless;
   name = "Factorio";
-  stateDir = "/var/lib/factorio";
+  stateDir = cfg.stateDir;
   mkSavePath = name: "${stateDir}/saves/${name}.zip";
   configFile = pkgs.writeText "factorio.conf" ''
     use-system-read-write-data-directories=true
@@ -25,7 +25,7 @@ let
     password = cfg.password;
     token = cfg.token;
     game_password = cfg.game-password;
-    require_user_verification = true;
+    require_user_verification = cfg.requireUserVerification;
     max_upload_in_kilobytes_per_second = 0;
     minimum_latency_in_ticks = 0;
     ignore_player_limit_for_returning_players = false;
@@ -80,6 +80,15 @@ in
           customizations.
         '';
       };
+      stateDir = mkOption {
+        type = types.path;
+        default = "/var/lib/factorio";
+        description = ''
+          The server's data directory.
+
+          The configuration and map will be stored here.
+        '';
+      };
       mods = mkOption {
         type = types.listOf types.package;
         default = [];
@@ -148,6 +157,13 @@ in
           Game password.
         '';
       };
+      requireUserVerification = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          When set to true, the server will only allow clients that have a valid factorio.com account.
+        '';
+      };
       autosave-interval = mkOption {
         type = types.nullOr types.int;
         default = null;
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
deleted file mode 100644
index 71ff6bb2f3f0..000000000000
--- a/nixos/modules/services/games/ghost-one.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib;
-let
-
-  cfg = config.services.ghostOne;
-  ghostUser = "ghostone";
-  stateDir = "/var/lib/ghost-one";
-
-in
-{
-
-  ###### interface
-
-  options = {
-    services.ghostOne = {
-
-      enable = mkOption {
-        default = false;
-        description = "Enable Ghost-One Warcraft3 game hosting server.";
-      };
-
-      language = mkOption {
-        default = "English";
-        type = types.enum [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
-        description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
-      };
-
-      war3path = mkOption {
-        default = "";
-        description = ''
-          The path to your local Warcraft III directory, which must contain war3.exe, storm.dll, and game.dll.
-        '';
-      };
-
-      mappath = mkOption {
-        default = "";
-        description = ''
-          The path to the directory where you keep your map files. GHost One doesn't require
-          map files but if it has access to them it can send them to players and automatically
-          calculate most map config values. GHost One will search [bot_mappath + map_localpath]
-          for the map file (map_localpath is set in each map's config file).
-        '';
-      };
-
-      config = mkOption {
-        default = "";
-        description = "Extra configuration options.";
-      };
-
-    };
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    users.extraUsers = singleton
-      { name = ghostUser;
-        uid = config.ids.uids.ghostone;
-        description = "Ghost One game server user";
-        home = stateDir;
-      };
-
-    users.extraGroups = singleton
-      { name = ghostUser;
-        gid = config.ids.gids.ghostone;
-      };
-
-    services.ghostOne.config = ''
-#      bot_log = /dev/stderr
-      bot_language = ${pkgs.ghostOne}/share/ghost-one/languages/${cfg.language}.cfg
-      bot_war3path = ${cfg.war3path}
-
-      bot_mapcfgpath = mapcfgs
-      bot_savegamepath = savegames
-      bot_mappath = ${cfg.mappath}
-      bot_replaypath = replays
-    '';
-
-    systemd.services."ghost-one" = {
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        mkdir -p ${stateDir}
-        cd ${stateDir}
-        chown ${ghostUser}:${ghostUser} .
-
-        mkdir -p mapcfgs
-        chown ${ghostUser}:${ghostUser} mapcfgs
-
-        mkdir -p replays
-        chown ${ghostUser}:${ghostUser} replays
-
-        mkdir -p savegames
-        chown ${ghostUser}:${ghostUser} savegames
-
-        ln -sf ${pkgs.writeText "ghost.cfg" cfg.config} ghost.cfg
-        ln -sf ${pkgs.ghostOne}/share/ghost-one/ip-to-country.csv
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${ghostUser} \
-          -c "LANG=C ${pkgs.ghostOne}/bin/ghost++"
-      '';
-    };
-
-  };
-
-}
diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix
index bb17c8859d84..0f05876aee32 100644
--- a/nixos/modules/services/hardware/acpid.nix
+++ b/nixos/modules/services/hardware/acpid.nix
@@ -31,7 +31,7 @@ let
           ''
             fn=$out/${name}
             echo "event=${handler.event}" > $fn
-            echo "action=${pkgs.writeScript "${name}.sh" (concatStringsSep "\n" [ "#! ${pkgs.bash}/bin/sh" handler.action ])}" >> $fn
+            echo "action=${pkgs.writeShellScriptBin "${name}.sh" handler.action }/bin/${name}.sh '%e'" >> $fn
           '';
         in concatStringsSep "\n" (mapAttrsToList f (canonicalHandlers // config.services.acpid.handlers))
       }
@@ -53,6 +53,12 @@ in
         description = "Whether to enable the ACPI daemon.";
       };
 
+      logEvents = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Log all event activity.";
+      };
+
       handlers = mkOption {
         type = types.attrsOf (types.submodule {
           options = {
@@ -69,11 +75,33 @@ in
           };
         });
 
-        description = "Event handlers.";
-        default = {};
-        example = { mute = { event = "button/mute.*"; action = "amixer set Master toggle"; }; };
-
+        description = ''
+          Event handlers.
 
+          <note><para>
+            Handler can be a single command.
+          </para></note>
+        '';
+        default = {};
+        example = {
+          ac-power = {
+            event = "ac_adapter/*";
+            action = ''
+              vals=($1)  # space separated string to array of multiple values
+              case ''${vals[3]} in
+                  00000000)
+                      echo unplugged >> /tmp/acpi.log
+                      ;;
+                  00000001)
+                      echo plugged in >> /tmp/acpi.log
+                      ;;
+                  *)
+                      echo unknown >> /tmp/acpi.log
+                      ;;
+              esac
+            '';
+          };
+        };
       };
 
       powerEventCommands = mkOption {
@@ -120,7 +148,7 @@ in
         ConditionPathExists = [ "/proc/acpi" ];
       };
 
-      script = "acpid --confdir ${acpiConfDir}";
+      script = "acpid ${optionalString config.services.acpid.logEvents "--logevents"} --confdir ${acpiConfDir}";
     };
 
   };
diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix
deleted file mode 100644
index b0f9ff56d1b2..000000000000
--- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix
+++ /dev/null
@@ -1,46 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-{
-
-  ###### interface
-
-  options = {
-
-    hardware.amdHybridGraphics.disable = lib.mkOption {
-      default = false;
-      type = lib.types.bool;
-      description = ''
-        Completely disable the AMD graphics card and use the
-        integrated graphics processor instead.
-      '';
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = lib.mkIf config.hardware.amdHybridGraphics.disable {
-    systemd.services."amd-hybrid-graphics" = {
-      path = [ pkgs.bash ];
-      description = "Disable AMD Card";
-      after = [ "sys-kernel-debug.mount" ];
-      before = [ "systemd-vconsole-setup.service" "display-manager.service" ];
-      requires = [ "sys-kernel-debug.mount" "vgaswitcheroo.path" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = "${pkgs.bash}/bin/sh -c 'echo -e \"IGD\\nOFF\" > /sys/kernel/debug/vgaswitcheroo/switch'";
-        ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch'";
-      };
-    };
-    systemd.paths."vgaswitcheroo" = {
-      pathConfig = {
-        PathExists = "/sys/kernel/debug/vgaswitcheroo/switch";
-        Unit = "amd-hybrid-graphics.service";
-      };
-      wantedBy = ["multi-user.target"];
-    };
-  };
-
-}
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index 4a8cd86b0b11..d7ca8a431794 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  bluez-bluetooth = pkgs.bluez;
   cfg = config.hardware.bluetooth;
+  bluez-bluetooth = cfg.package;
 
 in {
 
@@ -21,6 +21,16 @@ in {
         description = "Whether to power up the default Bluetooth controller on boot.";
       };
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.bluez;
+        defaultText = "pkgs.bluez";
+        example = "pkgs.bluez.override { enableMidi = true; }";
+        description = ''
+          Which BlueZ package to use.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
new file mode 100644
index 000000000000..d8abde2a600a
--- /dev/null
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -0,0 +1,90 @@
+# fwupd daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.fwupd;
+  originalEtc =
+    let
+      mkEtcFile = n: nameValuePair n { source = "${pkgs.fwupd}/etc/${n}"; };
+    in listToAttrs (map mkEtcFile pkgs.fwupd.filesInstalledToEtc);
+  extraTrustedKeys =
+    let
+      mkName = p: "pki/fwupd/${baseNameOf (toString p)}";
+      mkEtcFile = p: nameValuePair (mkName p) { source = p; };
+    in listToAttrs (map mkEtcFile cfg.extraTrustedKeys);
+in {
+
+  ###### interface
+  options = {
+    services.fwupd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable fwupd, a DBus service that allows
+          applications to update firmware.
+        '';
+      };
+
+      blacklistDevices = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
+        description = ''
+          Allow blacklisting specific devices by their GUID
+        '';
+      };
+
+      blacklistPlugins = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "udev" ];
+        description = ''
+          Allow blacklisting specific plugins
+        '';
+      };
+
+      extraTrustedKeys = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        example = literalExample "[ /etc/nixos/fwupd/myfirmware.pem ]";
+        description = ''
+          Installing a public key allows firmware signed with a matching private key to be recognized as trusted, which may require less authentication to install than for untrusted files. By default trusted firmware can be upgraded (but not downgraded) without the user or administrator password. Only very few keys are installed by default.
+        '';
+      };
+    };
+  };
+
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.fwupd ];
+
+    environment.etc = {
+      "fwupd/daemon.conf" = {
+        source = pkgs.writeText "daemon.conf" ''
+          [fwupd]
+          BlacklistDevices=${lib.concatStringsSep ";" cfg.blacklistDevices}
+          BlacklistPlugins=${lib.concatStringsSep ";" cfg.blacklistPlugins}
+        '';
+      };
+    } // originalEtc // extraTrustedKeys;
+
+    services.dbus.packages = [ pkgs.fwupd ];
+
+    services.udev.packages = [ pkgs.fwupd ];
+
+    systemd.packages = [ pkgs.fwupd ];
+
+    systemd.tmpfiles.rules = [
+      "d /var/lib/fwupd 0755 root root -"
+    ];
+  };
+
+  meta = {
+    maintainers = pkgs.fwupd.maintainers;
+  };
+}
diff --git a/nixos/modules/services/hardware/nvidia-optimus.nix b/nixos/modules/services/hardware/nvidia-optimus.nix
index 9fe4021c4247..eb1713baa140 100644
--- a/nixos/modules/services/hardware/nvidia-optimus.nix
+++ b/nixos/modules/services/hardware/nvidia-optimus.nix
@@ -23,7 +23,7 @@ let kernel = config.boot.kernelPackages; in
   ###### implementation
 
   config = lib.mkIf config.hardware.nvidiaOptimus.disable {
-    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb"];
+    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm"];
     boot.kernelModules = [ "bbswitch" ];
     boot.extraModulePackages = [ kernel.bbswitch ];
 
diff --git a/nixos/modules/services/hardware/thinkfan.nix b/nixos/modules/services/hardware/thinkfan.nix
index 018e82e58a3d..5a898631e090 100644
--- a/nixos/modules/services/hardware/thinkfan.nix
+++ b/nixos/modules/services/hardware/thinkfan.nix
@@ -55,7 +55,7 @@ in {
       enable = mkOption {
         default = false;
         description = ''
-          Whether to enable thinkfan, fan controller for ibm/lenovo thinkpads.
+          Whether to enable thinkfan, fan controller for IBM/Lenovo ThinkPads.
         '';
       };
 
diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix
index 38d0a3a1d752..fa0496114684 100644
--- a/nixos/modules/services/hardware/trezord.nix
+++ b/nixos/modules/services/hardware/trezord.nix
@@ -38,7 +38,7 @@ in {
       path = [];
       serviceConfig = {
         Type = "simple";
-        ExecStart = "${pkgs.trezord}/bin/trezord -f";
+        ExecStart = "${pkgs.trezord}/bin/trezord-go";
         User = "trezord";
       };
     };
diff --git a/nixos/modules/services/hardware/u2f.nix b/nixos/modules/services/hardware/u2f.nix
new file mode 100644
index 000000000000..bb4b2f05f890
--- /dev/null
+++ b/nixos/modules/services/hardware/u2f.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.u2f;
+in {
+  options = {
+    hardware.u2f = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable U2F hardware support.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.libu2f-host ];
+  };
+}
+
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 9f42f9e59ad5..7bfc3bb64872 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -146,7 +146,7 @@ let
 
       echo "Generating hwdb database..."
       # hwdb --update doesn't return error code even on errors!
-      res="$(${udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
+      res="$(${pkgs.buildPackages.udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)"
       echo "$res"
       [ -z "$(echo "$res" | egrep '^Error')" ]
       mv etc/udev/hwdb.bin $out
diff --git a/nixos/modules/services/hardware/usbmuxd.nix b/nixos/modules/services/hardware/usbmuxd.nix
new file mode 100644
index 000000000000..7ebd49fa01c2
--- /dev/null
+++ b/nixos/modules/services/hardware/usbmuxd.nix
@@ -0,0 +1,74 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  defaultUserGroup = "usbmux";
+  apple = "05ac";
+
+  cfg = config.services.usbmuxd;
+
+in
+
+{
+  options.services.usbmuxd = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable the usbmuxd ("USB multiplexing daemon") service. This daemon is
+        in charge of multiplexing connections over USB to an iOS device. This is
+        needed for transferring data from and to iOS devices (see ifuse). Also
+        this may enable plug-n-play tethering for iPhones.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUserGroup;
+      description = ''
+        The user usbmuxd should use to run after startup.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = defaultUserGroup;
+      description = ''
+        The group usbmuxd should use to run after startup.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = optional (cfg.user == defaultUserGroup) {
+      name = cfg.user;
+      description = "usbmuxd user";
+      group = cfg.group;
+    };
+
+    users.extraGroups = optional (cfg.group == defaultUserGroup) {
+      name = cfg.group;
+    };
+
+    # Give usbmuxd permission for Apple devices
+    services.udev.extraRules = ''
+      SUBSYSTEM=="usb", ATTR{idVendor}=="${apple}", GROUP="${cfg.group}"
+    '';
+
+    systemd.services.usbmuxd = {
+      description = "usbmuxd";
+      wantedBy = [ "multi-user.target" ];
+      unitConfig.Documentation = "man:usbmuxd(8)";
+      serviceConfig = {
+        # Trigger the udev rule manually. This doesn't require replugging the
+        # device when first enabling the option to get it to work
+        ExecStartPre = "${pkgs.libudev}/bin/udevadm trigger -s usb -a idVendor=${apple}";
+        ExecStart = "${pkgs.usbmuxd}/bin/usbmuxd -U ${cfg.user} -f";
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index a0dc0d6d089d..95f31829882f 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -141,7 +141,7 @@ in
         JAVA_HOME = jre;
         GRAYLOG_CONF = "${confFile}";
       };
-      path = [ pkgs.openjdk8 pkgs.which pkgs.procps ];
+      path = [ pkgs.jre_headless pkgs.which pkgs.procps ];
       preStart = ''
         mkdir -p /var/lib/graylog -m 755
 
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 2a8ac414720b..a4cab0c94cdc 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -8,7 +8,7 @@ let
   defaultRules = pkgs.runCommand "logcheck-default-rules" {} ''
                    cp -prd ${pkgs.logcheck}/etc/logcheck $out
                    chmod u+w $out
-                   rm $out/logcheck.*
+                   rm -r $out/logcheck.*
                  '';
 
   rulesDir = pkgs.symlinkJoin
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index b4abd2cd7e5e..28d89a7463ab 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -103,7 +103,7 @@ in
 
       listenAddress = mkOption {
         type = types.str;
-        default = "0.0.0.0";
+        default = "127.0.0.1";
         description = "Address on which to start webserver.";
       };
 
diff --git a/nixos/modules/services/mail/clamsmtp.nix b/nixos/modules/services/mail/clamsmtp.nix
new file mode 100644
index 000000000000..8f4f39aa7288
--- /dev/null
+++ b/nixos/modules/services/mail/clamsmtp.nix
@@ -0,0 +1,179 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.clamsmtp;
+  clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix
+in
+{
+  ##### interface
+  options = {
+    services.clamsmtp = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable clamsmtp.";
+      };
+
+      instances = mkOption {
+        description = "Instances of clamsmtp to run.";
+        type = types.listOf (types.submodule { options = {
+          action = mkOption {
+            type = types.enum [ "bounce" "drop" "pass" ];
+            default = "drop";
+            description =
+              ''
+                Action to take when a virus is detected.
+
+                Note that viruses often spoof sender addresses, so bouncing is
+                in most cases not a good idea.
+              '';
+          };
+
+          header = mkOption {
+            type = types.str;
+            default = "";
+            example = "X-Virus-Scanned: ClamAV using ClamSMTP";
+            description =
+              ''
+                A header to add to scanned messages. See clamsmtpd.conf(5) for
+                more details. Empty means no header.
+              '';
+          };
+
+          keepAlives = mkOption {
+            type = types.int;
+            default = 0;
+            description =
+              ''
+                Number of seconds to wait between each NOOP sent to the sending
+                server. 0 to disable.
+
+                This is meant for slow servers where the sending MTA times out
+                waiting for clamd to scan the file.
+              '';
+          };
+
+          listen = mkOption {
+            type = types.str;
+            example = "127.0.0.1:10025";
+            description =
+              ''
+                Address to wait for incoming SMTP connections on. See
+                clamsmtpd.conf(5) for more details.
+              '';
+          };
+
+          quarantine = mkOption {
+            type = types.bool;
+            default = false;
+            description =
+              ''
+                Whether to quarantine files that contain viruses by leaving them
+                in the temporary directory.
+              '';
+          };
+
+          maxConnections = mkOption {
+            type = types.int;
+            default = 64;
+            description = "Maximum number of connections to accept at once.";
+          };
+
+          outAddress = mkOption {
+            type = types.str;
+            description =
+              ''
+                Address of the SMTP server to send email to once it has been
+                scanned.
+              '';
+          };
+
+          tempDirectory = mkOption {
+            type = types.str;
+            default = "/tmp";
+            description =
+              ''
+                Temporary directory that needs to be accessible to both clamd
+                and clamsmtpd.
+              '';
+          };
+
+          timeout = mkOption {
+            type = types.int;
+            default = 180;
+            description = "Time-out for network connections.";
+          };
+
+          transparentProxy = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Enable clamsmtp's transparent proxy support.";
+          };
+
+          virusAction = mkOption {
+            type = with types; nullOr path;
+            default = null;
+            description =
+              ''
+                Command to run when a virus is found. Please see VIRUS ACTION in
+                clamsmtpd(8) for a discussion of this option and its safe use.
+              '';
+          };
+
+          xClient = mkOption {
+            type = types.bool;
+            default = false;
+            description =
+              ''
+                Send the XCLIENT command to the receiving server, for forwarding
+                client addresses and connection information if the receiving
+                server supports this feature.
+              '';
+          };
+        };});
+      };
+    };
+  };
+
+  ##### implementation
+  config = let
+    configfile = conf: pkgs.writeText "clamsmtpd.conf"
+      ''
+        Action: ${conf.action}
+        ClamAddress: ${clamdSocket}
+        Header: ${conf.header}
+        KeepAlives: ${toString conf.keepAlives}
+        Listen: ${conf.listen}
+        Quarantine: ${if conf.quarantine then "on" else "off"}
+        MaxConnections: ${toString conf.maxConnections}
+        OutAddress: ${conf.outAddress}
+        TempDirectory: ${conf.tempDirectory}
+        TimeOut: ${toString conf.timeout}
+        TransparentProxy: ${if conf.transparentProxy then "on" else "off"}
+        User: clamav
+        ${optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"}
+        XClient: ${if conf.xClient then "on" else "off"}
+      '';
+  in
+    mkIf cfg.enable {
+      assertions = [
+        { assertion = config.services.clamav.daemon.enable;
+          message = "clamsmtp requires clamav to be enabled";
+        }
+      ];
+
+      systemd.services = listToAttrs (imap1 (i: conf:
+        nameValuePair "clamsmtp-${toString i}" {
+          description = "ClamSMTP instance ${toString i}";
+          wantedBy = [ "multi-user.target" ];
+          script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}";
+          after = [ "clamav-daemon.service" ];
+          requires = [ "clamav-daemon.service" ];
+          serviceConfig.Type = "forking";
+          serviceConfig.PrivateTmp = "yes";
+          unitConfig.JoinsNamespaceOf = "clamav-daemon.service";
+        }
+      ) cfg.instances);
+    };
+}
diff --git a/nixos/modules/services/mail/dkimproxy-out.nix b/nixos/modules/services/mail/dkimproxy-out.nix
new file mode 100644
index 000000000000..894b88e25c1b
--- /dev/null
+++ b/nixos/modules/services/mail/dkimproxy-out.nix
@@ -0,0 +1,118 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.dkimproxy-out;
+  keydir = "/var/lib/dkimproxy-out";
+  privkey = "${keydir}/private.key";
+  pubkey = "${keydir}/public.key";
+in
+{
+  ##### interface
+  options = {
+    services.dkimproxy-out = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            Whether to enable dkimproxy_out.
+
+            Note that a key will be auto-generated, and can be found in
+            ${keydir}.
+          '';
+      };
+
+      listen = mkOption {
+        type = types.str;
+        example = "127.0.0.1:10027";
+        description = "Address:port DKIMproxy should listen on.";
+      };
+
+      relay = mkOption {
+        type = types.str;
+        example = "127.0.0.1:10028";
+        description = "Address:port DKIMproxy should forward mail to.";
+      };
+
+      domains = mkOption {
+        type = with types; listOf str;
+        example = [ "example.org" "example.com" ];
+        description = "List of domains DKIMproxy can sign for.";
+      };
+
+      selector = mkOption {
+        type = types.str;
+        example = "selector1";
+        description =
+          ''
+            The selector to use for DKIM key identification.
+
+            For example, if 'selector1' is used here, then for each domain
+            'example.org' given in `domain`, 'selector1._domainkey.example.org'
+            should contain the TXT record indicating the public key is the one
+            in ${pubkey}: "v=DKIM1; t=s; p=[THE PUBLIC KEY]".
+          '';
+      };
+
+      keySize = mkOption {
+        type = types.int;
+        default = 2048;
+        description =
+          ''
+            Size of the RSA key to use to sign outgoing emails. Note that the
+            maximum mandatorily verified as per RFC6376 is 2048.
+          '';
+      };
+
+      # TODO: allow signature for other schemes than dkim(c=relaxed/relaxed)?
+      # This being the scheme used by gmail, maybe nothing more is needed for
+      # reasonable use.
+    };
+  };
+
+  ##### implementation
+  config = let
+    configfile = pkgs.writeText "dkimproxy_out.conf"
+      ''
+        listen ${cfg.listen}
+        relay ${cfg.relay}
+
+        domain ${concatStringsSep "," cfg.domains}
+        selector ${cfg.selector}
+
+        signature dkim(c=relaxed/relaxed)
+
+        keyfile ${privkey}
+      '';
+  in
+    mkIf cfg.enable {
+      users.groups.dkimproxy-out = {};
+      users.users.dkimproxy-out = {
+        description = "DKIMproxy_out daemon";
+        group = "dkimproxy-out";
+        isSystemUser = true;
+      };
+
+      systemd.services.dkimproxy-out = {
+        description = "DKIMproxy_out";
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          if [ ! -d "${keydir}" ]; then
+            mkdir -p "${keydir}"
+            chmod 0700 "${keydir}"
+            ${pkgs.openssl}/bin/openssl genrsa -out "${privkey}" ${toString cfg.keySize}
+            ${pkgs.openssl}/bin/openssl rsa -in "${privkey}" -pubout -out "${pubkey}"
+            chown -R dkimproxy-out:dkimproxy-out "${keydir}"
+          fi
+        '';
+        script = ''
+          exec ${pkgs.dkimproxy}/bin/dkimproxy.out --conf_file=${configfile}
+        '';
+        serviceConfig = {
+          User = "dkimproxy-out";
+          PermissionsStartOnly = true;
+        };
+      };
+    };
+}
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 18101a312254..543e732127a5 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -30,6 +30,7 @@ let
 
     ''
       default_internal_user = ${cfg.user}
+      default_internal_group = ${cfg.group}
       ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"}
       ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"}
 
@@ -104,7 +105,7 @@ let
   };
 
   mailboxConfig = mailbox: ''
-    mailbox ${mailbox.name} {
+    mailbox "${mailbox.name}" {
       auto = ${toString mailbox.auto}
   '' + optionalString (mailbox.specialUse != null) ''
       special_use = \${toString mailbox.specialUse}
@@ -113,7 +114,7 @@ let
   mailboxes = { lib, pkgs, ... }: {
     options = {
       name = mkOption {
-        type = types.str;
+        type = types.strMatching ''[^"]+'';
         example = "Spam";
         description = "The name of the mailbox.";
       };
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 867c0ea6761c..5ab331ac067f 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -15,20 +15,18 @@ let
   haveVirtual = cfg.virtual != "";
 
   clientAccess =
-    if (cfg.dnsBlacklistOverrides != "")
-    then [ "check_client_access hash:/etc/postfix/client_access" ]
-    else [];
+    optional (cfg.dnsBlacklistOverrides != "")
+      "check_client_access hash:/etc/postfix/client_access";
 
   dnsBl =
-    if (cfg.dnsBlacklists != [])
-    then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ]
-    else [];
+    optionals (cfg.dnsBlacklists != [])
+      (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists);
 
   clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
 
   mainCf = let
     escape = replaceStrings ["$"] ["$$"];
-    mkList = items: "\n  " + concatStringsSep "\n  " items;
+    mkList = items: "\n  " + concatStringsSep ",\n  " items;
     mkVal = value:
       if isList value then mkList value
         else " " + (if value == true then "yes"
@@ -36,72 +34,9 @@ let
         else toString value);
     mkEntry = name: value: "${escape name} =${mkVal value}";
   in
-    concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config))
+    concatStringsSep "\n" (mapAttrsToList mkEntry cfg.config)
       + "\n" + cfg.extraConfig;
 
-  defaultConf = {
-    compatibility_level  = "9999";
-    mail_owner           = user;
-    default_privs        = "nobody";
-
-    # NixOS specific locations
-    data_directory       = "/var/lib/postfix/data";
-    queue_directory      = "/var/lib/postfix/queue";
-
-    # Default location of everything in package
-    meta_directory       = "${pkgs.postfix}/etc/postfix";
-    command_directory    = "${pkgs.postfix}/bin";
-    sample_directory     = "/etc/postfix";
-    newaliases_path      = "${pkgs.postfix}/bin/newaliases";
-    mailq_path           = "${pkgs.postfix}/bin/mailq";
-    readme_directory     = false;
-    sendmail_path        = "${pkgs.postfix}/bin/sendmail";
-    daemon_directory     = "${pkgs.postfix}/libexec/postfix";
-    manpage_directory    = "${pkgs.postfix}/share/man";
-    html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
-    shlib_directory      = false;
-    relayhost            = if cfg.relayHost == "" then "" else
-                             if cfg.lookupMX
-                             then "${cfg.relayHost}:${toString cfg.relayPort}"
-                             else "[${cfg.relayHost}]:${toString cfg.relayPort}";
-
-    mail_spool_directory = "/var/spool/mail/";
-    setgid_group         = setgidGroup;
-  }
-  // optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; }
-  // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
-  // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
-  // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
-  // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
-  // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
-  // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
-  // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
-  // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
-  // optionalAttrs haveAliases { alias_maps = "${cfg.aliasMapType}:/etc/postfix/aliases"; }
-  // optionalAttrs haveTransport { transport_maps = "hash:/etc/postfix/transport"; }
-  // optionalAttrs haveVirtual { virtual_alias_maps = "${cfg.virtualMapType}:/etc/postfix/virtual"; }
-  // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
-  // optionalAttrs cfg.useSrs {
-    sender_canonical_maps = "tcp:127.0.0.1:10001";
-    sender_canonical_classes = "envelope_sender";
-    recipient_canonical_maps = "tcp:127.0.0.1:10002";
-    recipient_canonical_classes= "envelope_recipient";
-  }
-  // optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; }
-  // optionalAttrs (cfg.sslCert != "") {
-    smtp_tls_CAfile = cfg.sslCACert;
-    smtp_tls_cert_file = cfg.sslCert;
-    smtp_tls_key_file = cfg.sslKey;
-
-    smtp_use_tls = true;
-
-    smtpd_tls_CAfile = cfg.sslCACert;
-    smtpd_tls_cert_file = cfg.sslCert;
-    smtpd_tls_key_file = cfg.sslKey;
-
-    smtpd_use_tls = true;
-  };
-
   masterCfOptions = { options, config, name, ... }: {
     options = {
       name = mkOption {
@@ -479,7 +414,10 @@ in
       postmasterAlias = mkOption {
         type = types.str;
         default = "root";
-        description = "Who should receive postmaster e-mail.";
+        description = "
+          Who should receive postmaster e-mail. Multiple values can be added by
+          separating values with comma.
+        ";
       };
 
       rootAlias = mkOption {
@@ -487,6 +425,7 @@ in
         default = "";
         description = "
           Who should receive root e-mail. Blank for no redirection.
+          Multiple values can be added by separating values with comma.
         ";
       };
 
@@ -507,7 +446,6 @@ in
 
       config = mkOption {
         type = with types; attrsOf (either bool (either str (listOf str)));
-        default = defaultConf;
         description = ''
           The main.cf configuration file as key value set.
         '';
@@ -749,6 +687,67 @@ in
           '';
         };
 
+      services.postfix.config = (mapAttrs (_: v: mkDefault v) {
+        compatibility_level  = "9999";
+        mail_owner           = cfg.user;
+        default_privs        = "nobody";
+
+        # NixOS specific locations
+        data_directory       = "/var/lib/postfix/data";
+        queue_directory      = "/var/lib/postfix/queue";
+
+        # Default location of everything in package
+        meta_directory       = "${pkgs.postfix}/etc/postfix";
+        command_directory    = "${pkgs.postfix}/bin";
+        sample_directory     = "/etc/postfix";
+        newaliases_path      = "${pkgs.postfix}/bin/newaliases";
+        mailq_path           = "${pkgs.postfix}/bin/mailq";
+        readme_directory     = false;
+        sendmail_path        = "${pkgs.postfix}/bin/sendmail";
+        daemon_directory     = "${pkgs.postfix}/libexec/postfix";
+        manpage_directory    = "${pkgs.postfix}/share/man";
+        html_directory       = "${pkgs.postfix}/share/postfix/doc/html";
+        shlib_directory      = false;
+        mail_spool_directory = "/var/spool/mail/";
+        setgid_group         = cfg.setgidGroup;
+      })
+      // optionalAttrs (cfg.relayHost != "") { relayhost = if cfg.lookupMX
+                                                           then "${cfg.relayHost}:${toString cfg.relayPort}"
+                                                           else "[${cfg.relayHost}]:${toString cfg.relayPort}"; }
+      // optionalAttrs config.networking.enableIPv6 { inet_protocols = mkDefault "all"; }
+      // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
+      // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
+      // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
+      // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
+      // optionalAttrs (cfg.origin != "") { myorigin =  cfg.origin; }
+      // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
+      // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
+      // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
+      // optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; }
+      // optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; }
+      // optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; }
+      // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
+      // optionalAttrs cfg.useSrs {
+        sender_canonical_maps = [ "tcp:127.0.0.1:10001" ];
+        sender_canonical_classes = [ "envelope_sender" ];
+        recipient_canonical_maps = [ "tcp:127.0.0.1:10002" ];
+        recipient_canonical_classes = [ "envelope_recipient" ];
+      }
+      // optionalAttrs cfg.enableHeaderChecks { header_checks = [ "regexp:/etc/postfix/header_checks" ]; }
+      // optionalAttrs (cfg.sslCert != "") {
+        smtp_tls_CAfile = cfg.sslCACert;
+        smtp_tls_cert_file = cfg.sslCert;
+        smtp_tls_key_file = cfg.sslKey;
+
+        smtp_use_tls = true;
+
+        smtpd_tls_CAfile = cfg.sslCACert;
+        smtpd_tls_cert_file = cfg.sslCert;
+        smtpd_tls_key_file = cfg.sslKey;
+
+        smtpd_use_tls = true;
+      };
+
       services.postfix.masterConfig = {
         smtp_inet = {
           name = "smtp";
diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index 6d403e448e04..09fb587e74b5 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -1,14 +1,152 @@
-{ config, lib, pkgs, ... }:
+{ config, options, pkgs, lib, ... }:
 
 with lib;
 
 let
 
   cfg = config.services.rspamd;
+  opts = options.services.rspamd;
 
-  mkBindSockets = socks: concatStringsSep "\n" (map (each: "  bind_socket = \"${each}\"") socks);
+  bindSocketOpts = {options, config, ... }: {
+    options = {
+      socket = mkOption {
+        type = types.str;
+        example = "localhost:11333";
+        description = ''
+          Socket for this worker to listen on in a format acceptable by rspamd.
+        '';
+      };
+      mode = mkOption {
+        type = types.str;
+        default = "0644";
+        description = "Mode to set on unix socket";
+      };
+      owner = mkOption {
+        type = types.str;
+        default = "${cfg.user}";
+        description = "Owner to set on unix socket";
+      };
+      group = mkOption {
+        type = types.str;
+        default = "${cfg.group}";
+        description = "Group to set on unix socket";
+      };
+      rawEntry = mkOption {
+        type = types.str;
+        internal = true;
+      };
+    };
+    config.rawEntry = let
+      maybeOption = option:
+        optionalString options.${option}.isDefined " ${option}=${config.${option}}";
+    in
+      if (!(hasPrefix "/" config.socket)) then "${config.socket}"
+      else "${config.socket}${maybeOption "mode"}${maybeOption "owner"}${maybeOption "group"}";
+  };
 
-   rspamdConfFile = pkgs.writeText "rspamd.conf"
+  workerOpts = { name, ... }: {
+    options = {
+      enable = mkOption {
+        type = types.nullOr types.bool;
+        default = null;
+        description = "Whether to run the rspamd worker.";
+      };
+      name = mkOption {
+        type = types.nullOr types.str;
+        default = name;
+        description = "Name of the worker";
+      };
+      type = mkOption {
+        type = types.nullOr (types.enum [
+          "normal" "controller" "fuzzy_storage" "proxy" "lua"
+        ]);
+        description = "The type of this worker";
+      };
+      bindSockets = mkOption {
+        type = types.listOf (types.either types.str (types.submodule bindSocketOpts));
+        default = [];
+        description = ''
+          List of sockets to listen, in format acceptable by rspamd
+        '';
+        example = [{
+          socket = "/run/rspamd.sock";
+          mode = "0666";
+          owner = "rspamd";
+        } "*:11333"];
+        apply = value: map (each: if (isString each)
+          then if (isUnixSocket each)
+            then {socket = each; owner = cfg.user; group = cfg.group; mode = "0644"; rawEntry = "${each}";}
+            else {socket = each; rawEntry = "${each}";}
+          else each) value;
+      };
+      count = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Number of worker instances to run
+        '';
+      };
+      includes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          List of files to include in configuration
+        '';
+      };
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Additional entries to put verbatim into worker section of rspamd config file.";
+      };
+    };
+    config = mkIf (name == "normal" || name == "controller" || name == "fuzzy") {
+      type = mkDefault name;
+      includes = mkDefault [ "$CONFDIR/worker-${name}.inc" ];
+      bindSockets = mkDefault (if name == "normal"
+        then [{
+              socket = "/run/rspamd/rspamd.sock";
+              mode = "0660";
+              owner = cfg.user;
+              group = cfg.group;
+            }]
+        else if name == "controller"
+        then [ "localhost:11334" ]
+        else [] );
+    };
+  };
+
+  indexOf = default: start: list: e:
+    if list == []
+    then default
+    else if (head list) == e then start
+    else (indexOf default (start + (length (listenStreams (head list).socket))) (tail list) e);
+
+  systemdSocket = indexOf (abort "Socket not found") 0 allSockets;
+
+  isUnixSocket = socket: hasPrefix "/" (if (isString socket) then socket else socket.socket);
+  isPort = hasPrefix "*:";
+  isIPv4Socket = hasPrefix "*v4:";
+  isIPv6Socket = hasPrefix "*v6:";
+  isLocalHost = hasPrefix "localhost:";
+  listenStreams = socket:
+    if (isLocalHost socket) then
+      let port = (removePrefix "localhost:" socket);
+      in [ "127.0.0.1:${port}" ] ++ (if config.networking.enableIPv6 then ["[::1]:${port}"] else [])
+    else if (isIPv6Socket socket) then [removePrefix "*v6:" socket]
+    else if (isPort socket) then [removePrefix "*:" socket]
+    else if (isIPv4Socket socket) then
+      throw "error: IPv4 only socket not supported in rspamd with socket activation"
+    else if (length (splitString " " socket)) != 1 then
+      throw "error: string options not supported in rspamd with socket activation"
+    else [socket];
+
+  mkBindSockets = enabled: socks: concatStringsSep "\n  " (flatten (map (each:
+    if cfg.socketActivation && enabled != false then
+      let systemd = (systemdSocket each);
+      in (imap (idx: e: "bind_socket = \"systemd:${toString (systemd + idx - 1)}\";") (listenStreams each.socket))
+    else "bind_socket = \"${each.rawEntry}\";") socks));
+
+  rspamdConfFile = pkgs.writeText "rspamd.conf"
     ''
       .include "$CONFDIR/common.conf"
 
@@ -22,17 +160,33 @@ let
         .include "$CONFDIR/logging.inc"
       }
 
-      worker {
-      ${mkBindSockets cfg.bindSocket}
-        .include "$CONFDIR/worker-normal.inc"
-      }
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+        worker ${optionalString (value.name != "normal" && value.name != "controller") "${value.name}"} {
+          type = "${value.type}";
+          ${optionalString (value.enable != null)
+            "enabled = ${if value.enable != false then "yes" else "no"};"}
+          ${mkBindSockets value.enable value.bindSockets}
+          ${optionalString (value.count != null) "count = ${toString value.count};"}
+          ${concatStringsSep "\n  " (map (each: ".include \"${each}\"") value.includes)}
+          ${value.extraConfig}
+        }
+      '') cfg.workers)}
 
-      worker {
-      ${mkBindSockets cfg.bindUISocket}
-        .include "$CONFDIR/worker-controller.inc"
-      }
+      ${cfg.extraConfig}
    '';
 
+  allMappedSockets = flatten (mapAttrsToList (name: value:
+    if value.enable != false
+    then imap (idx: each: {
+        name = "${name}";
+        index = idx;
+        value = each;
+      }) value.bindSockets
+    else []) cfg.workers);
+  allSockets = map (e: e.value) allMappedSockets;
+
+  allSocketNames = map (each: "rspamd-${each.name}-${toString each.index}.socket") allMappedSockets;
+
 in
 
 {
@@ -46,36 +200,52 @@ in
       enable = mkEnableOption "Whether to run the rspamd daemon.";
 
       debug = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to run the rspamd daemon in debug mode.";
       };
 
-      bindSocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ];
-        defaultText = ''[
-          "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}"
-        ]'';
+      socketActivation = mkOption {
+        type = types.bool;
         description = ''
-          List of sockets to listen, in format acceptable by rspamd
+          Enable systemd socket activation for rspamd.
         '';
-        example = ''
-          bindSocket = [
-            "/run/rspamd.sock mode=0666 owner=rspamd"
-            "*:11333"
-          ];
+      };
+
+      workers = mkOption {
+        type = with types; attrsOf (submodule workerOpts);
+        description = ''
+          Attribute set of workers to start.
+        '';
+        default = {
+          normal = {};
+          controller = {};
+        };
+        example = literalExample ''
+          {
+            normal = {
+              includes = [ "$CONFDIR/worker-normal.inc" ];
+              bindSockets = [{
+                socket = "/run/rspamd/rspamd.sock";
+                mode = "0660";
+                owner = "${cfg.user}";
+                group = "${cfg.group}";
+              }];
+            };
+            controller = {
+              includes = [ "$CONFDIR/worker-controller.inc" ];
+              bindSockets = [ "[::1]:11334" ];
+            };
+          }
         '';
       };
 
-      bindUISocket = mkOption {
-        type = types.listOf types.str;
-        default = [
-          "localhost:11334"
-        ];
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
         description = ''
-          List of sockets for web interface, in format acceptable by rspamd
+          Extra configuration to add at the end of the rspamd configuration
+          file.
         '';
       };
 
@@ -102,6 +272,13 @@ in
 
   config = mkIf cfg.enable {
 
+    services.rspamd.socketActivation = mkDefault (!opts.bindSocket.isDefined && !opts.bindUISocket.isDefined);
+
+    assertions = [ {
+      assertion = !cfg.socketActivation || !(opts.bindSocket.isDefined || opts.bindUISocket.isDefined);
+      message = "Can't use socketActivation for rspamd when using renamed bind socket options";
+    } ];
+
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
 
@@ -117,17 +294,22 @@ in
       gid = config.ids.gids.rspamd;
     };
 
+    environment.etc."rspamd.conf".source = rspamdConfFile;
+
     systemd.services.rspamd = {
       description = "Rspamd Service";
 
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
+      wantedBy = mkIf (!cfg.socketActivation) [ "multi-user.target" ];
+      after = [ "network.target" ] ++
+       (if cfg.socketActivation then allSocketNames else []);
+      requires = mkIf cfg.socketActivation allSocketNames;
 
       serviceConfig = {
         ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
         Restart = "always";
         RuntimeDirectory = "rspamd";
         PrivateTmp = true;
+        Sockets = mkIf cfg.socketActivation (concatStringsSep " " allSocketNames);
       };
 
       preStart = ''
@@ -135,5 +317,25 @@ in
         ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd
       '';
     };
+    systemd.sockets = mkIf cfg.socketActivation
+      (listToAttrs (map (each: {
+        name = "rspamd-${each.name}-${toString each.index}";
+        value = {
+          description = "Rspamd socket ${toString each.index} for worker ${each.name}";
+          wantedBy = [ "sockets.target" ];
+          listenStreams = (listenStreams each.value.socket);
+          socketConfig = {
+            BindIPv6Only = mkIf (isIPv6Socket each.value.socket) "ipv6-only";
+            Service = "rspamd.service";
+            SocketUser = mkIf (isUnixSocket each.value.socket) each.value.owner;
+            SocketGroup = mkIf (isUnixSocket each.value.socket) each.value.group;
+            SocketMode = mkIf (isUnixSocket each.value.socket) each.value.mode;
+          };
+        };
+      }) allMappedSockets));
   };
+  imports = [
+    (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ])
+    (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ])
+  ];
 }
diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb
index 84622622db70..0b58c59c7a51 100644
--- a/nixos/modules/services/misc/defaultUnicornConfig.rb
+++ b/nixos/modules/services/misc/defaultUnicornConfig.rb
@@ -1,205 +1,69 @@
-# The following was taken from github.com/crohr/syslogger and is BSD
-# licensed.
-require 'syslog'
-require 'logger'
-require 'thread'
+worker_processes 3
 
-class Syslogger
-
-  VERSION = "1.6.0"
-
-  attr_reader :level, :ident, :options, :facility, :max_octets
-  attr_accessor :formatter
-
-  MAPPING = {
-    Logger::DEBUG => Syslog::LOG_DEBUG,
-    Logger::INFO => Syslog::LOG_INFO,
-    Logger::WARN => Syslog::LOG_WARNING,
-    Logger::ERROR => Syslog::LOG_ERR,
-    Logger::FATAL => Syslog::LOG_CRIT,
-    Logger::UNKNOWN => Syslog::LOG_ALERT
-  }
-
-  #
-  # Initializes default options for the logger
-  # <tt>ident</tt>:: the name of your program [default=$0].
-  # <tt>options</tt>::  syslog options [default=<tt>Syslog::LOG_PID | Syslog::LOG_CONS</tt>].
-  #                     Correct values are:
-  #                       LOG_CONS    : writes the message on the console if an error occurs when sending the message;
-  #                       LOG_NDELAY  : no delay before sending the message;
-  #                       LOG_PERROR  : messages will also be written on STDERR;
-  #                       LOG_PID     : adds the process number to the message (just after the program name)
-  # <tt>facility</tt>:: the syslog facility [default=nil] Correct values include:
-  #                       Syslog::LOG_DAEMON
-  #                       Syslog::LOG_USER
-  #                       Syslog::LOG_SYSLOG
-  #                       Syslog::LOG_LOCAL2
-  #                       Syslog::LOG_NEWS
-  #                       etc.
-  #
-  # Usage:
-  #   logger = Syslogger.new("my_app", Syslog::LOG_PID | Syslog::LOG_CONS, Syslog::LOG_LOCAL0)
-  #   logger.level = Logger::INFO # use Logger levels
-  #   logger.warn "warning message"
-  #   logger.debug "debug message"
-  #
-  def initialize(ident = $0, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil)
-    @ident = ident
-    @options = options || (Syslog::LOG_PID | Syslog::LOG_CONS)
-    @facility = facility
-    @level = Logger::INFO
-    @mutex = Mutex.new
-    @formatter = Logger::Formatter.new
-  end
-
-  %w{debug info warn error fatal unknown}.each do |logger_method|
-    # Accepting *args as message could be nil.
-    #  Default params not supported in ruby 1.8.7
-    define_method logger_method.to_sym do |*args, &block|
-      return true if @level > Logger.const_get(logger_method.upcase)
-      message = args.first || block && block.call
-      add(Logger.const_get(logger_method.upcase), message)
-    end
-
-    unless logger_method == 'unknown'
-      define_method "#{logger_method}?".to_sym do
-        @level <= Logger.const_get(logger_method.upcase)
-      end
-    end
-  end
-
-  # Log a message at the Logger::INFO level. Useful for use with Rack::CommonLogger
-  def write(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Logs a message at the Logger::INFO level.
-  def <<(msg)
-    add(Logger::INFO, msg)
-  end
-
-  # Low level method to add a message.
-  # +severity+::  the level of the message. One of Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN
-  # +message+:: the message string.
-  #             If nil, the method will call the block and use the result as the message string.
-  #             If both are nil or no block is given, it will use the progname as per the behaviour of both the standard Ruby logger, and the Rails BufferedLogger.
-  # +progname+:: optionally, overwrite the program name that appears in the log message.
-  def add(severity, message = nil, progname = nil, &block)
-    if message.nil? && block.nil? && !progname.nil?
-      message, progname = progname, nil
-    end
-    progname ||= @ident
-
-    @mutex.synchronize do
-      Syslog.open(progname, @options, @facility) do |s|
-        s.mask = Syslog::LOG_UPTO(MAPPING[@level])
-        communication = clean(message || block && block.call)
-        if self.max_octets
-          buffer = "#{tags_text}"
-          communication.bytes do |byte|
-            buffer.concat(byte)
-            # if the last byte we added is potentially part of an escape, we'll go ahead and add another byte
-            if buffer.bytesize >= self.max_octets && !['%'.ord,'\\'.ord].include?(byte)
-              s.log(MAPPING[severity],buffer)
-              buffer = ""
-            end
-          end
-          s.log(MAPPING[severity],buffer) unless buffer.empty?
-        else
-          s.log(MAPPING[severity],"#{tags_text}#{communication}")
-        end
-      end
-    end
-  end
-
-  # Set the max octets of the messages written to the log
-  def max_octets=(max_octets)
-    @max_octets = max_octets
-  end
-
-  # Sets the minimum level for messages to be written in the log.
-  # +level+:: one of <tt>Logger::DEBUG</tt>, <tt>Logger::INFO</tt>, <tt>Logger::WARN</tt>, <tt>Logger::ERROR</tt>, <tt>Logger::FATAL</tt>, <tt>Logger::UNKNOWN</tt>
-  def level=(level)
-    level = Logger.const_get(level.to_s.upcase) if level.is_a?(Symbol)
-
-    unless level.is_a?(Fixnum)
-      raise ArgumentError.new("Invalid logger level `#{level.inspect}`")
-    end
-
-    @level = level
-  end
-
-  # Sets the ident string passed along to Syslog
-  def ident=(ident)
-    @ident = ident
-  end
-
-  # Tagging code borrowed from ActiveSupport gem
-  def tagged(*tags)
-    new_tags = push_tags(*tags)
-    yield self
-  ensure
-    pop_tags(new_tags.size)
-  end
-
-  def push_tags(*tags)
-    tags.flatten.reject{ |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags|
-      current_tags.concat new_tags
-    end
-  end
-
-  def pop_tags(size = 1)
-    current_tags.pop size
-  end
-
-  def clear_tags!
-    current_tags.clear
-  end
-
-  protected
-
-  # Borrowed from SyslogLogger.
-  def clean(message)
-    message = message.to_s.dup
-    message.strip! # remove whitespace
-    message.gsub!(/\n/, '\\n') # escape newlines
-    message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf)
-    message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes
-    message
-  end
-
-  private
-
-  def tags_text
-    tags = current_tags
-    if tags.any?
-      tags.collect { |tag| "[#{tag}] " }.join
-    end
-  end
-
-  def current_tags
-    Thread.current[:syslogger_tagged_logging_tags] ||= []
-  end
-end
+listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+listen "/run/gitlab/gitlab.socket", :backlog => 1024
 
-worker_processes 2
 working_directory ENV["GITLAB_PATH"]
-pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
-listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
+pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
 timeout 60
 
-logger Syslogger.new
-
+# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
+# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
 preload_app true
-
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true
 
 check_client_connection false
 
+before_fork do |server, worker|
+  # the following is highly recommended for Rails + "preload_app true"
+  # as there's no need for the master process to hold a connection
+  defined?(ActiveRecord::Base) and
+    ActiveRecord::Base.connection.disconnect!
+
+  # The following is only recommended for memory/DB-constrained
+  # installations.  It is not needed if your system can house
+  # twice as many worker_processes as you have configured.
+  #
+  # This allows a new master process to incrementally
+  # phase out the old master process with SIGTTOU to avoid a
+  # thundering herd (especially in the "preload_app false" case)
+  # when doing a transparent upgrade.  The last worker spawned
+  # will then kill off the old master process with a SIGQUIT.
+  old_pid = "#{server.config[:pid]}.oldbin"
+  if old_pid != server.pid
+    begin
+      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
+      Process.kill(sig, File.read(old_pid).to_i)
+    rescue Errno::ENOENT, Errno::ESRCH
+    end
+  end
+
+  # Throttle the master from forking too quickly by sleeping.  Due
+  # to the implementation of standard Unix signal handlers, this
+  # helps (but does not completely) prevent identical, repeated signals
+  # from being lost when the receiving process is busy.
+  # sleep 1
+end
+
 after_fork do |server, worker|
+  # per-process listener ports for debugging/admin/migrations
+  # addr = "127.0.0.1:#{9293 + worker.nr}"
+  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
+
+  # the following is *required* for Rails + "preload_app true",
   defined?(ActiveRecord::Base) and
     ActiveRecord::Base.establish_connection
+
+  # reset prometheus client, this will cause any opened metrics files to be closed
+  defined?(::Prometheus::Client.reinitialize_on_pid_change) &&
+    Prometheus::Client.reinitialize_on_pid_change
+
+  # if preload_app is true, then you may also want to check and
+  # restart any other shared sockets/descriptors such as Memcached,
+  # and Redis.  TokyoCabinet file handles are safe to reuse
+  # between any number of forked children (assuming your kernel
+  # correctly implements pread()/pwrite() system calls)
 end
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index e96645c79c77..e4517c636e88 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -32,11 +32,17 @@ in
         description = "Whether to enable Disnix";
       };
 
+      enableMultiUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether to support multi-user mode by enabling the Disnix D-Bus service";
+      };
+
       useWebServiceInterface = mkOption {
         default = false;
         description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
       };
-      
+
       package = mkOption {
         type = types.path;
         description = "The Disnix package";
@@ -51,8 +57,8 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    dysnomia.enable = true;
-    
+    services.dysnomia.enable = true;
+
     environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
     services.dbus.enable = true;
@@ -71,7 +77,7 @@ in
       };
 
     systemd.services = {
-      disnix = {
+      disnix = mkIf cfg.enableMultiUser {
         description = "Disnix server";
         wants = [ "dysnomia.target" ];
         wantedBy = [ "multi-user.target" ];
@@ -92,7 +98,7 @@ in
         }
         // (if config.environment.variables ? DYSNOMIA_CONTAINERS_PATH then { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; } else {})
         // (if config.environment.variables ? DYSNOMIA_MODULES_PATH then { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; } else {});
-        
+
         serviceConfig.ExecStart = "${cfg.package}/bin/disnix-service";
       };
 
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
index df44d0a54866..9e66e0811ab7 100644
--- a/nixos/modules/services/misc/dysnomia.nix
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -3,8 +3,8 @@
 with lib;
 
 let
-  cfg = config.dysnomia;
-  
+  cfg = config.services.dysnomia;
+
   printProperties = properties:
     concatMapStrings (propertyName:
       let
@@ -13,7 +13,7 @@ let
       if isList property then "${propertyName}=(${lib.concatMapStrings (elem: "\"${toString elem}\" ") (properties."${propertyName}")})\n"
       else "${propertyName}=\"${toString property}\"\n"
     ) (builtins.attrNames properties);
-  
+
   properties = pkgs.stdenv.mkDerivation {
     name = "dysnomia-properties";
     buildCommand = ''
@@ -22,13 +22,13 @@ let
       EOF
     '';
   };
-  
+
   containersDir = pkgs.stdenv.mkDerivation {
     name = "dysnomia-containers";
     buildCommand = ''
       mkdir -p $out
       cd $out
-      
+
       ${concatMapStrings (containerName:
         let
           containerProperties = cfg.containers."${containerName}";
@@ -42,11 +42,11 @@ let
       ) (builtins.attrNames cfg.containers)}
     '';
   };
-  
+
   linkMutableComponents = {containerName}:
     ''
       mkdir ${containerName}
-      
+
       ${concatMapStrings (componentName:
         let
           component = cfg.components."${containerName}"."${componentName}";
@@ -54,13 +54,13 @@ let
         "ln -s ${component} ${containerName}/${componentName}\n"
       ) (builtins.attrNames (cfg.components."${containerName}" or {}))}
     '';
-  
+
   componentsDir = pkgs.stdenv.mkDerivation {
     name = "dysnomia-components";
     buildCommand = ''
       mkdir -p $out
       cd $out
-      
+
       ${concatMapStrings (containerName:
         let
           components = cfg.components."${containerName}";
@@ -72,59 +72,59 @@ let
 in
 {
   options = {
-    dysnomia = {
-      
+    services.dysnomia = {
+
       enable = mkOption {
         type = types.bool;
         default = false;
         description = "Whether to enable Dysnomia";
       };
-      
+
       enableAuthentication = mkOption {
         type = types.bool;
         default = false;
         description = "Whether to publish privacy-sensitive authentication credentials";
       };
-      
+
       package = mkOption {
         type = types.path;
         description = "The Dysnomia package";
       };
-      
+
       properties = mkOption {
         description = "An attribute set in which each attribute represents a machine property. Optionally, these values can be shell substitutions.";
         default = {};
       };
-      
+
       containers = mkOption {
         description = "An attribute set in which each key represents a container and each value an attribute set providing its configuration properties";
         default = {};
       };
-      
+
       components = mkOption {
         description = "An atttribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state";
         default = {};
       };
-      
+
       extraContainerProperties = mkOption {
         description = "An attribute set providing additional container settings in addition to the default properties";
         default = {};
       };
-      
+
       extraContainerPaths = mkOption {
         description = "A list of paths containing additional container configurations that are added to the search folders";
         default = [];
       };
-      
+
       extraModulePaths = mkOption {
         description = "A list of paths containing additional modules that are added to the search folders";
         default = [];
       };
     };
   };
-  
+
   config = mkIf cfg.enable {
-  
+
     environment.etc = {
       "dysnomia/containers" = {
         source = containersDir;
@@ -136,16 +136,16 @@ in
         source = properties;
       };
     };
-    
+
     environment.variables = {
       DYSNOMIA_STATEDIR = "/var/state/dysnomia-nixos";
       DYSNOMIA_CONTAINERS_PATH = "${lib.concatMapStrings (containerPath: "${containerPath}:") cfg.extraContainerPaths}/etc/dysnomia/containers";
       DYSNOMIA_MODULES_PATH = "${lib.concatMapStrings (modulePath: "${modulePath}:") cfg.extraModulePaths}/etc/dysnomia/modules";
     };
-    
+
     environment.systemPackages = [ cfg.package ];
-    
-    dysnomia.package = pkgs.dysnomia.override (origArgs: {
+
+    services.dysnomia.package = pkgs.dysnomia.override (origArgs: {
       enableApacheWebApplication = config.services.httpd.enable;
       enableAxis2WebService = config.services.tomcat.axis2.enable;
       enableEjabberdDump = config.services.ejabberd.enable;
@@ -155,10 +155,10 @@ in
       enableTomcatWebApplication = config.services.tomcat.enable;
       enableMongoDatabase = config.services.mongodb.enable;
     });
-    
-    dysnomia.properties = {
+
+    services.dysnomia.properties = {
       hostname = config.networking.hostName;
-      system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
+      inherit (config.nixpkgs.localSystem) system;
 
       supportedTypes = (import "${pkgs.stdenv.mkDerivation {
         name = "supportedtypes";
@@ -173,8 +173,8 @@ in
         '';
       }}");
     };
-    
-    dysnomia.containers = lib.recursiveUpdate ({
+
+    services.dysnomia.containers = lib.recursiveUpdate ({
       process = {};
       wrapper = {};
     }
@@ -192,9 +192,11 @@ in
         mysqlPassword = builtins.readFile (config.services.mysql.rootPassword);
       };
     }
-    // lib.optionalAttrs (config.services.postgresql.enable && cfg.enableAuthentication) { postgresql-database = {
-      postgresqlUsername = "root";
-    }; }
+    // lib.optionalAttrs (config.services.postgresql.enable) { postgresql-database = {
+      } // lib.optionalAttrs (cfg.enableAuthentication) {
+        postgresqlUsername = "postgres";
+      };
+    }
     // lib.optionalAttrs (config.services.tomcat.enable) { tomcat-webapplication = {
       tomcatPort = 8080;
     }; }
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
index 053e7e95635f..164221cbab7f 100644
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -57,7 +57,7 @@ in {
         chown ${fahUser} ${stateDir}
         cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg
       '';
-      script = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
+      script = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
     };
 
     services.foldingAtHome.config = ''
diff --git a/nixos/modules/services/misc/geoip-updater.nix b/nixos/modules/services/misc/geoip-updater.nix
index 760fa66e80d6..e0b9df96f8e8 100644
--- a/nixos/modules/services/misc/geoip-updater.nix
+++ b/nixos/modules/services/misc/geoip-updater.nix
@@ -14,7 +14,7 @@ let
   # ExecStart= command with '@' doesn't work because we start a shell (new
   # process) that creates a new argv[0].)
   geoip-updater = pkgs.writeScriptBin "geoip-updater" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     skipExisting=0
     debug()
     {
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index f0b44b7bedeb..63e976ae566c 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.gitea;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database.type == "postgres";
   configFile = pkgs.writeText "app.ini" ''
     APP_NAME = ${cfg.appName}
     RUN_USER = ${cfg.user}
@@ -16,6 +18,9 @@ let
     USER = ${cfg.database.user}
     PASSWD = #dbpass#
     PATH = ${cfg.database.path}
+    ${optionalString usePostgresql ''
+      SSL_MODE = disable
+    ''}
 
     [repository]
     ROOT = ${cfg.repositoryRoot}
@@ -35,6 +40,10 @@ let
     SECRET_KEY = #secretkey#
     INSTALL_LOCK = true
 
+    [log]
+    ROOT_PATH = ${cfg.log.rootPath}
+    LEVEL = ${cfg.log.level}
+
     ${cfg.extraConfig}
   '';
 in
@@ -60,6 +69,19 @@ in
         description = "gitea data directory.";
       };
 
+      log = {
+        rootPath = mkOption {
+          default = "${cfg.stateDir}/log";
+          type = types.str;
+          description = "Root path for log files.";
+        };
+        level = mkOption {
+          default = "Trace";
+          type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ];
+          description = "General log level.";
+        };
+      };
+
       user = mkOption {
         type = types.str;
         default = "gitea";
@@ -82,7 +104,7 @@ in
 
         port = mkOption {
           type = types.int;
-          default = 3306;
+          default = (if !usePostgresql then 3306 else pg.port);
           description = "Database host port.";
         };
 
@@ -123,6 +145,15 @@ in
           default = "${cfg.stateDir}/data/gitea.db";
           description = "Path to the sqlite3 database file.";
         };
+
+        createDatabase = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether to create a local postgresql database automatically.
+            This only applies if database type "postgres" is selected.
+          '';
+        };
       };
 
       appName = mkOption {
@@ -186,10 +217,11 @@ in
   };
 
   config = mkIf cfg.enable {
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
 
     systemd.services.gitea = {
       description = "gitea";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.gitea.bin ];
 
@@ -231,12 +263,31 @@ in
           mkdir -p ${cfg.stateDir}/conf
           cp -r ${pkgs.gitea.out}/locale ${cfg.stateDir}/conf/locale
         fi
+      '' + optionalString (usePostgresql && cfg.database.createDatabase) ''
+        if ! test -e "${cfg.stateDir}/db-created"; then
+          echo "CREATE ROLE ${cfg.database.user}
+                  WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})'
+                  NOCREATEDB NOCREATEROLE LOGIN"   |
+            ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb             \
+            --owner=${cfg.database.user}           \
+            --encoding=UTF8                        \
+            --lc-collate=C                         \
+            --lc-ctype=C                           \
+            --template=template0                   \
+            ${cfg.database.name}
+          touch "${cfg.stateDir}/db-created"
+        fi
+      '' + ''
+        chown ${cfg.user} -R ${cfg.stateDir}
       '';
 
       serviceConfig = {
         Type = "simple";
         User = cfg.user;
         WorkingDirectory = cfg.stateDir;
+        PermissionsStartOnly = true;
         ExecStart = "${pkgs.gitea.bin}/bin/gitea web";
         Restart = "always";
       };
@@ -253,6 +304,7 @@ in
         description = "Gitea Service";
         home = cfg.stateDir;
         createHome = true;
+        useDefaultShell = true;
       };
     };
 
diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix
index 44880ebeda14..94a98e0335df 100644
--- a/nixos/modules/services/misc/gitit.nix
+++ b/nixos/modules/services/misc/gitit.nix
@@ -17,7 +17,7 @@ let
   gititSh = hsPkgs: extras: with pkgs; let
     env = gititWithPkgs hsPkgs extras;
   in writeScript "gitit" ''
-    #!${stdenv.shell}
+    #!${runtimeShell}
     cd $HOME
     export NIX_GHC="${env}/bin/ghc"
     export NIX_GHCPKG="${env}/bin/ghc-pkg"
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 7b2b40e59232..20d7ec90dcc9 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -29,8 +29,12 @@ let
 
   gitalyToml = pkgs.writeText "gitaly.toml" ''
     socket_path = "${lib.escape ["\""] gitalySocket}"
+    bin_dir = "${cfg.packages.gitaly}/bin"
     prometheus_listen_addr = "localhost:9236"
 
+    [git]
+    bin_path = "${pkgs.git}/bin/git"
+
     [gitaly-ruby]
     dir = "${cfg.packages.gitaly.ruby}"
 
@@ -70,7 +74,7 @@ let
       secret_key_base: ${cfg.secrets.secret}
       otp_key_base: ${cfg.secrets.otp}
       db_key_base: ${cfg.secrets.db}
-      jws_private_key: ${builtins.toJSON cfg.secrets.jws}
+      openid_connect_signing_key: ${builtins.toJSON cfg.secrets.jws}
   '';
 
   gitlabConfig = {
@@ -104,6 +108,7 @@ let
       ldap.enabled = false;
       omniauth.enabled = false;
       shared.path = "${cfg.statePath}/shared";
+      gitaly.client_path = "${cfg.packages.gitaly}/bin";
       backup.path = "${cfg.backupPath}";
       gitlab_shell = {
         path = "${cfg.packages.gitlab-shell}";
@@ -117,8 +122,6 @@ let
       };
       git = {
         bin_path = "git";
-        max_size = 20971520; # 20MB
-        timeout = 10;
       };
       monitoring = {
         ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
@@ -140,6 +143,7 @@ let
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
     GITLAB_STATE_PATH = "${cfg.statePath}";
     GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
+    SCHEMA = "${cfg.statePath}/db/schema.rb";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
     GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
     GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
@@ -248,7 +252,6 @@ in {
 
       databasePassword = mkOption {
         type = types.str;
-        default = "";
         description = "Gitlab database user password.";
       };
 
@@ -440,12 +443,6 @@ in {
 
     environment.systemPackages = [ pkgs.git gitlab-rake cfg.packages.gitlab-shell ];
 
-    assertions = [
-      { assertion = cfg.databasePassword != "";
-        message = "databasePassword must be set";
-      }
-    ];
-
     # Redis is required for the sidekiq queue runner.
     services.redis.enable = mkDefault true;
     # We use postgres as the main data store.
@@ -496,13 +493,15 @@ in {
       after = [ "network.target" "gitlab.service" ];
       wantedBy = [ "multi-user.target" ];
       environment.HOME = gitlabEnv.HOME;
-      path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ];
+      environment.GEM_HOME = "${cfg.packages.gitaly.rubyEnv}/${ruby.gemPath}";
+      environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
+      path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ruby ];
       serviceConfig = {
         #PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
-        TimeoutSec = "300";
+        TimeoutSec = "infinity";
         Restart = "on-failure";
         WorkingDirectory = gitlabEnv.HOME;
         ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}";
@@ -568,6 +567,7 @@ in {
         mkdir -p ${cfg.statePath}/tmp/pids
         mkdir -p ${cfg.statePath}/tmp/sockets
         mkdir -p ${cfg.statePath}/shell
+        mkdir -p ${cfg.statePath}/db
 
         rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
         mkdir -p ${cfg.statePath}/config
@@ -582,6 +582,7 @@ in {
         ln -sf ${cfg.statePath}/log /run/gitlab/log
         ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
         ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
+        ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml
         chown -R ${cfg.user}:${cfg.group} /run/gitlab
 
         # Prepare home directory
@@ -589,6 +590,7 @@ in {
         touch ${gitlabEnv.HOME}/.ssh/authorized_keys
         chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
 
+        cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
         cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
         ${optionalString cfg.smtp.enable ''
           ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
diff --git a/nixos/modules/services/misc/gitolite.nix b/nixos/modules/services/misc/gitolite.nix
index f395b9558b5a..6e60316d000c 100644
--- a/nixos/modules/services/misc/gitolite.nix
+++ b/nixos/modules/services/misc/gitolite.nix
@@ -207,7 +207,7 @@ in
             gitolite setup -pk ${pubkeyFile}
           fi
           if [ -n "${hooks}" ]; then
-            cp ${hooks} .gitolite/hooks/common/
+            cp -f ${hooks} .gitolite/hooks/common/
             chmod +x .gitolite/hooks/common/*
           fi
           gitolite setup # Upgrade if needed
diff --git a/nixos/modules/services/misc/gitweb.nix b/nixos/modules/services/misc/gitweb.nix
new file mode 100644
index 000000000000..ca21366b7796
--- /dev/null
+++ b/nixos/modules/services/misc/gitweb.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gitweb;
+
+in
+{
+
+  options.services.gitweb = {
+
+    projectroot = mkOption {
+      default = "/srv/git";
+      type = types.path;
+      description = ''
+        Path to git projects (bare repositories) that should be served by
+        gitweb. Must not end with a slash.
+      '';
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      description = ''
+        Verbatim configuration text appended to the generated gitweb.conf file.
+      '';
+      example = ''
+        $feature{'highlight'}{'default'} = [1];
+        $feature{'ctags'}{'default'} = [1];
+        $feature{'avatar'}{'default'} = ['gravatar'];
+      '';
+    };
+
+    gitwebTheme = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Use an alternative theme for gitweb, strongly inspired by GitHub.
+      '';
+    };
+
+    gitwebConfigFile = mkOption {
+      default = pkgs.writeText "gitweb.conf" ''
+        # path to git projects (<project>.git)
+        $projectroot = "${cfg.projectroot}";
+        $highlight_bin = "${pkgs.highlight}/bin/highlight";
+        ${cfg.extraConfig}
+      '';
+      type = types.path;
+      readOnly = true;
+      internal = true;
+    };
+
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+
+}
diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix
index f6d326e43d94..ba744d37e71c 100644
--- a/nixos/modules/services/misc/gogs.nix
+++ b/nixos/modules/services/misc/gogs.nix
@@ -35,6 +35,9 @@ let
     SECRET_KEY = #secretkey#
     INSTALL_LOCK = true
 
+    [log]
+    ROOT_PATH = ${cfg.stateDir}/log
+
     ${cfg.extraConfig}
   '';
 in
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 81fb024f9094..0888221ab62f 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -32,6 +32,24 @@ in
       description = "Content of the configuration file";
     };
 
+    mathjax = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable support for math rendering using MathJax";
+    };
+
+    allowUploads = mkOption {
+      type = types.nullOr (types.enum [ "dir" "page" ]);
+      default = null;
+      description = "Enable uploads of external files";
+    };
+
+    emoji = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Parse and interpret emoji tags";
+    };
+
     branch = mkOption {
       type = types.str;
       default = "master";
@@ -84,6 +102,9 @@ in
             --host ${cfg.address} \
             --config ${builtins.toFile "gollum-config.rb" cfg.extraConfig} \
             --ref ${cfg.branch} \
+            ${optionalString cfg.mathjax "--mathjax"} \
+            ${optionalString cfg.emoji "--emoji"} \
+            ${optionalString (cfg.allowUploads != null) "--allow-uploads ${cfg.allowUploads}"} \
             ${cfg.stateDir}
         '';
       };
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
new file mode 100644
index 000000000000..ac37c11106ef
--- /dev/null
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -0,0 +1,144 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.home-assistant;
+
+  configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config);
+
+  availableComponents = pkgs.home-assistant.availableComponents;
+
+  # Given component "parentConfig.platform", returns whether config.parentConfig
+  # is a list containing a set with set.platform == "platform".
+  #
+  # For example, the component sensor.luftdaten is used as follows:
+  # config.sensor = [ {
+  #   platform = "luftdaten";
+  #   ...
+  # } ];
+  useComponentPlatform = component:
+    let
+      path = splitString "." component;
+      parentConfig = attrByPath (init path) null cfg.config;
+      platform = last path;
+    in isList parentConfig && any
+      (item: item.platform or null == platform)
+      parentConfig;
+
+  # Returns whether component is used in config
+  useComponent = component:
+    hasAttrByPath (splitString "." component) cfg.config
+    || useComponentPlatform component;
+
+  # List of components used in config
+  extraComponents = filter useComponent availableComponents;
+
+  package = if cfg.autoExtraComponents
+    then (cfg.package.override { inherit extraComponents; })
+    else cfg.package;
+
+in {
+  meta.maintainers = with maintainers; [ dotlambda ];
+
+  options.services.home-assistant = {
+    enable = mkEnableOption "Home Assistant";
+
+    configDir = mkOption {
+      default = "/var/lib/hass";
+      type = types.path;
+      description = "The config directory, where your <filename>configuration.yaml</filename> is located.";
+    };
+
+    config = mkOption {
+      default = null;
+      type = with types; nullOr attrs;
+      example = literalExample ''
+        {
+          homeassistant = {
+            name = "Home";
+            time_zone = "UTC";
+          };
+          frontend = { };
+          http = { };
+          feedreader.urls = [ "https://nixos.org/blogs.xml" ];
+        }
+      '';
+      description = ''
+        Your <filename>configuration.yaml</filename> as a Nix attribute set.
+        Beware that setting this option will delete your previous <filename>configuration.yaml</filename>.
+      '';
+    };
+
+    package = mkOption {
+      default = pkgs.home-assistant;
+      defaultText = "pkgs.home-assistant";
+      type = types.package;
+      example = literalExample ''
+        pkgs.home-assistant.override {
+          extraPackages = ps: with ps; [ colorlog ];
+        }
+      '';
+      description = ''
+        Home Assistant package to use.
+        Override <literal>extraPackages</literal> in order to add additional dependencies.
+      '';
+    };
+
+    autoExtraComponents = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        If set to <literal>true</literal>, the components used in <literal>config</literal>
+        are set as the specified package's <literal>extraComponents</literal>.
+        This in turn adds all packaged dependencies to the derivation.
+        You might still see import errors in your log.
+        In this case, you will need to package the necessary dependencies yourself
+        or ask for someone else to package them.
+        If a dependency is packaged but not automatically added to this list,
+        you might need to specify it in <literal>extraPackages</literal>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.home-assistant = {
+      description = "Home Assistant";
+      after = [ "network.target" ];
+      preStart = lib.optionalString (cfg.config != null) ''
+        rm -f ${cfg.configDir}/configuration.yaml
+        ln -s ${configFile} ${cfg.configDir}/configuration.yaml
+      '';
+      serviceConfig = {
+        ExecStart = ''
+          ${package}/bin/hass --config "${cfg.configDir}"
+        '';
+        User = "hass";
+        Group = "hass";
+        Restart = "on-failure";
+        ProtectSystem = "strict";
+        ReadWritePaths = "${cfg.configDir}";
+        PrivateTmp = true;
+      };
+      path = [
+        "/run/wrappers" # needed for ping
+      ];
+    };
+
+    systemd.targets.home-assistant = rec {
+      description = "Home Assistant";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "home-assistant.service" ];
+      after = wants;
+    };
+
+    users.extraUsers.hass = {
+      home = cfg.configDir;
+      createHome = true;
+      group = "hass";
+      uid = config.ids.uids.hass;
+    };
+
+    users.extraGroups.hass.gid = config.ids.gids.hass;
+  };
+}
diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix
index e07a4a44613a..6da9cc8c47e6 100644
--- a/nixos/modules/services/misc/ihaskell.nix
+++ b/nixos/modules/services/misc/ihaskell.nix
@@ -55,7 +55,7 @@ in
       serviceConfig = {
         User = config.users.extraUsers.ihaskell.name;
         Group = config.users.extraGroups.ihaskell.name;
-        ExecStart = "${pkgs.stdenv.shell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
+        ExecStart = "${pkgs.runtimeShell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\"";
       };
     };
   };
diff --git a/nixos/modules/services/misc/logkeys.nix b/nixos/modules/services/misc/logkeys.nix
index 6051c884465d..df0b3ae24c90 100644
--- a/nixos/modules/services/misc/logkeys.nix
+++ b/nixos/modules/services/misc/logkeys.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix
index a3ec0ea59f8f..7e880ad09b89 100644
--- a/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixos/modules/services/misc/matrix-synapse.nix
@@ -4,6 +4,8 @@ with lib;
 
 let
   cfg = config.services.matrix-synapse;
+  pg = config.services.postgresql;
+  usePostgresql = cfg.database_type == "psycopg2";
   logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
   mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}'';
   mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}'';
@@ -38,13 +40,12 @@ database: {
   name: "${cfg.database_type}",
   args: {
     ${concatStringsSep ",\n    " (
-      mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args
+      mapAttrsToList (n: v: "\"${n}\": ${builtins.toJSON v}") cfg.database_args
     )}
   }
 }
 event_cache_size: "${cfg.event_cache_size}"
 verbose: ${cfg.verbose}
-log_file: "/var/log/matrix-synapse/homeserver.log"
 log_config: "${logConfigFile}"
 rc_messages_per_second: ${cfg.rc_messages_per_second}
 rc_message_burst_count: ${cfg.rc_message_burst_count}
@@ -53,8 +54,8 @@ federation_rc_sleep_limit: ${cfg.federation_rc_sleep_limit}
 federation_rc_sleep_delay: ${cfg.federation_rc_sleep_delay}
 federation_rc_reject_limit: ${cfg.federation_rc_reject_limit}
 federation_rc_concurrent: ${cfg.federation_rc_concurrent}
-media_store_path: "/var/lib/matrix-synapse/media"
-uploads_path: "/var/lib/matrix-synapse/uploads"
+media_store_path: "${cfg.dataDir}/media"
+uploads_path: "${cfg.dataDir}/uploads"
 max_upload_size: "${cfg.max_upload_size}"
 max_image_pixels: "${cfg.max_image_pixels}"
 dynamic_thumbnails: ${boolToString cfg.dynamic_thumbnails}
@@ -86,7 +87,7 @@ ${optionalString (cfg.macaroon_secret_key != null) ''
 expire_access_token: ${boolToString cfg.expire_access_token}
 enable_metrics: ${boolToString cfg.enable_metrics}
 report_stats: ${boolToString cfg.report_stats}
-signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
+signing_key_path: "${cfg.dataDir}/homeserver.signing.key"
 key_refresh_interval: "${cfg.key_refresh_interval}"
 perspectives:
   servers: {
@@ -156,7 +157,7 @@ in {
       tls_certificate_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.crt";
+        example = "${cfg.dataDir}/homeserver.tls.crt";
         description = ''
           PEM encoded X509 certificate for TLS.
           You can replace the self-signed certificate that synapse
@@ -168,7 +169,7 @@ in {
       tls_private_key_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.key";
+        example = "${cfg.dataDir}/homeserver.tls.key";
         description = ''
           PEM encoded private key for TLS. Specify null if synapse is not
           speaking TLS directly.
@@ -177,7 +178,7 @@ in {
       tls_dh_params_path = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "/var/lib/matrix-synapse/homeserver.tls.dh";
+        example = "${cfg.dataDir}/homeserver.tls.dh";
         description = ''
           PEM dh parameters for ephemeral keys
         '';
@@ -185,6 +186,7 @@ in {
       server_name = mkOption {
         type = types.str;
         example = "example.com";
+        default = config.networking.hostName;
         description = ''
           The domain name of the server, with optional explicit port.
           This is used by remote servers to connect to this server,
@@ -340,16 +342,39 @@ in {
       };
       database_type = mkOption {
         type = types.enum [ "sqlite3" "psycopg2" ];
-        default = "sqlite3";
+        default = if versionAtLeast config.system.stateVersion "18.03"
+          then "psycopg2"
+          else "sqlite3";
         description = ''
           The database engine name. Can be sqlite or psycopg2.
         '';
       };
+      create_local_database = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to create a local database automatically.
+        '';
+      };
+      database_name = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database name.";
+      };
+      database_user = mkOption {
+        type = types.str;
+        default = "matrix-synapse";
+        description = "Database user name.";
+      };
       database_args = mkOption {
         type = types.attrs;
         default = {
-          database = "/var/lib/matrix-synapse/homeserver.db";
-        };
+          sqlite3 = { database = "${cfg.dataDir}/homeserver.db"; };
+          psycopg2 = {
+            user = cfg.database_user;
+            database = cfg.database_name;
+          };
+        }."${cfg.database_type}";
         description = ''
           Arguments to pass to the engine.
         '';
@@ -579,6 +604,18 @@ in {
           Extra config options for matrix-synapse.
         '';
       };
+      extraConfigFiles = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          Extra config files to include.
+
+          The configuration files will be included based on the command line
+          argument --config-path. This allows to configure secrets without
+          having to go through the Nix store, e.g. based on deployment keys if
+          NixOPS is in use.
+        '';
+      };
       logConfig = mkOption {
         type = types.lines;
         default = readFile ./matrix-synapse-log_config.yaml;
@@ -586,6 +623,14 @@ in {
           A yaml python logging config file
         '';
       };
+      dataDir = mkOption {
+        type = types.str;
+        default = "/var/lib/matrix-synapse";
+        description = ''
+          The directory where matrix-synapse stores its stateful data such as
+          certificates, media and uploads.
+        '';
+      };
     };
   };
 
@@ -593,7 +638,7 @@ in {
     users.extraUsers = [
       { name = "matrix-synapse";
         group = "matrix-synapse";
-        home = "/var/lib/matrix-synapse";
+        home = cfg.dataDir;
         createHome = true;
         shell = "${pkgs.bash}/bin/bash";
         uid = config.ids.uids.matrix-synapse;
@@ -604,23 +649,48 @@ in {
         gid = config.ids.gids.matrix-synapse;
       } ];
 
+    services.postgresql.enable = mkIf usePostgresql (mkDefault true);
+
     systemd.services.matrix-synapse = {
       description = "Synapse Matrix homeserver";
-      after = [ "network.target" ];
+      after = [ "network.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         ${cfg.package}/bin/homeserver \
           --config-path ${configFile} \
-          --keys-directory /var/lib/matrix-synapse \
+          --keys-directory ${cfg.dataDir} \
           --generate-keys
+      '' + optionalString (usePostgresql && cfg.create_local_database) ''
+        if ! test -e "${cfg.dataDir}/db-created"; then
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createuser \
+            --login \
+            --no-createdb \
+            --no-createrole \
+            --encrypted \
+            ${cfg.database_user}
+          ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \
+            ${pg.package}/bin/createdb \
+            --owner=${cfg.database_user} \
+            --encoding=UTF8 \
+            --lc-collate=C \
+            --lc-ctype=C \
+            --template=template0 \
+            ${cfg.database_name}
+          touch "${cfg.dataDir}/db-created"
+        fi
       '';
       serviceConfig = {
         Type = "simple";
         User = "matrix-synapse";
         Group = "matrix-synapse";
-        WorkingDirectory = "/var/lib/matrix-synapse";
+        WorkingDirectory = cfg.dataDir;
         PermissionsStartOnly = true;
-        ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory /var/lib/matrix-synapse";
+        ExecStart = ''
+          ${cfg.package}/bin/homeserver \
+            ${ concatMapStringsSep "\n  " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
+            --keys-directory ${cfg.dataDir}
+        '';
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/misc/mbpfan.nix b/nixos/modules/services/misc/mbpfan.nix
index 972d8b572d36..50f6f80ad00c 100644
--- a/nixos/modules/services/misc/mbpfan.nix
+++ b/nixos/modules/services/misc/mbpfan.nix
@@ -8,13 +8,7 @@ let
 
 in {
   options.services.mbpfan = {
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Whether to enable the mbpfan daemon.
-      '';
-    };
+    enable = mkEnableOption "mbpfan, fan controller daemon for Apple Macs and MacBooks";
 
     package = mkOption {
       type = types.package;
diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix
index 47be10274d3b..effa29b64f63 100644
--- a/nixos/modules/services/misc/mesos-slave.nix
+++ b/nixos/modules/services/misc/mesos-slave.nix
@@ -188,7 +188,7 @@ in {
       description = "Mesos Slave";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      path = [ pkgs.stdenv.shellPackage ];
+      path = [ pkgs.runtimeShellPackage ];
       serviceConfig = {
         ExecStart = ''
           ${pkgs.mesos}/bin/mesos-slave \
@@ -213,7 +213,7 @@ in {
         PermissionsStartOnly = true;
       };
       preStart = ''
-        mkdir -m 0700 -p ${cfg.workDir}
+        mkdir -m 0701 -p ${cfg.workDir}
       '';
     };
   };
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index beca820d2d60..f2d34560a718 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -8,7 +8,7 @@ let
 
   nix = cfg.package.out;
 
-  isNix112 = versionAtLeast (getVersion nix) "1.12pre";
+  isNix20 = versionAtLeast (getVersion nix) "2.0pre";
 
   makeNixBuildUser = nr:
     { name = "nixbld${toString nr}";
@@ -26,32 +26,40 @@ let
 
   nixConf =
     let
-      # If we're using sandbox for builds, then provide /bin/sh in
-      # the sandbox as a bind-mount to bash. This means we also need to
-      # include the entire closure of bash.
-      sh = pkgs.stdenv.shell;
+      # In Nix < 2.0, If we're using sandbox for builds, then provide
+      # /bin/sh in the sandbox as a bind-mount to bash. This means we
+      # also need to include the entire closure of bash. Nix >= 2.0
+      # provides a /bin/sh by default.
+      sh = pkgs.runtimeShell;
       binshDeps = pkgs.writeReferencesToFile sh;
     in
-      pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
-        extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+      pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; } ''
+        ${optionalString (!isNix20) ''
+          extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+        ''}
         cat > $out <<END
         # WARNING: this file is generated from the nix.* options in
         # your NixOS configuration, typically
         # /etc/nixos/configuration.nix.  Do not edit it!
         build-users-group = nixbld
-        build-max-jobs = ${toString (cfg.maxJobs)}
-        build-cores = ${toString (cfg.buildCores)}
-        build-use-sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
-        build-sandbox-paths = ${toString cfg.sandboxPaths} /bin/sh=${sh} $(echo $extraPaths)
-        binary-caches = ${toString cfg.binaryCaches}
-        trusted-binary-caches = ${toString cfg.trustedBinaryCaches}
-        binary-cache-public-keys = ${toString cfg.binaryCachePublicKeys}
+        ${if isNix20 then "max-jobs" else "build-max-jobs"} = ${toString (cfg.maxJobs)}
+        ${if isNix20 then "cores" else "build-cores"} = ${toString (cfg.buildCores)}
+        ${if isNix20 then "sandbox" else "build-use-sandbox"} = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox}
+        ${if isNix20 then "extra-sandbox-paths" else "build-sandbox-paths"} = ${toString cfg.sandboxPaths} ${optionalString (!isNix20) "/bin/sh=${sh} $(echo $extraPaths)"}
+        ${if isNix20 then "substituters" else "binary-caches"} = ${toString cfg.binaryCaches}
+        ${if isNix20 then "trusted-substituters" else "trusted-binary-caches"} = ${toString cfg.trustedBinaryCaches}
+        ${if isNix20 then "trusted-public-keys" else "binary-cache-public-keys"} = ${toString cfg.binaryCachePublicKeys}
         auto-optimise-store = ${boolToString cfg.autoOptimiseStore}
-        ${optionalString cfg.requireSignedBinaryCaches ''
-          signed-binary-caches = *
+        ${if isNix20 then ''
+          require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"}
+        '' else ''
+          signed-binary-caches = ${if cfg.requireSignedBinaryCaches then "*" else ""}
         ''}
         trusted-users = ${toString cfg.trustedUsers}
         allowed-users = ${toString cfg.allowedUsers}
+        ${optionalString (isNix20 && !cfg.distributedBuilds) ''
+          builders =
+        ''}
         $extraOptions
         END
       '';
@@ -377,8 +385,9 @@ in
     systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
 
     systemd.services.nix-daemon =
-      { path = [ nix pkgs.openssl.bin pkgs.utillinux config.programs.ssh.package ]
-          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
+      { path = [ nix pkgs.utillinux ]
+          ++ optionals cfg.distributedBuilds [ config.programs.ssh.package pkgs.gzip ]
+          ++ optionals (!isNix20) [ pkgs.openssl.bin ];
 
         environment = cfg.envVars
           // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
@@ -396,10 +405,9 @@ in
       };
 
     nix.envVars =
-      { NIX_CONF_DIR = "/etc/nix";
-      }
+      optionalAttrs (!isNix20) {
+        NIX_CONF_DIR = "/etc/nix";
 
-      // optionalAttrs (!isNix112) {
         # Enable the copy-from-other-stores substituter, which allows
         # builds to be sped up by copying build results from remote
         # Nix stores.  To do this, mount the remote file system on a
@@ -407,12 +415,8 @@ in
         NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix";
       }
 
-      // optionalAttrs cfg.distributedBuilds {
-        NIX_BUILD_HOOK =
-          if isNix112 then
-            "${nix}/libexec/nix/build-remote"
-          else
-            "${nix}/libexec/nix/build-remote.pl";
+      // optionalAttrs (cfg.distributedBuilds && !isNix20) {
+        NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl";
       };
 
     # Set up the environment variables for running Nix.
@@ -420,7 +424,7 @@ in
       { NIX_PATH = concatStringsSep ":" cfg.nixPath;
       };
 
-    environment.extraInit =
+    environment.extraInit = optionalString (!isNix20)
       ''
         # Set up secure multi-user builds: non-root users build through the
         # Nix daemon.
@@ -435,19 +439,18 @@ in
 
     services.xserver.displayManager.hiddenUsers = map ({ name, ... }: name) nixbldUsers;
 
+    # FIXME: use systemd-tmpfiles to create Nix directories.
     system.activationScripts.nix = stringAfter [ "etc" "users" ]
       ''
         # Nix initialisation.
-        mkdir -m 0755 -p \
+        install -m 0755 -d \
           /nix/var/nix/gcroots \
           /nix/var/nix/temproots \
-          /nix/var/nix/manifests \
           /nix/var/nix/userpool \
           /nix/var/nix/profiles \
           /nix/var/nix/db \
-          /nix/var/log/nix/drvs \
-          /nix/var/nix/channel-cache
-        mkdir -m 1777 -p \
+          /nix/var/log/nix/drvs
+        install -m 1777 -d \
           /nix/var/nix/gcroots/per-user \
           /nix/var/nix/profiles/per-user \
           /nix/var/nix/gcroots/tmp
diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix
index 66148431709f..5bd9cf9086f1 100644
--- a/nixos/modules/services/misc/nix-ssh-serve.nix
+++ b/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -1,8 +1,12 @@
 { config, lib, pkgs, ... }:
 
 with lib;
-
-{
+let cfg = config.nix.sshServe;
+    command =
+      if cfg.protocol == "ssh"
+        then "nix-store --serve"
+      else "nix-daemon --stdio";
+in {
   options = {
 
     nix.sshServe = {
@@ -10,7 +14,7 @@ with lib;
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = "Whether to enable serving the Nix store as a binary cache via SSH.";
+        description = "Whether to enable serving the Nix store as a remote store via SSH.";
       };
 
       keys = mkOption {
@@ -20,14 +24,20 @@ with lib;
         description = "A list of SSH public keys allowed to access the binary cache via SSH.";
       };
 
+      protocol = mkOption {
+        type = types.enum [ "ssh" "ssh-ng" ];
+        default = "ssh";
+        description = "The specific Nix-over-SSH protocol to use.";
+      };
+
     };
 
   };
 
-  config = mkIf config.nix.sshServe.enable {
+  config = mkIf cfg.enable {
 
     users.extraUsers.nix-ssh = {
-      description = "Nix SSH substituter user";
+      description = "Nix SSH store user";
       uid = config.ids.uids.nix-ssh;
       useDefaultShell = true;
     };
@@ -41,11 +51,11 @@ with lib;
         PermitTTY no
         PermitTunnel no
         X11Forwarding no
-        ForceCommand ${config.nix.package.out}/bin/nix-store --serve
+        ForceCommand ${config.nix.package.out}/bin/${command}
       Match All
     '';
 
-    users.extraUsers.nix-ssh.openssh.authorizedKeys.keys = config.nix.sshServe.keys;
+    users.extraUsers.nix-ssh.openssh.authorizedKeys.keys = cfg.keys;
 
   };
 }
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 41cadb4a6de0..4bd1c20edf71 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -16,14 +16,14 @@ let
     It isn't perfect, but it seems to cover a vast majority of use cases.
     Caveat: even if the package is reached by a different means,
     the path above will be shown and not e.g. `${config.services.foo.package}`. */
-  manual = import ../../../doc/manual {
+  manual = import ../../../doc/manual rec {
     inherit pkgs config;
-    version = config.system.nixosRelease;
-    revision = "release-${config.system.nixosRelease}";
+    version = config.system.nixos.release;
+    revision = "release-${version}";
     options =
       let
         scrubbedEval = evalModules {
-          modules = [ { nixpkgs.system = config.nixpkgs.system; } ] ++ baseModules;
+          modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules;
           args = (config._module.args) // { modules = [ ]; };
           specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; };
         };
@@ -43,7 +43,7 @@ let
 
   helpScript = pkgs.writeScriptBin "nixos-help"
     ''
-      #! ${pkgs.stdenv.shell} -e
+      #! ${pkgs.runtimeShell} -e
       browser="$BROWSER"
       if [ -z "$browser" ]; then
         browser="$(type -P xdg-open || true)"
@@ -112,10 +112,10 @@ in
 
     system.build.manual = manual;
 
-    environment.systemPackages =
-      [ manual.manual helpScript ]
-      ++ optionals config.services.xserver.enable [desktopItem pkgs.nixos-icons]
-      ++ optional config.programs.man.enable manual.manpages;
+    environment.systemPackages = []
+      ++ optionals config.services.xserver.enable [ desktopItem pkgs.nixos-icons ]
+      ++ optional  config.documentation.man.enable manual.manpages
+      ++ optionals config.documentation.doc.enable [ manual.manual helpScript ];
 
     boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"];
 
diff --git a/nixos/modules/services/misc/novacomd.nix b/nixos/modules/services/misc/novacomd.nix
new file mode 100644
index 000000000000..7cfc68d2b673
--- /dev/null
+++ b/nixos/modules/services/misc/novacomd.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.novacomd;
+
+in {
+
+  options = {
+    services.novacomd = {
+      enable = mkEnableOption "Novacom service for connecting to WebOS devices";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.webos.novacom ];
+
+    systemd.services.novacomd = {
+      description = "Novacom WebOS daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.webos.novacomd}/sbin/novacomd";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ dtzWill ];
+}
diff --git a/nixos/modules/services/misc/osrm.nix b/nixos/modules/services/misc/osrm.nix
new file mode 100644
index 000000000000..7ec8b15906fc
--- /dev/null
+++ b/nixos/modules/services/misc/osrm.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.osrm;
+in
+
+{
+  options.services.osrm = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable the OSRM service.";
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = "IP address on which the web server will listen.";
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 5000;
+      description = "Port on which the web server will run.";
+    };
+
+    threads = mkOption {
+      type = types.int;
+      default = 4;
+      description = "Number of threads to use.";
+    };
+
+    algorithm = mkOption {
+      type = types.enum [ "CH" "CoreCH" "MLD" ];
+      default = "MLD";
+      description = "Algorithm to use for the data. Must be one of CH, CoreCH, MLD";
+    };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "--max-table-size 1000" "--max-matching-size 1000" ];
+      description = "Extra command line arguments passed to osrm-routed";
+    };
+
+    dataFile = mkOption {
+      type = types.path;
+      example = "/var/lib/osrm/berlin-latest.osrm";
+      description = "Data file location";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    users.users.osrm = {
+      group = config.users.users.osrm.name;
+      description = "OSRM user";
+      createHome = false;
+    };
+
+    users.groups.osrm = { };
+
+    systemd.services.osrm = {
+      description = "OSRM service";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = config.users.extraUsers.osrm.name;
+        ExecStart = ''
+          ${pkgs.osrm-backend}/bin/osrm-routed \
+            --ip ${cfg.address} \
+            --port ${toString cfg.port} \
+            --threads ${toString cfg.threads} \
+            --algorithm ${cfg.algorithm} \
+            ${toString cfg.extraFlags} \
+            ${cfg.dataFile}
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index ae3f84333d2d..c757093e5c1b 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -6,6 +6,8 @@ let
 
   cfg = config.services.parsoid;
 
+  parsoid = pkgs.nodePackages."parsoid-git://github.com/abbradar/parsoid#stable";
+
   confTree = {
     worker_heartbeat_timeout = 300000;
     logging = { level = "info"; };
@@ -93,7 +95,7 @@ in
       after = [ "network.target" ];
       serviceConfig = {
         User = "nobody";
-        ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
+        ExecStart = "${parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
       };
     };
 
diff --git a/nixos/modules/services/misc/safeeyes.nix b/nixos/modules/services/misc/safeeyes.nix
new file mode 100644
index 000000000000..1a33971d9227
--- /dev/null
+++ b/nixos/modules/services/misc/safeeyes.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.safeeyes;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.safeeyes = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the safeeyes OSGi service";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.user.services.safeeyes = {
+      description = "Safeeyes";
+
+      wantedBy = [ "graphical-session.target" ];
+      partOf   = [ "graphical-session.target" ];
+
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.safeeyes}/bin/safeeyes
+        '';
+        Restart = "on-failure";
+        RestartSec = 3;
+        StartLimitInterval = 350;
+        StartLimitBurst = 10;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/misc/serviio.nix b/nixos/modules/services/misc/serviio.nix
new file mode 100644
index 000000000000..a6612e9c6adb
--- /dev/null
+++ b/nixos/modules/services/misc/serviio.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.serviio;
+
+  serviioStart = pkgs.writeScript "serviio.sh" ''
+    #!${pkgs.bash}/bin/sh
+
+    SERVIIO_HOME=${pkgs.serviio}
+    
+    # Setup the classpath
+    SERVIIO_CLASS_PATH="$SERVIIO_HOME/lib/*:$SERVIIO_HOME/config"
+
+    # Setup Serviio specific properties
+    JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -Dorg.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade
+               -Dderby.system.home=${cfg.dataDir}/library -Dserviio.home=${cfg.dataDir} -Dffmpeg.location=${pkgs.ffmpeg}/bin/ffmpeg -Ddcraw.location=${pkgs.dcraw}/bin/dcraw"
+
+    # Execute the JVM in the foreground
+    exec ${pkgs.jre}/bin/java -Xmx512M -Xms20M -XX:+UseG1GC -XX:GCTimeRatio=1 -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 $JAVA_OPTS -classpath "$SERVIIO_CLASS_PATH" org.serviio.MediaServer "$@"
+  '';
+  
+in {
+
+  ###### interface
+  options = {
+    services.serviio = {
+      
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the Serviio Media Server.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/serviio";
+        description = ''
+          The directory where serviio stores its state, data, etc.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.serviio = {
+      description = "Serviio Media Server";
+      after = [ "local-fs.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.serviio ];
+      serviceConfig = {
+        User = "serviio";
+        Group = "serviio";
+        ExecStart = "${serviioStart}";
+        ExecStop = "${serviioStart} -stop";
+      };
+    };
+
+    users.extraUsers = [
+      { 
+        name = "serviio";
+        group = "serviio";
+        home = cfg.dataDir;
+        description = "Serviio Media Server User";
+        createHome = true;
+        isSystemUser = true;
+      }
+    ];
+
+    users.extraGroups = [
+      { name = "serviio";} 
+    ];
+
+    networking.firewall = {
+      allowedTCPPorts = [ 
+        8895  # serve UPnP responses
+        23423 # console
+        23424 # mediabrowser
+      ];
+      allowedUDPPorts = [ 
+        1900 # UPnP service discovey
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/ssm-agent.nix b/nixos/modules/services/misc/ssm-agent.nix
index c1e1f0903539..e951a4c7ffa8 100644
--- a/nixos/modules/services/misc/ssm-agent.nix
+++ b/nixos/modules/services/misc/ssm-agent.nix
@@ -8,11 +8,11 @@ let
   # in nixpkgs doesn't seem to work properly on NixOS, so let's just fake the two fields SSM
   # looks for. See https://github.com/aws/amazon-ssm-agent/issues/38 for upstream fix.
   fake-lsb-release = pkgs.writeScriptBin "lsb_release" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
 
     case "$1" in
       -i) echo "nixos";;
-      -r) echo "${config.system.nixosVersion}";;
+      -r) echo "${config.system.nixos.version}";;
     esac
   '';
 in {
diff --git a/nixos/modules/services/misc/xmr-stak.nix b/nixos/modules/services/misc/xmr-stak.nix
new file mode 100644
index 000000000000..57f439365471
--- /dev/null
+++ b/nixos/modules/services/misc/xmr-stak.nix
@@ -0,0 +1,73 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.xmr-stak;
+
+  pkg = pkgs.xmr-stak.override {
+    inherit (cfg) openclSupport cudaSupport;
+  };
+
+  xmrConfArg = optionalString (cfg.configText != "") ("-c " +
+    pkgs.writeText "xmr-stak-config.txt" cfg.configText);
+
+in
+
+{
+  options = {
+    services.xmr-stak = {
+      enable = mkEnableOption "xmr-stak miner";
+      openclSupport = mkEnableOption "support for OpenCL (AMD/ATI graphics cards)";
+      cudaSupport = mkEnableOption "support for CUDA (NVidia graphics cards)";
+
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "--noCPU" "--currency monero" ];
+        description = "List of parameters to pass to xmr-stak.";
+      };
+
+      configText = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          "currency" : "monero",
+          "pool_list" :
+            [ { "pool_address" : "pool.supportxmr.com:5555",
+                "wallet_address" : "<long-hash>",
+                "pool_password" : "minername",
+                "pool_weight" : 1,
+              },
+            ],
+        '';
+        description = ''
+          Verbatim xmr-stak config.txt. If empty, the <literal>-c</literal>
+          parameter will not be added to the xmr-stak command.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.xmr-stak = {
+      wantedBy = [ "multi-user.target" ];
+      bindsTo = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      environment = mkIf cfg.cudaSupport {
+        LD_LIBRARY_PATH = "${pkgs.linuxPackages_latest.nvidia_x11}/lib";
+      };
+      script = ''
+        exec ${pkg}/bin/xmr-stak ${xmrConfArg} ${concatStringsSep " " cfg.extraArgs}
+      '';
+      serviceConfig = let rootRequired = cfg.openclSupport || cfg.cudaSupport; in {
+        # xmr-stak generates cpu and/or gpu configuration files
+        WorkingDirectory = "/tmp";
+        PrivateTmp = true;
+        DynamicUser = !rootRequired;
+        LimitMEMLOCK = toString (1024*1024);
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/zookeeper.nix b/nixos/modules/services/misc/zookeeper.nix
index d85b5e4ec507..91539592511c 100644
--- a/nixos/modules/services/misc/zookeeper.nix
+++ b/nixos/modules/services/misc/zookeeper.nix
@@ -106,10 +106,19 @@ in {
       '';
     };
 
+    package = mkOption {
+      description = "The zookeeper package to use";
+      default = pkgs.zookeeper;
+      defaultText = "pkgs.zookeeper";
+      type = types.package;
+    };
+
   };
 
 
   config = mkIf cfg.enable {
+    environment.systemPackages = [cfg.package];
+
     systemd.services.zookeeper = {
       description = "Zookeeper Daemon";
       wantedBy = [ "multi-user.target" ];
@@ -118,7 +127,7 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.jre}/bin/java \
-            -cp "${pkgs.zookeeper}/lib/*:${pkgs.zookeeper}/${pkgs.zookeeper.name}.jar:${configDir}" \
+            -cp "${cfg.package}/lib/*:${cfg.package}/${cfg.package.name}.jar:${configDir}" \
             ${escapeShellArgs cfg.extraCmdLineOptions} \
             -Dzookeeper.datadir.autocreate=false \
             ${optionalString cfg.preferIPv4 "-Djava.net.preferIPv4Stack=true"} \
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 9abd6e9ab641..839116de6265 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -38,7 +38,7 @@ let
   ];
 
   shellCmdsForEventScript = eventname: commands: ''
-    echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}"
+    echo "#!${pkgs.runtimeShell}" > "$out/${eventname}"
     echo '${commands}' >> "$out/${eventname}"
     chmod a+x "$out/${eventname}"
   '';
diff --git a/nixos/modules/services/monitoring/fusion-inventory.nix b/nixos/modules/services/monitoring/fusion-inventory.nix
index 1c00f3c299e9..c3b869e00880 100644
--- a/nixos/modules/services/monitoring/fusion-inventory.nix
+++ b/nixos/modules/services/monitoring/fusion-inventory.nix
@@ -55,9 +55,6 @@ in {
       description = "Fusion Inventory Agent";
       wantedBy = [ "multi-user.target" ];
 
-      environment = {
-        OPTIONS = "--no-category=software";
-      };
       serviceConfig = {
         ExecStart = "${pkgs.fusionInventory}/bin/fusioninventory-agent --conf-file=${configFile} --daemon --no-fork";
       };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 4fbacef788f9..eceb91525db4 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -25,6 +25,7 @@ let
     DATABASE_USER = cfg.database.user;
     DATABASE_PASSWORD = cfg.database.password;
     DATABASE_PATH = cfg.database.path;
+    DATABASE_CONN_MAX_LIFETIME = cfg.database.connMaxLifetime;
 
     SECURITY_ADMIN_USER = cfg.security.adminUser;
     SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword;
@@ -49,7 +50,7 @@ in {
     protocol = mkOption {
       description = "Which protocol to listen.";
       default = "http";
-      type = types.enum ["http" "https"];
+      type = types.enum ["http" "https" "socket"];
     };
 
     addr = mkOption {
@@ -111,7 +112,7 @@ in {
       type = mkOption {
         description = "Database type.";
         default = "sqlite3";
-        type = types.enum ["mysql" "sqlite3" "postgresql"];
+        type = types.enum ["mysql" "sqlite3" "postgres"];
       };
 
       host = mkOption {
@@ -143,6 +144,15 @@ in {
         default = "${cfg.dataDir}/data/grafana.db";
         type = types.path;
       };
+
+      connMaxLifetime = mkOption {
+        description = ''
+          Sets the maximum amount of time (in seconds) a connection may be reused.
+          For MySQL this setting should be shorter than the `wait_timeout' variable.
+        '';
+        default = 14400;
+        type = types.int;
+      };
     };
 
     security = {
@@ -241,7 +251,9 @@ in {
       description = "Grafana Service Daemon";
       wantedBy = ["multi-user.target"];
       after = ["networking.target"];
-      environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
+      environment = {
+        QT_QPA_PLATFORM = "offscreen";
+      } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
       serviceConfig = {
         ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
         WorkingDirectory = cfg.dataDir;
@@ -249,7 +261,7 @@ in {
       };
       preStart = ''
         ln -fs ${cfg.package}/share/grafana/conf ${cfg.dataDir}
-        ln -fs ${cfg.package}/share/grafana/vendor ${cfg.dataDir}
+        ln -fs ${cfg.package}/share/grafana/tools ${cfg.dataDir}
       '';
     };
 
diff --git a/nixos/modules/services/monitoring/monit.nix b/nixos/modules/services/monitoring/monit.nix
index 71f50cc0f19d..d48e5c550abb 100644
--- a/nixos/modules/services/monitoring/monit.nix
+++ b/nixos/modules/services/monitoring/monit.nix
@@ -26,16 +26,10 @@ in
 
     environment.systemPackages = [ pkgs.monit ];
 
-    environment.etc = [
-      {
-        source = pkgs.writeTextFile {
-          name = "monitrc";
-          text = config.services.monit.config;
-        };
-        target = "monitrc";
-        mode = "0400";
-      }
-    ];
+    environment.etc."monitrc" = {
+      text = config.services.monit.config;
+      mode = "0400";
+    };
 
     systemd.services.monit = {
       description = "Pro-active monitoring utility for unix systems";
@@ -48,6 +42,8 @@ in
         KillMode = "process";
         Restart = "always";
       };
+      restartTriggers = [ config.environment.etc."monitrc".source ];
     };
+
   };
 }
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index cc6d51f0ef1b..358ffd431dd4 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -17,40 +17,6 @@ let
   nodeCfg = config.services.munin-node;
   cronCfg = config.services.munin-cron;
 
-  muninPlugins = pkgs.stdenv.mkDerivation {
-    name = "munin-available-plugins";
-    buildCommand = ''
-      mkdir -p $out
-
-      cp --preserve=mode ${pkgs.munin}/lib/plugins/* $out/
-
-      for file in $out/*; do
-        case "$file" in
-            */plugin.sh|*/plugins.history)
-              chmod +x "$file"
-              continue;;
-        esac
-
-        # read magic makers from the file
-        family=$(sed -nr 's/.*#%#\s+family\s*=\s*(\S+)\s*/\1/p' $file)
-        cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file)
-
-        wrapProgram $file \
-          --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \
-          --set MUNIN_LIBDIR "${pkgs.munin}/lib" \
-          --set MUNIN_PLUGSTATE "/var/run/munin"
-
-        # munin uses markers to tell munin-node-configure what a plugin can do
-        echo "#%# family=$family" >> $file
-        echo "#%# capabilities=$cap" >> $file
-      done
-
-      # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
-      rm -f $out/diskstats
-    '';
-    buildInputs = [ pkgs.makeWrapper ];
-  };
-
   muninConf = pkgs.writeText "munin.conf"
     ''
       dbdir     /var/lib/munin
@@ -83,6 +49,29 @@ let
 
       ${nodeCfg.extraConfig}
     '';
+
+  pluginConf = pkgs.writeText "munin-plugin-conf"
+    ''
+      [hddtemp_smartctl]
+      user root
+      group root
+
+      [meminfo]
+      user root
+      group root
+
+      [ipmi*]
+      user root
+      group root
+    '';
+
+  pluginConfDir = pkgs.stdenv.mkDerivation {
+    name = "munin-plugin-conf.d";
+    buildCommand = ''
+      mkdir $out
+      ln -s ${pluginConf} $out/nixos-config
+    '';
+  };
 in
 
 {
@@ -179,17 +168,22 @@ in
       description = "Munin Node";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.munin ];
+      path = with pkgs; [ munin smartmontools "/run/current-system/sw" "/run/wrappers" ];
+      environment.MUNIN_LIBDIR = "${pkgs.munin}/lib";
       environment.MUNIN_PLUGSTATE = "/var/run/munin";
+      environment.MUNIN_LOGDIR = "/var/log/munin";
       preStart = ''
         echo "updating munin plugins..."
 
         mkdir -p /etc/munin/plugins
         rm -rf /etc/munin/plugins/*
-        PATH="/run/wrappers/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash
+        ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${pkgs.munin}/lib/plugins --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash
+
+        # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
+        rm /etc/munin/plugins/diskstats || true
       '';
       serviceConfig = {
-        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
+        ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index e1fde4fc9500..d23b329eeb25 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -5,18 +5,25 @@ with lib;
 let
   cfg = config.services.netdata;
 
-  configFile = pkgs.writeText "netdata.conf" cfg.configText;
+  wrappedPlugins = pkgs.runCommand "wrapped-plugins" {} ''
+    mkdir -p $out/libexec/netdata/plugins.d
+    ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
+  '';
+
+  localConfig = {
+    global = {
+      "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d";
+    };
+  };
+  mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
+  configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
 
   defaultUser = "netdata";
 
 in {
   options = {
     services.netdata = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = "Whether to enable netdata monitoring.";
-      };
+      enable = mkEnableOption "netdata";
 
       user = mkOption {
         type = types.str;
@@ -31,9 +38,9 @@ in {
       };
 
       configText = mkOption {
-        type = types.lines;
-        default = "";
-        description = "netdata.conf configuration.";
+        type = types.nullOr types.lines;
+        description = "Verbatim netdata.conf, cannot be combined with config.";
+        default = null;
         example = ''
           [global]
           debug log = syslog
@@ -42,11 +49,29 @@ in {
         '';
       };
 
+      config = mkOption {
+        type = types.attrsOf types.attrs;
+        default = {};
+        description = "netdata.conf configuration as nix attributes. cannot be combined with configText.";
+        example = literalExample ''
+          global = {
+            "debug log" = "syslog";
+            "access log" = "syslog";
+            "error log" = "syslog";
+          };
+        '';
+        };
+      };
     };
-  };
 
   config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.config != {} -> cfg.configText == null ;
+          message = "Cannot specify both config and configText";
+        }
+      ];
     systemd.services.netdata = {
+      path = with pkgs; [ gawk curl ];
       description = "Real time performance monitoring";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
@@ -66,6 +91,15 @@ in {
       };
     };
 
+    security.wrappers."apps.plugin" = {
+      source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin";
+      capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
+      owner = cfg.user;
+      group = cfg.group;
+      permissions = "u+rx,g+rx,o-rwx";
+    };
+
+
     users.extraUsers = optional (cfg.user == defaultUser) {
       name = defaultUser;
     };
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index cf761edad926..8a47c9f1e7d8 100644
--- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -111,11 +111,11 @@ in {
       after    = [ "network.target" ];
       script = ''
         ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \
-        -config.file ${alertmanagerYml} \
-        -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-        -log.level ${cfg.logLevel} \
-        ${optionalString (cfg.webExternalUrl != null) ''-web.external-url ${cfg.webExternalUrl} \''}
-        ${optionalString (cfg.logFormat != null) "-log.format ${cfg.logFormat}"}
+        --config.file ${alertmanagerYml} \
+        --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        --log.level ${cfg.logLevel} \
+        ${optionalString (cfg.webExternalUrl != null) ''--web.external-url ${cfg.webExternalUrl} \''}
+        ${optionalString (cfg.logFormat != null) "--log.format ${cfg.logFormat}"}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
deleted file mode 100644
index ce2e1cf2d74b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix
+++ /dev/null
@@ -1,68 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.blackboxExporter;
-in {
-  options = {
-    services.prometheus.blackboxExporter = {
-      enable = mkEnableOption "prometheus blackbox exporter";
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9115;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the blackbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-blackbox-exporter = {
-      description = "Prometheus exporter for blackbox probes";
-      unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
-        ExecStart = ''
-          ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
-            --web.listen-address :${toString cfg.port} \
-            --config.file ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix b/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
deleted file mode 100644
index f8a5b9576a11..000000000000
--- a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix
+++ /dev/null
@@ -1,128 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.collectdExporter;
-
-  collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
-    -collectd.listen-address ${optionalString (cfg.collectdBinary.listenAddress != null) cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
-    -collectd.security-level ${cfg.collectdBinary.securityLevel} \
-  '' else "";
-
-in {
-  options = {
-    services.prometheus.collectdExporter = {
-      enable = mkEnableOption "prometheus collectd exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9103;
-        description = ''
-          Port to listen on.
-          This is used for scraping as well as the to receive collectd data via the write_http plugin.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface, telemetry and collectd JSON data.
-        '';
-      };
-
-      collectdBinary = {
-        enable = mkEnableOption "collectd binary protocol receiver";
-
-        authFile = mkOption {
-          default = null;
-          type = types.nullOr types.path;
-          description = "File mapping user names to pre-shared keys (passwords).";
-        };
-
-        port = mkOption {
-          type = types.int;
-          default = 25826;
-          description = ''Network address on which to accept collectd binary network packets.'';
-        };
-
-        listenAddress = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          example = "0.0.0.0";
-          description = ''
-            Address to listen on for binary network packets.
-            '';
-        };
-
-        securityLevel = mkOption {
-          type = types.enum ["None" "Sign" "Encrypt"];
-          default = "None";
-          description = ''
-            Minimum required security level for accepted packets.
-            '';
-        };
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the collectd exporter.
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = (optional cfg.openFirewall cfg.port) ++
-      (optional (cfg.openFirewall && cfg.collectdBinary.enable) cfg.collectdBinary.port);
-
-    systemd.services.prometheus-collectd-exporter = {
-      description = "Prometheus exporter for Collectd metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/collectd_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
-            -log.format ${cfg.logFormat} \
-            -log.level ${cfg.logLevel} \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            ${collectSettingsArgs} \
-            ${concatStringsSep " " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
new file mode 100644
index 000000000000..180b273d177e
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -0,0 +1,173 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters;
+
+  # each attribute in `exporterOpts` is expected to have specified:
+  #   - port        (types.int):   port on which the exporter listens
+  #   - serviceOpts (types.attrs): config that is merged with the
+  #                                default definition of the exporter's
+  #                                systemd service
+  #   - extraOpts   (types.attrs): extra configuration options to
+  #                                configure the exporter with, which
+  #                                are appended to the default options
+  #
+  #  Note that `extraOpts` is optional, but a script for the exporter's
+  #  systemd service must be provided by specifying either
+  #  `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart`
+  exporterOpts = {
+    blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; };
+    collectd = import ./exporters/collectd.nix { inherit config lib pkgs; };
+    dovecot  = import ./exporters/dovecot.nix  { inherit config lib pkgs; };
+    fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; };
+    json     = import ./exporters/json.nix     { inherit config lib pkgs; };
+    minio    = import ./exporters/minio.nix    { inherit config lib pkgs; };
+    nginx    = import ./exporters/nginx.nix    { inherit config lib pkgs; };
+    node     = import ./exporters/node.nix     { inherit config lib pkgs; };
+    postfix  = import ./exporters/postfix.nix  { inherit config lib pkgs; };
+    snmp     = import ./exporters/snmp.nix     { inherit config lib pkgs; };
+    unifi    = import ./exporters/unifi.nix    { inherit config lib pkgs; };
+    varnish  = import ./exporters/varnish.nix  { inherit config lib pkgs; };
+  };
+
+  mkExporterOpts = ({ name, port }: {
+    enable = mkEnableOption "the prometheus ${name} exporter";
+    port = mkOption {
+      type = types.int;
+      default = port;
+      description = ''
+        Port to listen on.
+      '';
+    };
+    listenAddress = mkOption {
+      type = types.str;
+      default = "0.0.0.0";
+      description = ''
+        Address to listen on.
+      '';
+    };
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Extra commandline options to pass to the ${name} exporter.
+      '';
+    };
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open port in firewall for incoming connections.
+      '';
+    };
+    firewallFilter = mkOption {
+      type = types.str;
+      default = "-p tcp -m tcp --dport ${toString port}";
+      example = literalExample ''
+        "-i eth0 -p tcp -m tcp --dport ${toString port}"
+      '';
+      description = ''
+        Specify a filter for iptables to use when
+        <option>services.prometheus.exporters.${name}.openFirewall</option>
+        is true. It is used as `ip46tables -I INPUT <option>firewallFilter</option> -j ACCEPT`.
+      '';
+    };
+    user = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        User name under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+    group = mkOption {
+      type = types.str;
+      default = "nobody";
+      description = ''
+        Group under which the ${name} exporter shall be run.
+        Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true.
+      '';
+    };
+  });
+
+  mkSubModule = { name, port, extraOpts, serviceOpts }: {
+    ${name} = mkOption {
+      type = types.submodule {
+        options = (mkExporterOpts {
+          inherit name port;
+        } // extraOpts);
+      };
+      internal = true;
+      default = {};
+    };
+  };
+
+  mkSubModules = (foldl' (a: b: a//b) {}
+    (mapAttrsToList (name: opts: mkSubModule {
+      inherit name;
+      inherit (opts) port serviceOpts;
+      extraOpts = opts.extraOpts or {};
+    }) exporterOpts)
+  );
+
+  mkExporterConf = { name, conf, serviceOpts }:
+    mkIf conf.enable {
+      networking.firewall.extraCommands = mkIf conf.openFirewall ''
+        ip46tables -I INPUT ${conf.firewallFilter} -j ACCEPT
+      '';
+      systemd.services."prometheus-${name}-exporter" = mkMerge ([{
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          Restart = mkDefault "always";
+          PrivateTmp = mkDefault true;
+          WorkingDirectory = mkDefault /tmp;
+        } // mkIf (!(serviceOpts.serviceConfig.DynamicUser or false)) {
+          User = conf.user;
+          Group = conf.group;
+        };
+      } serviceOpts ]);
+  };
+in
+{
+  options.services.prometheus.exporters = mkOption {
+    type = types.submodule {
+      options = (mkSubModules);
+    };
+    description = "Prometheus exporter configuration";
+    default = {};
+    example = literalExample ''
+      {
+        node = {
+          enable = true;
+          enabledCollectors = [ "systemd" ];
+        };
+        varnish.enable = true;
+      }
+    '';
+  };
+
+  config = mkMerge ([{
+    assertions = [{
+      assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null);
+      message = ''
+        Please ensure you have either `services.prometheus.exporters.snmp.configuration'
+          or `services.prometheus.exporters.snmp.configurationPath' set!
+      '';
+    }];
+  }] ++ [(mkIf config.services.minio.enable {
+    services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
+    services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
+    services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
+  })] ++ (mapAttrsToList (name: conf:
+    mkExporterConf {
+      inherit name;
+      inherit (conf) serviceOpts;
+      conf = cfg.${name};
+    }) exporterOpts)
+  );
+
+  meta.doc = ./exporters.xml;
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml
new file mode 100644
index 000000000000..4f0bcb298106
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -0,0 +1,135 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-prometheus-exporters">
+
+<title>Prometheus exporters</title>
+
+<para>Prometheus exporters provide metrics for the <link xlink:href="https://prometheus.io">prometheus monitoring system</link>.</para>
+
+<section><title>Configuration</title>
+  <para>One of the most common exporters is the <link xlink:href="https://github.com/prometheus/node_exporter">node exporter</link>, it provides hardware and OS metrics from the host it's running on. The exporter could be configured as follows:
+<programlisting>
+  services.promtheus.exporters.node = {
+    enable = true;
+    enabledCollectors = [
+      "logind"
+      "systemd"
+    ];
+    disabledCollectors = [
+      "textfile"
+    ];
+    openFirewall = true;
+    firewallFilter = "-i br0 -p tcp -m tcp --dport 9100";
+  };
+</programlisting>
+It should now serve all metrics from the collectors
+that are explicitly enabled and the ones that are
+<link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled by default</link>, via http under <literal>/metrics</literal>. In this example the firewall should just
+allow incoming connections to the exporter's port on the bridge interface <literal>br0</literal>
+(this would have to be configured seperately of course).
+For more information about configuration see <literal>man configuration.nix</literal> or
+search through the <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available options</link>.
+</para>
+</section>
+<section><title>Adding a new exporter</title>
+  <para>To add a new exporter, it has to be packaged first (see <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for examples), then a module can be added. The postfix exporter is used in this example:</para>
+<itemizedlist>
+  <listitem>
+    <para>
+      Some default options for all exporters are provided by
+      <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>:
+    </para>
+  </listitem>
+  <listitem override='none'>
+    <itemizedlist>
+      <listitem><para><literal>enable</literal></para></listitem>
+      <listitem><para><literal>port</literal></para></listitem>
+      <listitem><para><literal>listenAddress</literal></para></listitem>
+      <listitem><para><literal>extraFlags</literal></para></listitem>
+      <listitem><para><literal>openFirewall</literal></para></listitem>
+      <listitem><para><literal>firewallFilter</literal></para></listitem>
+      <listitem><para><literal>user</literal></para></listitem>
+      <listitem><para><literal>group</literal></para></listitem>
+    </itemizedlist>
+  </listitem>
+  <listitem>
+    <para>As there is already a package available, the module can now be added.
+      This is accomplished by adding a new file to the
+      <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal> directory,
+      which will be called postfix.nix and contains all exporter specific options
+      and configuration:
+      <programlisting>
+        # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix
+        { config, lib, pkgs }:
+
+        with lib;
+
+        let
+          # for convenience we define cfg here
+          cfg = config.services.prometheus.exporters.postfix;
+        in
+        {
+          port = 9154; # The postfix exporter listens on this port by default
+
+          # `extraOpts` is an attribute set which contains additional options
+          # (and optional overrides for default options).
+          # Note that this attribute is optional.
+          extraOpts = {
+            telemetryPath = mkOption {
+              type = types.str;
+              default = "/metrics";
+              description = ''
+                Path under which to expose metrics.
+              '';
+            };
+            logfilePath = mkOption {
+              type = types.path;
+              default = /var/log/postfix_exporter_input.log;
+              example = /var/log/mail.log;
+              description = ''
+                Path where Postfix writes log entries.
+                This file will be truncated by this exporter!
+              '';
+            };
+            showqPath = mkOption {
+              type = types.path;
+              default = /var/spool/postfix/public/showq;
+              example = /var/lib/postfix/queue/public/showq;
+              description = ''
+                Path at which Postfix places its showq socket.
+              '';
+            };
+          };
+
+          # `serviceOpts` is an attribute set which contains configuration
+          # for the exporter's systemd service. One of
+          # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
+          # has to be specified here. This will be merged with the default
+          # service confiuration.
+          serviceOpts = {
+            serviceConfig = {
+              ExecStart = ''
+                ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+                  --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+                  --web.telemetry-path ${cfg.telemetryPath} \
+                  ${concatStringsSep " \\\n  " cfg.extraFlags}
+              '';
+            };
+          };
+        }
+      </programlisting>
+    </para>
+  </listitem>
+  <listitem>
+    <para>
+      This should already be enough for the postfix exporter. Additionally one could
+      now add assertions and conditional default values. This can be done in the
+      'meta-module' that combines all exporter definitions and generates the submodules:
+      <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal>
+    </para>
+  </listitem>
+</itemizedlist>
+</section>
+</chapter>
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
new file mode 100644
index 000000000000..d09d1c4f3663
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.blackbox;
+in
+{
+  port = 9115;
+  extraOpts = {
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --config.file ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
new file mode 100644
index 000000000000..0eba3527162d
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
@@ -0,0 +1,78 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.collectd;
+in
+{
+  port = 9103;
+  extraOpts = {
+    collectdBinary = {
+      enable = mkEnableOption "collectd binary protocol receiver";
+
+      authFile = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = "File mapping user names to pre-shared keys (passwords).";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 25826;
+        description = ''Network address on which to accept collectd binary network packets.'';
+      };
+
+      listenAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          Address to listen on for binary network packets.
+          '';
+      };
+
+      securityLevel = mkOption {
+        type = types.enum ["None" "Sign" "Encrypt"];
+        default = "None";
+        description = ''
+          Minimum required security level for accepted packets.
+        '';
+      };
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    collectSettingsArgs = if (cfg.collectdBinary.enable) then ''
+      -collectd.listen-address ${cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \
+      -collectd.security-level ${cfg.collectdBinary.securityLevel} \
+    '' else "";
+  in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${collectSettingsArgs} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
new file mode 100644
index 000000000000..4ca6d4e5f8b6
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.dovecot;
+in
+{
+  port = 9166;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    socketPath = mkOption {
+      type = types.path;
+      default = "/var/run/dovecot/stats";
+      example = "/var/run/dovecot2/stats";
+      description = ''
+        Path under which the stats socket is placed.
+        The user/group under which the exporter runs,
+        should be able to access the socket in order
+        to scrape the metrics successfully.
+      '';
+    };
+    scopes = mkOption {
+      type = types.listOf types.str;
+      default = [ "user" ];
+      example = [ "user" "global" ];
+      description = ''
+        Stats scopes to query.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          --dovecot.socket-path ${cfg.socketPath} \
+          --dovecot.scopes ${concatStringsSep "," cfg.scopes} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
new file mode 100644
index 000000000000..a3f1d9d31323
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.fritzbox;
+in
+{
+  port = 9133;
+  extraOpts = {
+    gatewayAddress = mkOption {
+      type = types.str;
+      default = "fritz.box";
+      description = ''
+        The hostname or IP of the FRITZ!Box.
+      '';
+    };
+
+    gatewayPort = mkOption {
+      type = types.int;
+      default = 49000;
+      description = ''
+        The port of the FRITZ!Box UPnP service.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
+          -listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -gateway-address ${cfg.gatewayAddress} \
+          -gateway-port ${toString cfg.gatewayPort} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
new file mode 100644
index 000000000000..a5494e85e016
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.json;
+in
+{
+  port = 7979;
+  extraOpts = {
+    url = mkOption {
+      type = types.str;
+      description = ''
+        URL to scrape JSON from.
+      '';
+    };
+    configFile = mkOption {
+      type = types.path;
+      description = ''
+        Path to configuration file.
+      '';
+    };
+    listenAddress = {}; # not used
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
+          --port ${toString cfg.port} \
+          ${cfg.url} ${cfg.configFile} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
new file mode 100644
index 000000000000..3cc4ffdbc8fd
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.minio;
+in
+{
+  port = 9290;
+  extraOpts = {
+    minioAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:9000";
+      description = ''
+        The URL of the minio server.
+        Use HTTPS if Minio accepts secure connections only.
+        By default this connects to the local minio server if enabled.
+      '';
+    };
+
+    minioAccessKey = mkOption {
+      type = types.str;
+      example = "yourMinioAccessKey";
+      description = ''
+        The value of the Minio access key.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.accessKey</literal>.
+      '';
+    };
+
+    minioAccessSecret = mkOption {
+      type = types.str;
+      description = ''
+        The value of the Minio access secret.
+        It is required in order to connect to the server.
+        By default this uses the one from the local minio server if enabled
+        and <literal>config.services.minio.secretKey</literal>.
+      '';
+    };
+
+    minioBucketStats = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Collect statistics about the buckets and files in buckets.
+        It requires more computation, use it carefully in case of large buckets..
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          -minio.server ${cfg.minioAddress} \
+          -minio.access-key ${cfg.minioAccessKey} \
+          -minio.access-secret ${cfg.minioAccessSecret} \
+          ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
new file mode 100644
index 000000000000..6a3ba2d0457c
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nginx;
+in
+{
+  port = 9113;
+  extraOpts = {
+    scrapeUri = mkOption {
+      type = types.string;
+      default = "http://localhost/nginx_status";
+      description = ''
+        Address to access the nginx status page.
+        Can be enabled with services.nginx.statusPage = true.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
+          -nginx.scrape_uri '${cfg.scrapeUri}' \
+          -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
new file mode 100644
index 000000000000..c85f5f9cfb2d
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.node;
+in
+{
+  port = 9100;
+  extraOpts = {
+    enabledCollectors = mkOption {
+      type = types.listOf types.string;
+      default = [];
+      example = ''[ "systemd" ]'';
+      description = ''
+        Collectors to enable. The collectors listed here are enabled in addition to the default ones.
+      '';
+    };
+    disabledCollectors = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = ''[ "timex" ]'';
+      description = ''
+        Collectors to disable which are enabled by default.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-node-exporter}/bin/node_exporter \
+          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
+          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
new file mode 100644
index 000000000000..efe78ebcba86
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.postfix;
+in
+{
+  port = 9154;
+  extraOpts = {
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = ''
+        Path under which to expose metrics.
+      '';
+    };
+    logfilePath = mkOption {
+      type = types.path;
+      default = "/var/log/postfix_exporter_input.log";
+      example = "/var/log/mail.log";
+      description = ''
+        Path where Postfix writes log entries.
+        This file will be truncated by this exporter!
+      '';
+    };
+    showqPath = mkOption {
+      type = types.path;
+      default = "/var/spool/postfix/public/showq";
+      example = "/var/lib/postfix/queue/public/showq";
+      description = ''
+        Path where Postfix places it's showq socket.
+      '';
+    };
+    systemd = {
+      enable = mkEnableOption ''
+        reading metrics from the systemd-journal instead of from a logfile
+      '';
+      unit = mkOption {
+        type = types.str;
+        default = "postfix.service";
+        description = ''
+          Name of the postfix systemd unit.
+        '';
+      };
+      slice = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Name of the postfix systemd slice.
+          This overrides the <option>systemd.unit</option>.
+        '';
+      };
+      journalPath = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to the systemd journal.
+        '';
+      };
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path ${cfg.telemetryPath} \
+          --postfix.showq_path ${cfg.showqPath} \
+          ${concatStringsSep " \\\n  " (cfg.extraFlags
+          ++ optional cfg.systemd.enable "--systemd.enable"
+          ++ optional cfg.systemd.enable (if cfg.systemd.slice != null
+                                          then "--systemd.slice ${cfg.systemd.slice}"
+                                          else "--systemd.unit ${cfg.systemd.unit}")
+          ++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null))
+                       "--systemd.jounal_path ${cfg.systemd.journalPath}"
+          ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${cfg.logfilePath}")}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
new file mode 100644
index 000000000000..404cd0a1896b
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.snmp;
+in
+{
+  port = 9116;
+  extraOpts = {
+    configurationPath = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
+      '';
+      example = "./snmp.yml";
+    };
+
+    configuration = mkOption {
+      type = types.nullOr types.attrs;
+      default = {};
+      description = ''
+        Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
+      '';
+      example = ''
+        {
+          "default" = {
+            "version" = 2;
+            "auth" = {
+              "community" = "public";
+            };
+          };
+        };
+      '';
+    };
+
+    logFormat = mkOption {
+      type = types.str;
+      default = "logger:stderr";
+      description = ''
+        Set the log target and format.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" "fatal"];
+      default = "info";
+      description = ''
+        Only log messages with the given severity or above.
+      '';
+    };
+  };
+  serviceOpts = let
+    configFile = if cfg.configurationPath != null
+                 then cfg.configurationPath
+                 else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}";
+    in {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
+          -config.file ${configFile} \
+          -log.format ${cfg.logFormat} \
+          -log.level ${cfg.logLevel} \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
new file mode 100644
index 000000000000..011dcbe208e4
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.unifi;
+in
+{
+  port = 9130;
+  extraOpts = {
+    unifiAddress = mkOption {
+      type = types.str;
+      example = "https://10.0.0.1:8443";
+      description = ''
+        URL of the UniFi Controller API.
+      '';
+    };
+
+    unifiInsecure = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        If enabled skip the verification of the TLS certificate of the UniFi Controller API.
+        Use with caution.
+      '';
+    };
+
+    unifiUsername = mkOption {
+      type = types.str;
+      example = "ReadOnlyUser";
+      description = ''
+        username for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiPassword = mkOption {
+      type = types.str;
+      description = ''
+        Password for authentication against UniFi Controller API.
+      '';
+    };
+
+    unifiTimeout = mkOption {
+      type = types.str;
+      default = "5s";
+      example = "2m";
+      description = ''
+        Timeout including unit for UniFi Controller API requests.
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
+          -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \
+          -unifi.addr ${cfg.unifiAddress} \
+          -unifi.username ${cfg.unifiUsername} \
+          -unifi.password ${cfg.unifiPassword} \
+          -unifi.timeout ${cfg.unifiTimeout} \
+          ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
new file mode 100644
index 000000000000..b439a83e7aa2
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.varnish;
+in
+{
+  port = 9131;
+  serviceOpts = {
+    path = [ pkgs.varnish ];
+    serviceConfig = {
+      DynamicUser = true;
+      ExecStart = ''
+        ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
+          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
deleted file mode 100644
index 6da39b6519cb..000000000000
--- a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix
+++ /dev/null
@@ -1,76 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.fritzboxExporter;
-in {
-  options = {
-    services.prometheus.fritzboxExporter = {
-      enable = mkEnableOption "prometheus fritzbox exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9133;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      gatewayAddress = mkOption {
-        type = types.str;
-        default = "fritz.box";
-        description = ''
-          The hostname or IP of the FRITZ!Box.
-        '';
-      };
-
-      gatewayPort = mkOption {
-        type = types.int;
-        default = 49000;
-        description = ''
-          The port of the FRITZ!Box UPnP service.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the fritzbox exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-fritzbox-exporter = {
-      description = "Prometheus exporter for FRITZ!Box via UPnP";
-      unitConfig.Documentation = "https://github.com/ndecker/fritzbox_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
-            -listen-address :${toString cfg.port} \
-            -gateway-address ${cfg.gatewayAddress} \
-            -gateway-port ${toString cfg.gatewayPort} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix
deleted file mode 100644
index 6bc56df9834b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/json-exporter.nix
+++ /dev/null
@@ -1,74 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.jsonExporter;
-in {
-  options = {
-    services.prometheus.jsonExporter = {
-      enable = mkEnableOption "prometheus JSON exporter";
-
-      url = mkOption {
-        type = types.str;
-        description = ''
-          URL to scrape JSON from.
-        '';
-      };
-
-      configFile = mkOption {
-        type = types.path;
-        description = ''
-          Path to configuration file.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 7979;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the JSON exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-json-exporter = {
-      description = "Prometheus exporter for JSON over HTTP";
-      unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \
-            --port ${toString cfg.port} \
-            ${cfg.url} ${cfg.configFile} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix b/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
deleted file mode 100644
index 4314671523cf..000000000000
--- a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix
+++ /dev/null
@@ -1,117 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.minioExporter;
-in {
-  options = {
-    services.prometheus.minioExporter = {
-      enable = mkEnableOption "prometheus minio exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9290;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "0.0.0.0";
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      minioAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:9000";
-        default = if config.services.minio.enable then "http://localhost:9000" else null;
-        description = ''
-          The URL of the minio server.
-          Use HTTPS if Minio accepts secure connections only.
-          By default this connects to the local minio server if enabled.
-        '';
-      };
-
-      minioAccessKey = mkOption ({
-        type = types.str;
-        example = "BKIKJAA5BMMU2RHO6IBB";
-        description = ''
-          The value of the Minio access key.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.accessKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.accessKey != "") {
-        default = config.services.minio.accessKey;
-      });
-
-      minioAccessSecret = mkOption ({
-        type = types.str;
-        description = ''
-          The calue of the Minio access secret.
-          It is required in order to connect to the server.
-          By default this uses the one from the local minio server if enabled
-          and <literal>config.services.minio.secretKey</literal>.
-        '';
-      } // optionalAttrs (config.services.minio.enable && config.services.minio.secretKey != "") {
-        default = config.services.minio.secretKey;
-      });
-
-      minioBucketStats = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Collect statistics about the buckets and files in buckets.
-          It requires more computation, use it carefully in case of large buckets..
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the minio exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-minio-exporter = {
-      description = "Prometheus exporter for Minio server metrics";
-      unitConfig.Documentation = "https://github.com/joe-pll/minio-exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.minio.enable "minio.service";
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-            -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \
-            -minio.server ${cfg.minioAddress} \
-            -minio.access-key ${cfg.minioAccessKey} \
-            -minio.access-secret ${cfg.minioAccessSecret} \
-            ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
deleted file mode 100644
index 1ccafee3b18b..000000000000
--- a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nginxExporter;
-in {
-  options = {
-    services.prometheus.nginxExporter = {
-      enable = mkEnableOption "prometheus nginx exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9113;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      scrapeUri = mkOption {
-        type = types.string;
-        default = "http://localhost/nginx_status";
-        description = ''
-          Address to access the nginx status page.
-          Can be enabled with services.nginx.statusPage = true.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the nginx exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-nginx-exporter = {
-      after = [ "network.target" "nginx.service" ];
-      description = "Prometheus exporter for nginx metrics";
-      unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter";
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        User = "nobody";
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \
-            -nginx.scrape_uri '${cfg.scrapeUri}' \
-            -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix
deleted file mode 100644
index bad4389ce799..000000000000
--- a/nixos/modules/services/monitoring/prometheus/node-exporter.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.nodeExporter;
-in {
-  options = {
-    services.prometheus.nodeExporter = {
-      enable = mkEnableOption "prometheus node exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9100;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.string;
-        default = "0.0.0.0";
-        description = ''
-          Address to listen on.
-        '';
-      };
-
-      enabledCollectors = mkOption {
-        type = types.listOf types.string;
-        default = [];
-        example = ''[ "systemd" ]'';
-        description = ''
-          Collectors to enable. The collectors listed here are enabled in addition to the default ones.
-        '';
-      };
-
-      disabledCollectors = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = ''[ "timex" ]'';
-        description = ''
-          Collectors to disable which are enabled by default.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the node exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-node-exporter = {
-      description = "Prometheus exporter for machine metrics";
-      unitConfig.Documentation = "https://github.com/prometheus/node_exporter";
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \
-          ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \
-          ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \
-          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
deleted file mode 100644
index fe33f8c1f04d..000000000000
--- a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.snmpExporter;
-  mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath);
-in {
-  options = {
-    services.prometheus.snmpExporter = {
-      enable = mkEnableOption "Prometheus snmp exporter";
-
-      user = mkOption {
-        type = types.str;
-        default = "nobody";
-        description = ''
-          User name under which snmp exporter shall be run.
-        '';
-      };
-
-      group = mkOption {
-        type = types.str;
-        default = "nogroup";
-        description = ''
-          Group under which snmp exporter shall be run.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 9116;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      listenAddress = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Address to listen on for web interface and telemetry.
-        '';
-      };
-
-      configurationPath = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = ''
-          Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option.
-        '';
-        example = "./snmp.yml";
-      };
-
-      configuration = mkOption {
-        type = types.nullOr types.attrs;
-        default = {};
-        description = ''
-          Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option.
-        '';
-        example = ''
-          {
-            "default" = {
-              "version" = 2;
-              "auth" = {
-                "community" = "public";
-              };
-            };
-          };
-        '';
-      };
-
-      logFormat = mkOption {
-        type = types.str;
-        default = "logger:stderr";
-        description = ''
-          Set the log target and format.
-        '';
-      };
-
-      logLevel = mkOption {
-        type = types.enum ["debug" "info" "warn" "error" "fatal"];
-        default = "info";
-        description = ''
-          Only log messages with the given severity or above.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    assertions = singleton
-      {
-        assertion = (cfg.configurationPath == null) != (cfg.configuration == null);
-        message = "Please ensure you have either 'configuration' or 'configurationPath' set!";
-      };
-
-    systemd.services.prometheus-snmp-exporter = {
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      script = ''
-        ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \
-          -config.file ${mkConfigFile} \
-          -log.format ${cfg.logFormat} \
-          -log.level ${cfg.logLevel} \
-          -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port}
-      '';
-
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-        Restart  = "always";
-        PrivateTmp = true;
-        WorkingDirectory = "/tmp";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
deleted file mode 100644
index 0a56d6ae95a5..000000000000
--- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-
-let
-  cfg = config.services.prometheus.unifiExporter;
-in {
-  options = {
-    services.prometheus.unifiExporter = {
-      enable = mkEnableOption "prometheus unifi exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9130;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      unifiAddress = mkOption {
-        type = types.str;
-        example = "https://10.0.0.1:8443";
-        description = ''
-          URL of the UniFi Controller API.
-        '';
-      };
-
-      unifiInsecure = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          If enabled skip the verification of the TLS certificate of the UniFi Controller API.
-          Use with caution.
-        '';
-      };
-      
-      unifiUsername = mkOption {
-        type = types.str;
-        example = "ReadOnlyUser";
-        description = ''
-          username for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiPassword = mkOption {
-        type = types.str;
-        description = ''
-          Password for authentication against UniFi Controller API.
-        '';
-      };
-      
-      unifiTimeout = mkOption {
-        type = types.str;
-        default = "5s";
-        example = "2m";
-        description = ''
-          Timeout including unit for UniFi Controller API requests.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the unifi exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-unifi-exporter = {
-      description = "Prometheus exporter for UniFi Controller metrics";
-      unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter";
-      wantedBy = [ "multi-user.target" ];
-      after = optional config.services.unifi.enable "unifi.service";
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecStart = ''
-          ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \
-            -telemetry.addr :${toString cfg.port} \
-            -unifi.addr ${cfg.unifiAddress} \
-            -unifi.username ${cfg.unifiUsername} \
-            -unifi.password ${cfg.unifiPassword} \
-            -unifi.timeout ${cfg.unifiTimeout} \
-            ${optionalString cfg.unifiInsecure "-unifi.insecure" } \
-            ${concatStringsSep " \\\n  " cfg.extraFlags}
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
deleted file mode 100644
index 143ebb62aeac..000000000000
--- a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix
+++ /dev/null
@@ -1,61 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-# Shamelessly cribbed from nginx-exporter.nix. ~ C.
-with lib;
-
-let
-  cfg = config.services.prometheus.varnishExporter;
-in {
-  options = {
-    services.prometheus.varnishExporter = {
-      enable = mkEnableOption "prometheus Varnish exporter";
-
-      port = mkOption {
-        type = types.int;
-        default = 9131;
-        description = ''
-          Port to listen on.
-        '';
-      };
-
-      extraFlags = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        description = ''
-          Extra commandline options when launching the Varnish exporter.
-        '';
-      };
-
-      openFirewall = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Open port in firewall for incoming connections.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
-
-    systemd.services.prometheus-varnish-exporter = {
-      description = "Prometheus exporter for Varnish metrics";
-      unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter";
-      wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.varnish ];
-      script = ''
-        exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \
-          -web.listen-address :${toString cfg.port} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-      serviceConfig = {
-        User = "nobody";
-        Restart = "always";
-        PrivateTmp = true;
-        WorkingDirectory = /tmp;
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 1d6940c516a9..fecae4ca1b36 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -14,7 +14,7 @@ let
   nx = cfg.notifications.x11;
 
   smartdNotify = pkgs.writeScript "smartd-notify.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     ${optionalString nm.enable ''
       {
       ${pkgs.coreutils}/bin/cat << EOF
@@ -64,7 +64,7 @@ let
        "DEVICESCAN ${notifyOpts}${cfg.defaults.autodetected}"}
   '';
 
-  smartdOpts = { name, ... }: {
+  smartdDeviceOpts = { name, ... }: {
 
     options = {
 
@@ -108,6 +108,18 @@ in
         '';
       };
 
+      extraOptions = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        example = ["-A /var/log/smartd/" "--interval=3600"];
+        description = ''
+          Extra command-line options passed to the <literal>smartd</literal>
+          daemon on startup.
+
+          (See <literal>man 8 smartd</literal>.)
+        '';
+      };
+
       notifications = {
 
         mail = {
@@ -197,7 +209,7 @@ in
       devices = mkOption {
         default = [];
         example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
-        type = with types; listOf (submodule smartdOpts);
+        type = with types; listOf (submodule smartdDeviceOpts);
         description = "List of devices to monitor.";
       };
 
@@ -222,7 +234,7 @@ in
 
       path = [ pkgs.nettools ]; # for hostname and dnsdomanname calls in smartd
 
-      serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd --no-fork --configfile=${smartdConf}";
+      serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd ${lib.concatStringsSep " " cfg.extraOptions} --no-fork --configfile=${smartdConf}";
     };
 
   };
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
index df2adb9f2766..7b0e9981cbb1 100644
--- a/nixos/modules/services/monitoring/statsd.nix
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -9,6 +9,12 @@ let
   isBuiltinBackend = name:
     builtins.elem name [ "graphite" "console" "repeater" ];
 
+  backendsToPackages = let
+    mkMap = list: name:
+      if isBuiltinBackend name then list
+      else list ++ [ pkgs.nodePackages.${name} ];
+  in foldl mkMap [];
+
   configFile = pkgs.writeText "statsd.conf" ''
     {
       address: "${cfg.listenAddress}",
@@ -27,13 +33,21 @@ let
         prettyprint: false
       },
       log: {
-        backend: "syslog"
+        backend: "stdout"
       },
       automaticConfigReload: false${optionalString (cfg.extraConfig != null) ","}
       ${cfg.extraConfig}
     }
   '';
 
+  deps = pkgs.buildEnv {
+    name = "statsd-runtime-deps";
+    pathsToLink = [ "/lib" ];
+    ignoreCollisions = true;
+
+    paths = backendsToPackages cfg.backends;
+  };
+
 in
 
 {
@@ -42,11 +56,7 @@ in
 
   options.services.statsd = {
 
-    enable = mkOption {
-      description = "Whether to enable statsd stats aggregation service";
-      default = false;
-      type = types.bool;
-    };
+    enable = mkEnableOption "statsd";
 
     listenAddress = mkOption {
       description = "Address that statsd listens on over UDP";
@@ -110,6 +120,11 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = map (backend: {
+      assertion = !isBuiltinBackend backend -> hasAttrByPath [ backend ] pkgs.nodePackages;
+      message = "Only builtin backends (graphite, console, repeater) or backends enumerated in `pkgs.nodePackages` are allowed!";
+    }) cfg.backends;
+
     users.extraUsers = singleton {
       name = "statsd";
       uid = config.ids.uids.statsd;
@@ -120,9 +135,7 @@ in
       description = "Statsd Server";
       wantedBy = [ "multi-user.target" ];
       environment = {
-        NODE_PATH=concatMapStringsSep ":"
-          (pkg: "${builtins.getAttr pkg pkgs.statsd.nodePackages}/lib/node_modules")
-          (filter (name: !isBuiltinBackend name) cfg.backends);
+        NODE_PATH = "${deps}/lib/node_modules";
       };
       serviceConfig = {
         ExecStart = "${pkgs.statsd}/bin/statsd ${configFile}";
diff --git a/nixos/modules/services/network-filesystems/beegfs.nix b/nixos/modules/services/network-filesystems/beegfs.nix
new file mode 100644
index 000000000000..a6a2ec6cbc36
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/beegfs.nix
@@ -0,0 +1,343 @@
+{ config, lib, pkgs, ...} :
+
+with lib;
+
+let
+  cfg = config.services.beegfs;
+
+  # functions for the generations of config files
+
+  configMgmtd = name: cfg: pkgs.writeText "mgmt-${name}.conf" ''
+    storeMgmtdDirectory = ${cfg.mgmtd.storeDir}
+    storeAllowFirstRunInit = false
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.mgmtd.extraConfig}
+  '';
+
+  configAdmon = name: cfg: pkgs.writeText "admon-${name}.conf" ''
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.admon.extraConfig}
+  '';
+
+  configMeta = name: cfg: pkgs.writeText "meta-${name}.conf" ''
+    storeMetaDirectory = ${cfg.meta.storeDir}
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+    storeAllowFirstRunInit = false
+
+    ${cfg.mgmtd.extraConfig}
+  '';
+
+  configStorage = name: cfg: pkgs.writeText "storage-${name}.conf" ''
+    storeStorageDirectory = ${cfg.storage.storeDir}
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+    storeAllowFirstRunInit = false
+
+    ${cfg.storage.extraConfig}
+  '';
+
+  configHelperd = name: cfg: pkgs.writeText "helperd-${name}.conf" ''
+    connAuthFile = ${cfg.connAuthFile}
+    ${cfg.helperd.extraConfig}
+  '';
+
+  configClientFilename = name : "/etc/beegfs/client-${name}.conf";
+
+  configClient = name: cfg: ''
+    sysMgmtdHost = ${cfg.mgmtdHost}
+    connAuthFile = ${cfg.connAuthFile}
+    connPortShift = ${toString cfg.connPortShift}
+
+    ${cfg.client.extraConfig}
+  '';
+
+  serviceList = [
+    { service = "admon"; cfgFile = configAdmon; }
+    { service = "meta"; cfgFile = configMeta; }
+    { service = "mgmtd"; cfgFile = configMgmtd; }
+    { service = "storage"; cfgFile = configStorage; }
+  ];
+
+  # functions to generate systemd.service entries
+
+  systemdEntry = service: cfgFile: (mapAttrs' ( name: cfg:
+    (nameValuePair "beegfs-${service}-${name}" (mkIf cfg."${service}".enable {
+    wantedBy = [ "multi-user.target" ];
+    requires = [ "network-online.target" ];
+    after = [ "network-online.target" ];
+    serviceConfig = rec {
+      ExecStart = ''
+        ${pkgs.beegfs}/bin/beegfs-${service} \
+          cfgFile=${cfgFile name cfg} \
+          pidFile=${PIDFile}
+      '';
+      PIDFile = "/run/beegfs-${service}-${name}.pid";
+      TimeoutStopSec = "300";
+    };
+  }))) cfg);
+
+  systemdHelperd =  mapAttrs' ( name: cfg:
+    (nameValuePair "beegfs-helperd-${name}" (mkIf cfg.client.enable {
+    wantedBy = [ "multi-user.target" ];
+    requires = [ "network-online.target" ];
+    after = [ "network-online.target" ];
+    serviceConfig = rec {
+      ExecStart = ''
+        ${pkgs.beegfs}/bin/beegfs-helperd \
+          cfgFile=${configHelperd name cfg} \
+          pidFile=${PIDFile}
+      '';
+      PIDFile = "/run/beegfs-helperd-${name}.pid";
+      TimeoutStopSec = "300";
+    };
+   }))) cfg;
+
+  # wrappers to beegfs tools. Avoid typing path of config files
+  utilWrappers = mapAttrsToList ( name: cfg:
+      ( pkgs.runCommand "beegfs-utils-${name}" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
+        mkdir -p $out/bin
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-check-servers \
+                    $out/bin/beegfs-check-servers-${name} \
+                    --add-flags "-c ${configClientFilename name}" \
+                    --prefix PATH : ${lib.makeBinPath [ pkgs.beegfs ]}
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
+                    $out/bin/beegfs-ctl-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}"
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
+                    $out/bin/beegfs-df-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}" \
+                    --add-flags --listtargets  \
+                    --add-flags --hidenodeid \
+                    --add-flags --pools \
+                    --add-flags --spaceinfo
+
+        makeWrapper ${pkgs.beegfs}/bin/beegfs-fsck \
+                    $out/bin/beegfs-fsck-${name} \
+                    --add-flags "--cfgFile=${configClientFilename name}"
+      ''
+     )) cfg;
+in
+{
+  ###### interface
+
+  options = {
+    services.beegfsEnable = mkEnableOption "BeeGFS";
+
+    services.beegfs = mkOption {
+      default = {};
+      description = ''
+        BeeGFS configurations. Every mount point requires a separate configuration.
+      '';
+      type = with types; attrsOf (submodule ({ config, ... } : {
+        options = {
+          mgmtdHost = mkOption {
+            type = types.str;
+            default = null;
+            example = "master";
+            description = ''Hostname of managament host.'';
+          };
+
+          connAuthFile = mkOption {
+            type = types.str;
+            default = "";
+            example = "/etc/my.key";
+            description = "File containing shared secret authentication.";
+          };
+
+          connPortShift = mkOption {
+            type = types.int;
+            default = 0;
+            example = 5;
+            description = ''
+              For each additional beegfs configuration shift all
+              service TCP/UDP ports by at least 5.
+            '';
+          };
+
+          client = {
+            enable = mkEnableOption "BeeGFS client";
+
+            mount = mkOption {
+              type = types.bool;
+              default = true;
+              description = "Create fstab entry automatically";
+            };
+
+            mountPoint = mkOption {
+              type = types.str;
+              default = "/run/beegfs";
+              description = ''
+                Mount point under which the beegfs filesytem should be mounted.
+                If mounted manually the mount option specifing the config file is needed:
+                cfgFile=/etc/beegfs/beegfs-client-&lt;name&gt;.conf
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-client.conf.
+                See documentation for further details.
+             '';
+            };
+          };
+
+          helperd = {
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-helperd.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          mgmtd = {
+            enable = mkEnableOption "BeeGFS mgmtd daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-mgmtd";
+              description = ''
+                Data directory for mgmtd.
+                Must not be shared with other beegfs daemons.
+                This directory must exist and it must be initialized
+                with beegfs-setup-mgmtd, e.g. "beegfs-setup-mgmtd -C -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-mgmtd.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          admon = {
+            enable = mkEnableOption "BeeGFS admon daemon";
+
+            extraConfig = mkOption {
+              type = types.lines;
+              default = "";
+              description = ''
+                Additional lines for beegfs-admon.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          meta = {
+            enable = mkEnableOption "BeeGFS meta data daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-meta";
+              description = ''
+                Data directory for meta data service.
+                Must not be shared with other beegfs daemons.
+                The underlying filesystem must be mounted with xattr turned on.
+                This directory must exist and it must be initialized
+                with beegfs-setup-meta, e.g.
+                "beegfs-setup-meta -C -s &lt;serviceID&gt; -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Additional lines for beegfs-meta.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+
+          storage = {
+            enable = mkEnableOption "BeeGFS storage daemon";
+
+            storeDir = mkOption {
+              type = types.path;
+              default = null;
+              example = "/data/beegfs-storage";
+              description = ''
+                Data directories for storage service.
+                Must not be shared with other beegfs daemons.
+                The underlying filesystem must be mounted with xattr turned on.
+                This directory must exist and it must be initialized
+                with beegfs-setup-storage, e.g.
+                "beegfs-setup-storage -C -s &lt;serviceID&gt; -i &lt;storageTargetID&gt; -p &lt;storeDir&gt;"
+              '';
+            };
+
+            extraConfig = mkOption {
+              type = types.str;
+              default = "";
+              description = ''
+                Addional lines for beegfs-storage.conf. See documentation
+                for further details.
+              '';
+            };
+          };
+        };
+      }));
+    };
+  };
+
+  ###### implementation
+
+  config =
+    mkIf config.services.beegfsEnable {
+
+    environment.systemPackages = utilWrappers;
+
+    # Put the client.conf files in /etc since they are needed
+    # by the commandline tools
+    environment.etc = mapAttrs' ( name: cfg:
+      (nameValuePair "beegfs/client-${name}.conf" (mkIf (cfg.client.enable)
+    {
+      enable = true;
+      text = configClient name cfg;
+    }))) cfg;
+
+    # Kernel module, we need it only once per host.
+    boot = mkIf (
+      foldr (a: b: a || b) false
+        (map (x: x.client.enable) (collect (x: x ? client) cfg)))
+    {
+      kernelModules = [ "beegfs" ];
+      extraModulePackages = [ pkgs.linuxPackages.beegfs-module ];
+    };
+
+    # generate fstab entries
+    fileSystems = mapAttrs' (name: cfg:
+      (nameValuePair cfg.client.mountPoint (optionalAttrs cfg.client.mount (mkIf cfg.client.enable {
+      device = "beegfs_nodev";
+      fsType = "beegfs";
+      mountPoint = cfg.client.mountPoint;
+      options = [ "cfgFile=${configClientFilename name}" "_netdev" ];
+    })))) cfg;
+
+    # generate systemd services
+    systemd.services = systemdHelperd //
+      foldr (a: b: a // b) {}
+        (map (x: systemdEntry x.service x.cfgFile) serviceList);
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/ceph.nix b/nixos/modules/services/network-filesystems/ceph.nix
new file mode 100644
index 000000000000..5de8ae79a246
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/ceph.nix
@@ -0,0 +1,371 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  ceph = pkgs.ceph;
+  cfg  = config.services.ceph;
+  # function that translates "camelCaseOptions" to "camel case options", credits to tilpner in #nixos@freenode
+  translateOption = replaceStrings upperChars (map (s: " ${s}") lowerChars);
+  generateDaemonList = (daemonType: daemons: extraServiceConfig:
+    mkMerge (
+      map (daemon: 
+        { "ceph-${daemonType}-${daemon}" = generateServiceFile daemonType daemon cfg.global.clusterName ceph extraServiceConfig; }
+      ) daemons
+    )
+  );
+  generateServiceFile = (daemonType: daemonId: clusterName: ceph: extraServiceConfig: {
+    enable = true;
+    description = "Ceph ${builtins.replaceStrings lowerChars upperChars daemonType} daemon ${daemonId}";
+    after = [ "network-online.target" "local-fs.target" "time-sync.target" ] ++ optional (daemonType == "osd") "ceph-mon.target";
+    wants = [ "network-online.target" "local-fs.target" "time-sync.target" ];
+    partOf = [ "ceph-${daemonType}.target" ];
+    wantedBy = [ "ceph-${daemonType}.target" ];
+
+    serviceConfig = {
+      LimitNOFILE = 1048576;
+      LimitNPROC = 1048576;
+      Environment = "CLUSTER=${clusterName}";
+      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      PrivateDevices = "yes";
+      PrivateTmp = "true";
+      ProtectHome = "true";
+      ProtectSystem = "full";
+      Restart = "on-failure";
+      StartLimitBurst = "5";
+      StartLimitInterval = "30min";
+      ExecStart = "${ceph.out}/bin/${if daemonType == "rgw" then "radosgw" else "ceph-${daemonType}"} -f --cluster ${clusterName} --id ${if daemonType == "rgw" then "client.${daemonId}" else daemonId} --setuser ceph --setgroup ceph";
+    } // extraServiceConfig
+      // optionalAttrs (daemonType == "osd") { ExecStartPre = "${ceph.out}/libexec/ceph/ceph-osd-prestart.sh --id ${daemonId} --cluster ${clusterName}"; };
+    } // optionalAttrs (builtins.elem daemonType [ "mds" "mon" "rgw" "mgr" ]) { preStart = ''
+        daemonPath="/var/lib/ceph/${if daemonType == "rgw" then "radosgw" else daemonType}/${clusterName}-${daemonId}"
+        if [ ! -d ''$daemonPath ]; then
+          mkdir -m 755 -p ''$daemonPath
+          chown -R ceph:ceph ''$daemonPath 
+        fi
+      '';
+    } // optionalAttrs (daemonType == "osd") { path = [ pkgs.getopt ]; }
+  );
+  generateTargetFile = (daemonType:
+    {
+      "ceph-${daemonType}" = {
+        description = "Ceph target allowing to start/stop all ceph-${daemonType} services at once";
+        partOf = [ "ceph.target" ];
+        before = [ "ceph.target" ];
+      };
+    }
+  );
+in 
+{
+  options.services.ceph = {
+    # Ceph has a monolithic configuration file but different sections for
+    # each daemon, a separate client section and a global section
+    enable = mkEnableOption "Ceph global configuration";
+
+    global = {
+      fsid = mkOption {
+        type = types.str;
+        example = ''
+          433a2193-4f8a-47a0-95d2-209d7ca2cca5
+        '';
+        description = ''
+          Filesystem ID, a generated uuid, its must be generated and set before
+          attempting to start a cluster
+        '';
+      };
+
+      clusterName = mkOption {
+        type = types.str;
+        default = "ceph";
+        description = ''
+          Name of cluster
+        '';
+      };
+
+      monInitialMembers = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          node0, node1, node2 
+        '';
+        description = ''
+          List of hosts that will be used as monitors at startup.
+        '';
+      };
+
+      monHost = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.10.0.1, 10.10.0.2, 10.10.0.3
+        '';
+        description = ''
+          List of hostname shortnames/IP addresses of the initial monitors.
+        '';
+      };
+
+      maxOpenFiles = mkOption {
+        type = types.int;
+        default = 131072;
+        description = ''
+          Max open files for each OSD daemon.
+        '';
+      };
+
+      authClusterRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring daemons to authenticate with eachother in the cluster.
+        '';
+      };
+
+      authServiceRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring clients to authenticate with the cluster to access services in the cluster (e.g. radosgw, mds or osd).
+        '';
+      };
+
+      authClientRequired = mkOption {
+        type = types.enum [ "cephx" "none" ];
+        default = "cephx";
+        description = ''
+          Enables requiring the cluster to authenticate itself to the client.
+        '';
+      };
+
+      publicNetwork = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.20.0.0/24, 192.168.1.0/24
+        '';
+        description = ''
+          A comma-separated list of subnets that will be used as public networks in the cluster.
+        '';
+      };
+
+      clusterNetwork = mkOption {
+        type = with types; nullOr commas;
+        default = null;
+        example = ''
+          10.10.0.0/24, 192.168.0.0/24
+        '';
+        description = ''
+          A comma-separated list of subnets that will be used as cluster networks in the cluster.
+        '';
+      };
+    };
+
+    mgr = {
+      enable = mkEnableOption "Ceph MGR daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of names for manager daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mgr.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the global section for manager daemons.
+        '';
+      };
+    };
+
+    mon = {
+      enable = mkEnableOption "Ceph MON daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of monitor daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mon.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the monitor section.
+        '';
+      };
+    };
+
+    osd = {
+      enable = mkEnableOption "Ceph OSD daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of OSD daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in osd.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {
+          "osd journal size" = "10000";
+          "osd pool default size" = "3";
+          "osd pool default min size" = "2";
+          "osd pool default pg num" = "200";
+          "osd pool default pgp num" = "200";
+          "osd crush chooseleaf type" = "1";
+        };
+        description = ''
+          Extra configuration to add to the OSD section.
+        '';
+      };
+    };
+
+    mds = {
+      enable = mkEnableOption "Ceph MDS daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of metadata service daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in mds.name1
+        '';
+      };
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = ''
+          Extra configuration to add to the MDS section.
+        '';
+      };
+    };
+
+    rgw = {
+      enable = mkEnableOption "Ceph RadosGW daemon";
+      daemons = mkOption {
+        type = with types; listOf str;
+        default = [];
+        example = ''
+          [ "name1" "name2" ];
+        '';
+        description = ''
+          A list of rados gateway daemons that should have a service created. The names correspond
+          to the id part in ceph i.e. [ "name1" ] would result in client.name1, radosgw daemons
+          aren't daemons to cluster in the sense that OSD, MGR or MON daemons are. They are simply
+          daemons, from ceph, that uses the cluster as a backend.
+        '';
+      };
+    };
+
+    client = {
+      enable = mkEnableOption "Ceph client configuration";
+      extraConfig = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        example = ''
+          {
+            # This would create a section for a radosgw daemon named node0 and related
+            # configuration for it
+            "client.radosgw.node0" = { "some config option" = "true"; };
+          };
+        '';
+        description = ''
+          Extra configuration to add to the client section. Configuration for rados gateways
+          would be added here, with their own sections, see example.
+        '';
+      };
+    };
+  };
+
+  config = mkIf config.services.ceph.enable {
+    assertions = [
+      { assertion = cfg.global.fsid != "";
+        message = "fsid has to be set to a valid uuid for the cluster to function";
+      }
+      { assertion = cfg.mgr.enable == true;
+        message = "ceph 12.x requires atleast 1 MGR daemon enabled for the cluster to function";
+      }
+      { assertion = cfg.mon.enable == true -> cfg.mon.daemons != [];
+        message = "have to set id of atleast one MON if you're going to enable Monitor";
+      }
+      { assertion = cfg.mds.enable == true -> cfg.mds.daemons != [];
+        message = "have to set id of atleast one MDS if you're going to enable Metadata Service";
+      }
+      { assertion = cfg.osd.enable == true -> cfg.osd.daemons != [];
+        message = "have to set id of atleast one OSD if you're going to enable OSD";
+      }
+      { assertion = cfg.mgr.enable == true -> cfg.mgr.daemons != [];
+        message = "have to set id of atleast one MGR if you're going to enable MGR";
+      }
+    ];
+
+    warnings = optional (cfg.global.monInitialMembers == null) 
+      ''Not setting up a list of members in monInitialMembers requires that you set the host variable for each mon daemon or else the cluster won't function'';
+    
+    environment.etc."ceph/ceph.conf".text = let
+      # Translate camelCaseOptions to the expected camel case option for ceph.conf
+      translatedGlobalConfig = mapAttrs' (name: value: nameValuePair (translateOption name) value) cfg.global;
+      # Merge the extraConfig set for mgr daemons, as mgr don't have their own section
+      globalAndMgrConfig = translatedGlobalConfig // optionalAttrs cfg.mgr.enable cfg.mgr.extraConfig;
+      # Remove all name-value pairs with null values from the attribute set to avoid making empty sections in the ceph.conf
+      globalConfig = mapAttrs' (name: value: nameValuePair (translateOption name) value) (filterAttrs (name: value: value != null) globalAndMgrConfig);
+      totalConfig = {
+          "global" = globalConfig;
+        } // optionalAttrs (cfg.mon.enable && cfg.mon.extraConfig != {}) { "mon" = cfg.mon.extraConfig; }
+          // optionalAttrs (cfg.mds.enable && cfg.mds.extraConfig != {}) { "mds" = cfg.mds.extraConfig; }
+          // optionalAttrs (cfg.osd.enable && cfg.osd.extraConfig != {}) { "osd" = cfg.osd.extraConfig; }
+          // optionalAttrs (cfg.client.enable && cfg.client.extraConfig != {})  cfg.client.extraConfig;
+      in
+        generators.toINI {} totalConfig;
+
+    users.extraUsers = singleton {
+      name = "ceph";
+      uid = config.ids.uids.ceph;
+      description = "Ceph daemon user";
+    };
+
+    users.extraGroups = singleton {
+      name = "ceph";
+      gid = config.ids.gids.ceph;
+    };
+
+    systemd.services = let
+      services = [] 
+        ++ optional cfg.mon.enable (generateDaemonList "mon" cfg.mon.daemons { RestartSec = "10"; }) 
+        ++ optional cfg.mds.enable (generateDaemonList "mds" cfg.mds.daemons { StartLimitBurst = "3"; })
+        ++ optional cfg.osd.enable (generateDaemonList "osd" cfg.osd.daemons { StartLimitBurst = "30"; RestartSec = "20s"; })
+        ++ optional cfg.rgw.enable (generateDaemonList "rgw" cfg.rgw.daemons { })
+        ++ optional cfg.mgr.enable (generateDaemonList "mgr" cfg.mgr.daemons { StartLimitBurst = "3"; });
+      in 
+        mkMerge services;
+
+    systemd.targets = let
+      targets = [
+        { "ceph" = { description = "Ceph target allowing to start/stop all ceph service instances at once"; }; }
+      ] ++ optional cfg.mon.enable (generateTargetFile "mon")
+        ++ optional cfg.mds.enable (generateTargetFile "mds")
+        ++ optional cfg.osd.enable (generateTargetFile "osd")
+        ++ optional cfg.rgw.enable (generateTargetFile "rgw")
+        ++ optional cfg.mgr.enable (generateTargetFile "mgr");
+      in
+        mkMerge targets;
+
+    systemd.tmpfiles.rules = [
+      "d /run/ceph 0770 ceph ceph -"
+    ];
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/davfs2.nix b/nixos/modules/services/network-filesystems/davfs2.nix
new file mode 100644
index 000000000000..6b2a770100c5
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/davfs2.nix
@@ -0,0 +1,74 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.davfs2;
+  cfgFile = pkgs.writeText "davfs2.conf" ''
+    dav_user ${cfg.davUser}
+    dav_group ${cfg.davGroup}
+    ${cfg.extraConfig}
+  '';
+in
+{
+  options.services.davfs2 = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable davfs2.
+      '';
+    };
+
+    davUser = mkOption {
+      type = types.string;
+      default = "davfs2";
+      description = ''
+        When invoked by root the mount.davfs daemon will run as this user.
+        Value must be given as name, not as numerical id.
+      '';
+    };
+
+    davGroup = mkOption {
+      type = types.string;
+      default = "davfs2";
+      description = ''
+        The group of the running mount.davfs daemon. Ordinary users must be
+        member of this group in order to mount a davfs2 file system. Value must
+        be given as name, not as numerical id.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        kernel_fs coda
+        proxy foo.bar:8080
+        use_locks 0
+      '';
+      description = ''
+        Extra lines appended to the configuration of davfs2.
+      ''  ;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.davfs2 ];
+    environment.etc."davfs2/davfs2.conf".source = cfgFile;
+
+    users.extraGroups = optionalAttrs (cfg.davGroup == "davfs2") (singleton {
+      name = "davfs2";
+      gid = config.ids.gids.davfs2;
+    });
+
+    users.extraUsers = optionalAttrs (cfg.davUser == "davfs2") (singleton {
+      name = "davfs2";
+      createHome = false;
+      group = cfg.davGroup;
+      uid = config.ids.uids.davfs2;
+      description = "davfs2 user";
+    });
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index d4a695ef5880..39a4fd6beff8 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -39,8 +39,6 @@ let
     # NB: migration must be performed prior to pre-start, else we get the failure message!
     preStart = ''
       ipfs repo fsck # workaround for BUG #4212 (https://github.com/ipfs/go-ipfs/issues/4214)
-      ipfs --local config Addresses.API ${cfg.apiAddress}
-      ipfs --local config Addresses.Gateway ${cfg.gatewayAddress}
     '' + optionalString cfg.autoMount ''
       ipfs --local config Mounts.FuseAllowOther --json true
       ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir}
@@ -56,7 +54,11 @@ let
               EOF
               ipfs --local config --json "${concatStringsSep "." path}" "$value"
             '')
-            cfg.extraConfig)
+            ({ Addresses.API = cfg.apiAddress;
+               Addresses.Gateway = cfg.gatewayAddress;
+               Addresses.Swarm = cfg.swarmAddress;
+            } //
+            cfg.extraConfig))
         );
     serviceConfig = {
       ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}";
@@ -140,6 +142,12 @@ in {
         description = "Where IPFS exposes its API to";
       };
 
+      swarmAddress = mkOption {
+        type = types.listOf types.str;
+        default = [ "/ip4/0.0.0.0/tcp/4001" "/ip6/::/tcp/4001" ];
+        description = "Where IPFS listens for incoming p2p connections";
+      };
+
       enableGC = mkOption {
         type = types.bool;
         default = false;
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
deleted file mode 100644
index 0946e379e796..000000000000
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ /dev/null
@@ -1,99 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-let
-  inherit (lib) mkOption mkIf;
-
-  cfg = config.services.openafsClient;
-
-  cellServDB = pkgs.fetchurl {
-    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
-    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
-  };
-
-  afsConfig = pkgs.runCommand "afsconfig" {} ''
-    mkdir -p $out
-    echo ${cfg.cellName} > $out/ThisCell
-    cp ${cellServDB} $out/CellServDB
-    echo "/afs:${cfg.cacheDirectory}:${cfg.cacheSize}" > $out/cacheinfo
-  '';
-
-  openafsPkgs = config.boot.kernelPackages.openafsClient;
-in
-{
-  ###### interface
-
-  options = {
-
-    services.openafsClient = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable the OpenAFS client.";
-      };
-
-      cellName = mkOption {
-        default = "grand.central.org";
-        description = "Cell name.";
-      };
-
-      cacheSize = mkOption {
-        default = "100000";
-        description = "Cache size.";
-      };
-
-      cacheDirectory = mkOption {
-        default = "/var/cache/openafs";
-        description = "Cache directory.";
-      };
-
-      crypt = mkOption {
-        default = false;
-        description = "Whether to enable (weak) protocol encryption.";
-      };
-
-      sparse = mkOption {
-        default = false;
-        description = "Minimal cell list in /afs.";
-      };
-
-    };
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ openafsPkgs ];
-
-    environment.etc = [
-      { source = afsConfig;
-        target = "openafs";
-      }
-    ];
-
-    systemd.services.afsd = {
-      description = "AFS client";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-      serviceConfig = { RemainAfterExit = true; };
-
-      preStart = ''
-        mkdir -p -m 0755 /afs
-        mkdir -m 0700 -p ${cfg.cacheDirectory}
-        ${pkgs.kmod}/bin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true
-        ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} -fakestat -afsdb
-        ${openafsPkgs}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
-      '';
-
-      # Doing this in preStop, because after these commands AFS is basically
-      # stopped, so systemd has nothing to do, just noticing it.  If done in
-      # postStop, then we get a hang + kernel oops, because AFS can't be
-      # stopped simply by sending signals to processes.
-      preStop = ''
-        ${pkgs.utillinux}/bin/umount /afs
-        ${openafsPkgs}/sbin/afsd -shutdown
-      '';
-    };
-  };
-}
diff --git a/nixos/modules/services/network-filesystems/openafs/client.nix b/nixos/modules/services/network-filesystems/openafs/client.nix
new file mode 100644
index 000000000000..3826fe3edfd0
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/client.nix
@@ -0,0 +1,239 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) getBin mkOption mkIf optionalString singleton types;
+
+  cfg = config.services.openafsClient;
+
+  cellServDB = pkgs.fetchurl {
+    url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14;
+    sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc";
+  };
+
+  clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
+
+  afsConfig = pkgs.runCommand "afsconfig" {} ''
+    mkdir -p $out
+    echo ${cfg.cellName} > $out/ThisCell
+    cat ${cellServDB} ${clientServDB} > $out/CellServDB
+    echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
+  '';
+
+  openafsMod = config.boot.kernelPackages.openafs;
+  openafsBin = lib.getBin pkgs.openafs;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.openafsClient = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the OpenAFS client.";
+      };
+
+      afsdb = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Resolve cells via AFSDB DNS records.";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule { options = cellServDBConfig; });
+        description = ''
+          This cell's database server records, added to the global
+          CellServDB. See CellServDB(5) man page for syntax. Ignored when
+          <literal>afsdb</literal> is set to <literal>true</literal>.
+        '';
+        example = ''
+          [ { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
+            { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
+          ]
+        '';
+      };
+
+      cache = {
+        blocks = mkOption {
+          default = 100000;
+          type = types.int;
+          description = "Cache size in 1KB blocks.";
+        };
+
+        chunksize = mkOption {
+          default = 0;
+          type = types.ints.between 0 30;
+          description = ''
+            Size of each cache chunk given in powers of
+            2. <literal>0</literal> resets the chunk size to its default
+            values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
+            diskcache). Maximum value is 30. Important performance
+            parameter. Set to higher values when dealing with large files.
+          '';
+        };
+
+        directory = mkOption {
+          default = "/var/cache/openafs";
+          type = types.str;
+          description = "Cache directory.";
+        };
+
+        diskless = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Use in-memory cache for diskless machines. Has no real
+            performance benefit anymore.
+          '';
+        };
+      };
+
+      crypt = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Whether to enable (weak) protocol encryption.";
+      };
+
+      daemons = mkOption {
+        default = 2;
+        type = types.int;
+        description = ''
+          Number of daemons to serve user requests. Numbers higher than 6
+          usually do no increase performance. Default is sufficient for up
+          to five concurrent users.
+        '';
+      };
+
+      fakestat = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Return fake data on stat() calls. If <literal>true</literal>,
+          always do so. If <literal>false</literal>, only do so for
+          cross-cell mounts (as these are potentially expensive).
+        '';
+      };
+
+      inumcalc = mkOption {
+        default = "compat";
+        type = types.strMatching "compat|md5";
+        description = ''
+          Inode calculation method. <literal>compat</literal> is
+          computationally less expensive, but <literal>md5</literal> greatly
+          reduces the likelihood of inode collisions in larger scenarios
+          involving multiple cells mounted into one AFS space.
+        '';
+      };
+
+      mountPoint = mkOption {
+        default = "/afs";
+        type = types.str;
+        description = ''
+          Mountpoint of the AFS file tree, conventionally
+          <literal>/afs</literal>. When set to a different value, only
+          cross-cells that use the same value can be accessed.
+        '';
+      };
+
+      sparse = mkOption {
+        default = true;
+        type = types.bool;
+        description = "Minimal cell list in /afs.";
+      };
+
+      startDisconnected = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Start up in disconnected mode.  You need to execute
+          <literal>fs disco online</literal> (as root) to switch to
+          connected mode. Useful for roaming devices.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.afsdb || cfg.cellServDB != [];
+        message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsClient.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      clientCellServDB = {
+        source = pkgs.runCommand "CellServDB" {} ''
+          cat ${cellServDB} ${clientServDB} > $out
+        '';
+        target = "openafs/CellServDB";
+        mode = "0644";
+      };
+      clientCell = {
+        text = ''
+          ${cfg.cellName}
+        '';
+        target = "openafs/ThisCell";
+        mode = "0644";
+      };
+    };
+
+    systemd.services.afsd = {
+      description = "AFS client";
+      wantedBy = [ "multi-user.target" ];
+      after = singleton (if cfg.startDisconnected then  "network.target" else "network-online.target");
+      serviceConfig = { RemainAfterExit = true; };
+      restartIfChanged = false;
+
+      preStart = ''
+        mkdir -p -m 0755 ${cfg.mountPoint}
+        mkdir -m 0700 -p ${cfg.cache.directory}
+        ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
+        ${openafsBin}/sbin/afsd \
+          -mountdir ${cfg.mountPoint} \
+          -confdir ${afsConfig} \
+          ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
+          -blocks ${toString cfg.cache.blocks} \
+          -chunksize ${toString cfg.cache.chunksize} \
+          ${optionalString cfg.cache.diskless "-memcache"} \
+          -inumcalc ${cfg.inumcalc} \
+          ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
+          ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
+          ${optionalString cfg.afsdb "-afsdb"}
+        ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
+        ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
+      '';
+
+      # Doing this in preStop, because after these commands AFS is basically
+      # stopped, so systemd has nothing to do, just noticing it.  If done in
+      # postStop, then we get a hang + kernel oops, because AFS can't be
+      # stopped simply by sending signals to processes.
+      preStop = ''
+        ${pkgs.utillinux}/bin/umount ${cfg.mountPoint}
+        ${openafsBin}/sbin/afsd -shutdown
+        ${pkgs.kmod}/sbin/rmmod libafs
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/lib.nix b/nixos/modules/services/network-filesystems/openafs/lib.nix
new file mode 100644
index 000000000000..ecfc72d2eaf9
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/lib.nix
@@ -0,0 +1,28 @@
+{ lib, ...}:
+
+let
+  inherit (lib) concatStringsSep mkOption types;
+
+in rec {
+
+  mkCellServDB = cellName: db: ''
+    >${cellName}
+  '' + (concatStringsSep "\n" (map (dbm: if (dbm.ip != "" && dbm.dnsname != "") then dbm.ip + " #" + dbm.dnsname else "")
+                                   db));
+
+  # CellServDB configuration type
+  cellServDBConfig = {
+    ip = mkOption {
+      type = types.str;
+      default = "";
+      example = "1.2.3.4";
+      description = "IP Address of a database server";
+    };
+    dnsname = mkOption {
+      type = types.str;
+      default = "";
+      example = "afs.example.org";
+      description = "DNS full-qualified domain name of a database server";
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix
new file mode 100644
index 000000000000..429eb945ac9e
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -0,0 +1,260 @@
+{ config, pkgs, lib, ... }:
+
+with import ./lib.nix { inherit lib; };
+
+let
+  inherit (lib) concatStringsSep intersperse mapAttrsToList mkForce mkIf mkMerge mkOption optionalString types;
+
+  bosConfig = pkgs.writeText "BosConfig" (''
+    restrictmode 1
+    restarttime 16 0 0 0 0
+    checkbintime 3 0 5 0 0
+  '' + (optionalString cfg.roles.database.enable ''
+    bnode simple vlserver 1
+    parm ${openafsBin}/libexec/openafs/vlserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.vlserverArgs}
+    end
+    bnode simple ptserver 1
+    parm ${openafsBin}/libexec/openafs/ptserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.ptserverArgs}
+    end
+  '') + (optionalString cfg.roles.fileserver.enable ''
+    bnode dafs dafs 1
+    parm ${openafsBin}/libexec/openafs/dafileserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.fileserverArgs}
+    parm ${openafsBin}/libexec/openafs/davolserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.volserverArgs}
+    parm ${openafsBin}/libexec/openafs/salvageserver ${cfg.roles.fileserver.salvageserverArgs}
+    parm ${openafsBin}/libexec/openafs/dasalvager ${cfg.roles.fileserver.salvagerArgs}
+    end
+  '') + (optionalString (cfg.roles.database.enable && cfg.roles.backup.enable) ''
+    bnode simple buserver 1
+    parm ${openafsBin}/libexec/openafs/buserver ${cfg.roles.backup.buserverArgs} ${optionalString (cfg.roles.backup.cellServDB != []) "-cellservdb /etc/openafs/backup/"}
+    end
+  ''));
+
+  netInfo = if (cfg.advertisedAddresses != []) then
+    pkgs.writeText "NetInfo" ((concatStringsSep "\nf " cfg.advertisedAddresses) + "\n")
+  else null;
+
+  buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.roles.backup.cellServDB);
+
+  cfg = config.services.openafsServer;
+
+  udpSizeStr = toString cfg.udpPacketSize;
+
+  openafsBin = lib.getBin pkgs.openafs;
+
+in {
+
+  options = {
+
+    services.openafsServer = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the OpenAFS server. An OpenAFS server needs a
+          complex setup. So, be aware that enabling this service and setting
+          some options does not give you a turn-key-ready solution. You need
+          at least a running Kerberos 5 setup, as OpenAFS relies on it for
+          authentication. See the Guide "QuickStartUnix" coming with
+          <literal>pkgs.openafs.doc</literal> for complete setup
+          instructions.
+        '';
+      };
+
+      advertisedAddresses = mkOption {
+        default = [];
+        description = "List of IP addresses this server is advertised under. See NetInfo(5)";
+      };
+
+      cellName = mkOption {
+        default = "";
+        type = types.str;
+        description = "Cell name, this server will serve.";
+        example = "grand.central.org";
+      };
+
+      cellServDB = mkOption {
+        default = [];
+        type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+        description = "Definition of all cell-local database server machines.";
+      };
+
+      roles = {
+        fileserver = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = "Fileserver role, serves files and volumes from its local storage.";
+          };
+
+          fileserverArgs = mkOption {
+            default = "-vattachpar 128 -vhashsize 11 -L -rxpck 400 -cb 1000000";
+            type = types.str;
+            description = "Arguments to the dafileserver process. See its man page.";
+          };
+
+          volserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the davolserver process. See its man page.";
+            example = "-sync never";
+          };
+
+          salvageserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the salvageserver process. See its man page.";
+            example = "-showlog";
+          };
+
+          salvagerArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the dasalvager process. See its man page.";
+            example = "-showlog -showmounts";
+          };
+        };
+
+        database = {
+          enable = mkOption {
+            default = true;
+            type = types.bool;
+            description = ''
+              Database server role, maintains the Volume Location Database,
+              Protection Database (and Backup Database, see
+              <literal>backup</literal> role). There can be multiple
+              servers in the database role for replication, which then need
+              reliable network connection to each other.
+
+              Servers in this role appear in AFSDB DNS records or the
+              CellServDB.
+            '';
+          };
+
+          vlserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the vlserver process. See its man page.";
+            example = "-rxbind";
+          };
+
+          ptserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the ptserver process. See its man page.";
+            example = "-restricted -default_access S---- S-M---";
+          };
+        };
+
+        backup = {
+          enable = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Backup server role. Use in conjunction with the
+              <literal>database</literal> role to maintain the Backup
+              Database. Normally only used in conjunction with tape storage
+              or IBM's Tivoli Storage Manager.
+            '';
+          };
+
+          buserverArgs = mkOption {
+            default = "";
+            type = types.str;
+            description = "Arguments to the buserver process. See its man page.";
+            example = "-p 8";
+          };
+
+          cellServDB = mkOption {
+            default = [];
+            type = with types; listOf (submodule [ { options = cellServDBConfig;} ]);
+            description = ''
+              Definition of all cell-local backup database server machines.
+              Use this when your cell uses less backup database servers than
+              other database server machines.
+            '';
+          };
+        };
+      };
+
+      dottedPrincipals= mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If enabled, allow principal names containing (.) dots. Enabling
+          this has security implications!
+        '';
+      };
+
+      udpPacketSize = mkOption {
+        default = 1310720;
+        type = types.int;
+        description = ''
+          UDP packet size to use in Bytes. Higher values can speed up
+          communications. The default of 1 MB is a sufficient in most
+          cases. Make sure to increase the kernel's UDP buffer size
+          accordingly via <literal>net.core(w|r|opt)mem_max</literal>
+          sysctl.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.cellServDB != [];
+        message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB.";
+      }
+      { assertion = cfg.cellName != "";
+        message = "You must specify the local cell name in config.services.openafsServer.cellName.";
+      }
+    ];
+
+    environment.systemPackages = [ pkgs.openafs ];
+
+    environment.etc = {
+      bosConfig = {
+        source = bosConfig;
+        target = "openafs/BosConfig";
+        mode = "0644";
+      };
+      cellServDB = {
+        text = mkCellServDB cfg.cellName cfg.cellServDB;
+        target = "openafs/server/CellServDB";
+        mode = "0644";
+      };
+      thisCell = {
+        text = cfg.cellName;
+        target = "openafs/server/ThisCell";
+        mode = "0644";
+      };
+      buCellServDB = {
+        enable = (cfg.roles.backup.cellServDB != []);
+        text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB;
+        target = "openafs/backup/CellServDB";
+      };
+    };
+
+    systemd.services = {
+      openafs-server = {
+        description = "OpenAFS server";
+        after = [ "syslog.target" "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        restartIfChanged = false;
+        unitConfig.ConditionPathExists = [ "/etc/openafs/server/rxkad.keytab" ];
+        preStart = ''
+          mkdir -m 0755 -p /var/openafs
+          ${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"}
+          ${optionalString (cfg.roles.backup.cellServDB != []) "cp ${buCellServDB}"}
+        '';
+        serviceConfig = {
+          ExecStart = "${openafsBin}/bin/bosserver -nofork";
+          ExecStop = "${openafsBin}/bin/bos shutdown localhost -wait -localauth";
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 9b9c91a4f167..b23266e8d43a 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -54,9 +54,12 @@ let
       };
 
       serviceConfig = {
-        ExecStart = "${samba}/sbin/${appName} ${args}";
+        ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        LimitNOFILE = 16384;
+        PIDFile = "/run/${appName}.pid";
         Type = "notify";
+        NotifyAccess = "all"; #may not do anything...
       };
 
       restartTriggers = [ configFile ];
@@ -230,11 +233,12 @@ in
             after = [ "samba-setup.service" "network.target" ];
             wantedBy = [ "multi-user.target" ];
           };
-
+          # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd
+          # for correct use with systemd
           services = {
-            "samba-smbd" = daemonService "smbd" "-F";
-            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F");
-            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F");
+            "samba-smbd" = daemonService "smbd" "";
+            "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "");
+            "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "");
             "samba-setup" = {
               description = "Samba Setup Task";
               script = setupScript;
diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix
index 0c6714563d8a..95d7641e8b53 100644
--- a/nixos/modules/services/network-filesystems/xtreemfs.nix
+++ b/nixos/modules/services/network-filesystems/xtreemfs.nix
@@ -11,7 +11,7 @@ let
   home = cfg.homeDir;
 
   startupScript = class: configPath: pkgs.writeScript "xtreemfs-osd.sh" ''
-    #! ${pkgs.stdenv.shell}
+    #! ${pkgs.runtimeShell}
     JAVA_HOME="${pkgs.jdk}"
     JAVADIR="${xtreemfs}/share/java"
     JAVA_CALL="$JAVA_HOME/bin/java -ea -cp $JAVADIR/XtreemFS.jar:$JAVADIR/BabuDB.jar:$JAVADIR/Flease.jar:$JAVADIR/protobuf-java-2.5.0.jar:$JAVADIR/Foundation.jar:$JAVADIR/jdmkrt.jar:$JAVADIR/jdmktk.jar:$JAVADIR/commons-codec-1.3.jar"
diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix
index 4de206641331..44b0edf62018 100644
--- a/nixos/modules/services/network-filesystems/yandex-disk.nix
+++ b/nixos/modules/services/network-filesystems/yandex-disk.nix
@@ -99,10 +99,10 @@ in
             exit 1
         fi
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token'
 
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \
           -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}'
       '';
 
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
index fc7d56a24fa7..9898f164c5cf 100644
--- a/nixos/modules/services/networking/amuled.nix
+++ b/nixos/modules/services/networking/amuled.nix
@@ -68,7 +68,7 @@ in
       '';
 
       script = ''
-        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \
+        ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \
             -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
       '';
     };
diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix
index ad4ac9bf45e3..df9c92db2e54 100644
--- a/nixos/modules/services/networking/aria2.nix
+++ b/nixos/modules/services/networking/aria2.nix
@@ -10,9 +10,9 @@ let
   settingsDir = "${homeDir}";
   sessionFile = "${homeDir}/aria2.session";
   downloadDir = "${homeDir}/Downloads";
-  
+
   rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to);
-  
+
   settingsFile = pkgs.writeText "aria2.conf"
   ''
     dir=${cfg.downloadDir}
@@ -110,12 +110,12 @@ in
         mkdir -m 0770 -p "${homeDir}"
         chown aria2:aria2 "${homeDir}"
         if [[ ! -d "${config.services.aria2.downloadDir}" ]]
-        then 
+        then
           mkdir -m 0770 -p "${config.services.aria2.downloadDir}"
           chown aria2:aria2 "${config.services.aria2.downloadDir}"
         fi
         if [[ ! -e "${sessionFile}" ]]
-        then 
+        then
           touch "${sessionFile}"
           chown aria2:aria2 "${sessionFile}"
         fi
@@ -132,4 +132,4 @@ in
       };
     };
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index 1a7a1e24b702..c25bd0fdc541 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -7,21 +7,27 @@ let
     let
       cfg = config.services.${variant};
       pkg = pkgs.${variant};
+      birdBin = if variant == "bird6" then "bird6" else "bird";
       birdc = if variant == "bird6" then "birdc6" else "birdc";
+      descr =
+        { bird = "1.9.x with IPv4 suport";
+          bird6 = "1.9.x with IPv6 suport";
+          bird2 = "2.x";
+        }.${variant};
       configFile = pkgs.stdenv.mkDerivation {
         name = "${variant}.conf";
         text = cfg.config;
         preferLocalBuild = true;
         buildCommand = ''
           echo -n "$text" > $out
-          ${pkg}/bin/${variant} -d -p -c $out
+          ${pkg}/bin/${birdBin} -d -p -c $out
         '';
       };
     in {
       ###### interface
       options = {
         services.${variant} = {
-          enable = mkEnableOption "BIRD Internet Routing Daemon";
+          enable = mkEnableOption "BIRD Internet Routing Daemon (${descr})";
           config = mkOption {
             type = types.lines;
             description = ''
@@ -36,12 +42,12 @@ let
       config = mkIf cfg.enable {
         environment.systemPackages = [ pkg ];
         systemd.services.${variant} = {
-          description = "BIRD Internet Routing Daemon";
+          description = "BIRD Internet Routing Daemon (${descr})";
           wantedBy = [ "multi-user.target" ];
           serviceConfig = {
             Type = "forking";
             Restart = "on-failure";
-            ExecStart = "${pkg}/bin/${variant} -c ${configFile} -u ${variant} -g ${variant}";
+            ExecStart = "${pkg}/bin/${birdBin} -c ${configFile} -u ${variant} -g ${variant}";
             ExecReload = "${pkg}/bin/${birdc} configure";
             ExecStop = "${pkg}/bin/${birdc} down";
             CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID"
@@ -56,14 +62,15 @@ let
         users = {
           extraUsers.${variant} = {
             description = "BIRD Internet Routing Daemon user";
-            group = "${variant}";
+            group = variant;
           };
           extraGroups.${variant} = {};
         };
       };
     };
 
-  inherit (config.services) bird bird6;
-in {
-  imports = [(generic "bird") (generic "bird6")];
+in
+
+{
+  imports = map generic [ "bird" "bird6" "bird2" ];
 }
diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix
index 546d27069232..c3ca6fbe725e 100644
--- a/nixos/modules/services/networking/connman.nix
+++ b/nixos/modules/services/networking/connman.nix
@@ -52,6 +52,15 @@ in {
         '';
       };
 
+      extraFlags = mkOption {
+        type = with types; listOf string;
+        default = [ ];
+        example = [ "--nodnsproxy" ];
+        description = ''
+          Extra flags to pass to connmand
+        '';
+      };
+
     };
 
   };
@@ -81,7 +90,7 @@ in {
         Type = "dbus";
         BusName = "net.connman";
         Restart = "on-failure";
-        ExecStart = "${pkgs.connman}/sbin/connmand --config=${configFile} --nodaemon";
+        ExecStart = "${pkgs.connman}/sbin/connmand --config=${configFile} --nodaemon ${toString cfg.extraFlags}";
         StandardOutput = "null";
       };
     };
diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix
index a9a77f3412af..32acce51e692 100644
--- a/nixos/modules/services/networking/dante.nix
+++ b/nixos/modules/services/networking/dante.nix
@@ -47,7 +47,7 @@ in
 
     systemd.services.dante = {
       description   = "Dante SOCKS v4 and v5 compatible proxy server";
-      after         = [ "network.target" ];
+      after         = [ "network-online.target" ];
       wantedBy      = [ "multi-user.target" ];
 
       serviceConfig = {
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 9e56545f746c..9a2e13e9553c 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -3,24 +3,24 @@
 let
   cfg = config.services.ddclient;
   boolToStr = bool: if bool then "yes" else "no";
+  dataDir = "/var/lib/ddclient";
 
   configText = ''
     # This file can be used as a template for configFile or is automatically generated by Nix options.
-    daemon=${toString cfg.interval}
-    cache=${cfg.homeDir}/ddclient.cache
-    pid=/run/ddclient/ddclient.pid
-    foreground=NO
+    cache=${dataDir}/ddclient.cache
+    foreground=YES
     use=${cfg.use}
     login=${cfg.username}
     password=${cfg.password}
     protocol=${cfg.protocol}
-    ${let server = cfg.server; in
-      lib.optionalString (server != "") "server=${server}"}
+    ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
+    ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
+    ${lib.optionalString (cfg.zone != "")   "zone=${cfg.zone}"}
     ssl=${boolToStr cfg.ssl}
     wildcard=YES
     quiet=${boolToStr cfg.quiet}
     verbose=${boolToStr cfg.verbose}
-    ${cfg.domain}
+    ${lib.concatStringsSep "," cfg.domains}
     ${cfg.extraConfig}
   '';
 
@@ -44,17 +44,11 @@ with lib;
         '';
       };
 
-      homeDir = mkOption {
-        default = "/var/lib/ddclient";
-        type = str;
-        description = "Home directory for the daemon user.";
-      };
-
-      domain = mkOption {
-        default = "";
-        type = str;
+      domains = mkOption {
+        default = [ "" ];
+        type = listOf str;
         description = ''
-          Domain name to synchronize.
+          Domain name(s) to synchronize.
         '';
       };
 
@@ -62,7 +56,7 @@ with lib;
         default = "";
         type = str;
         description = ''
-          Username.
+          User name.
         '';
       };
 
@@ -75,9 +69,12 @@ with lib;
       };
 
       interval = mkOption {
-        default = 600;
-        type = int;
-        description = "The interval at which to run the check and update.";
+        default = "10min";
+        type = str;
+        description = ''
+          The interval at which to run the check and update.
+          See <command>man 7 systemd.time</command> for the format.
+        '';
       };
 
       configFile = mkOption {
@@ -95,7 +92,7 @@ with lib;
         default = "dyndns2";
         type = str;
         description = ''
-          Protocol to use with dynamic DNS provider (see http://sourceforge.net/apps/trac/ddclient/wiki/Protocols).
+          Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
         '';
       };
 
@@ -115,11 +112,20 @@ with lib;
         '';
       };
 
-      extraConfig = mkOption {
+
+      quiet = mkOption {
+        default = false;
+        type = bool;
+        description = ''
+          Print no messages for unnecessary updates.
+        '';
+      };
+
+      script = mkOption {
         default = "";
-        type = lines;
+        type = str;
         description = ''
-          Extra configuration. Contents will be added verbatim to the configuration file.
+          script as required by some providers.
         '';
       };
 
@@ -139,11 +145,19 @@ with lib;
         '';
       };
 
-      quiet = mkOption {
-        default = false;
-        type = bool;
+      zone = mkOption {
+        default = "";
+        type = str;
         description = ''
-          Print no messages for unnecessary updates.
+          zone as required by some providers.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = lines;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
         '';
       };
     };
@@ -153,23 +167,8 @@ with lib;
   ###### implementation
 
   config = mkIf config.services.ddclient.enable {
-
-    users = {
-      extraGroups.ddclient.gid = config.ids.gids.ddclient;
-
-      extraUsers.ddclient = {
-        uid = config.ids.uids.ddclient;
-        description = "ddclient daemon user";
-        group = "ddclient";
-        home = cfg.homeDir;
-        createHome = true;
-      };
-    };
-
     environment.etc."ddclient.conf" = {
       enable = cfg.configFile == "/etc/ddclient.conf";
-      uid = config.ids.uids.ddclient;
-      gid = config.ids.gids.ddclient;
       mode = "0600";
       text = configText;
     };
@@ -180,15 +179,22 @@ with lib;
       after = [ "network.target" ];
       restartTriggers = [ config.environment.etc."ddclient.conf".source ];
 
-      serviceConfig = {
-        RuntimeDirectory = "ddclient";
-        # we cannot run in forking mode as it swallows all the program output
-        Type = "simple";
-        User = "ddclient";
-        Group = "ddclient";
-        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}";
-        ProtectSystem = "full";
-        PrivateTmp = true;
+      serviceConfig = rec {
+        DynamicUser = true;
+        RuntimeDirectory = StateDirectory;
+        StateDirectory = builtins.baseNameOf dataDir;
+        Type = "oneshot";
+        ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf";
+        ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
+      };
+    };
+
+    systemd.timers.ddclient = {
+      description = "Run ddclient";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnBootSec = cfg.interval;
+        OnUnitInactiveSec = cfg.interval;
       };
     };
   };
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index d283c7624335..de0aa1a2c2c3 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -16,7 +16,7 @@ let
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
   ignoredInterfaces =
-    map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces)
+    map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces)
     ++ mapAttrsToList (i: _: i) config.networking.sits
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
     ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.vswitches))
@@ -156,11 +156,11 @@ in
     systemd.services.dhcpcd = let
       cfgN = config.networking;
       hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")
-                          || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != "");
+                          && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != ""));
     in
       { description = "DHCP Client";
 
-        wantedBy = optional (!hasDefaultGatewaySet) "network-online.target";
+        wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target";
         after = [ "network.target" ];
         wants = [ "network.target" ];
 
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index 2eac6dfec5b7..fd7e317eee95 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -36,6 +36,7 @@ let
 
       preStart = ''
         mkdir -m 755 -p ${cfg.stateDir}
+        chown dhcpd:nogroup ${cfg.stateDir}
         touch ${cfg.stateDir}/dhcpd.leases
       '';
 
diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix
index 379203cd1ab6..ba5c8e2d5e53 100644
--- a/nixos/modules/services/networking/dnscache.nix
+++ b/nixos/modules/services/networking/dnscache.nix
@@ -9,12 +9,12 @@ let
     mkdir -p $out/{servers,ip}
 
     ${concatMapStrings (ip: ''
-      echo > "$out/ip/"${lib.escapeShellArg ip}
+      touch "$out/ip/"${lib.escapeShellArg ip}
     '') cfg.clientIps}
 
     ${concatStrings (mapAttrsToList (host: ips: ''
       ${concatMapStrings (ip: ''
-        echo ${lib.escapeShellArg ip} > "$out/servers/"${lib.escapeShellArg host}
+        echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host}
       '') ips}
     '') cfg.domainServers)}
 
@@ -34,33 +34,49 @@ in {
 
   options = {
     services.dnscache = {
+
       enable = mkOption {
         default = false;
         type = types.bool;
-        description = "Whether to run the dnscache caching dns server";
+        description = "Whether to run the dnscache caching dns server.";
       };
 
       ip = mkOption {
         default = "0.0.0.0";
         type = types.str;
-        description = "IP address on which to listen for connections";
+        description = "IP address on which to listen for connections.";
       };
 
       clientIps = mkOption {
         default = [ "127.0.0.1" ];
         type = types.listOf types.str;
-        description = "client IP addresses (or prefixes) from which to accept connections";
+        description = "Client IP addresses (or prefixes) from which to accept connections.";
         example = ["192.168" "172.23.75.82"];
       };
 
       domainServers = mkOption {
         default = { };
         type = types.attrsOf (types.listOf types.str);
-        description = "table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts)";
+        description = ''
+          Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts).
+          If entry for @ is not specified predefined list of root servers is used.
+        '';
         example = {
-          "example.com" = ["8.8.8.8" "8.8.4.4"];
+          "@" = ["8.8.8.8" "8.8.4.4"];
+          "example.com" = ["192.168.100.100"];
         };
       };
+
+      forwardOnly = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to treat root servers (for @) as caching
+          servers, requesting addresses the same way a client does. This is
+          needed if you want to use e.g. Google DNS as your upstream DNS.
+        '';
+      };
+
     };
   };
 
@@ -82,6 +98,7 @@ in {
       '';
       script = ''
         cd /var/lib/dnscache/
+        ${optionalString cfg.forwardOnly "export FORWARDONLY=1"}
         exec ./run
       '';
     };
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
index ed658258c7f9..857657eea4db 100644
--- a/nixos/modules/services/networking/dnscrypt-proxy.nix
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -10,7 +10,7 @@ let
   # This is somewhat more flexible than preloading the key as an
   # embedded string.
   upstreamResolverListPubKey = pkgs.fetchurl {
-    url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub;
+    url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub;
     sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
   };
 
@@ -258,9 +258,9 @@ in
         domain=raw.githubusercontent.com
         get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)"
         $get -o dnscrypt-resolvers.csv.tmp \
-          https://$domain/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv
         $get -o dnscrypt-resolvers.csv.minisig.tmp \
-          https://$domain/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
         mv dnscrypt-resolvers.csv.minisig{.tmp,}
         if ! minisign -q -V -p ${upstreamResolverListPubKey} \
           -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index 23cc92946e41..bf13d5c6f5fe 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -145,6 +145,16 @@ in {
     };
     users.groups.dnscrypt-wrapper = { };
 
+    security.polkit.extraConfig = ''
+      // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.freedesktop.systemd1.manage-units" &&
+              action.lookup("unit") == "dnscrypt-wrapper.service" &&
+              subject.user == "dnscrypt-wrapper") {
+              return polkit.Result.YES;
+          }
+        });
+    '';
 
     systemd.services.dnscrypt-wrapper = {
       description = "dnscrypt-wrapper daemon";
diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix
index a9f3fd65d76b..97d223a56cab 100644
--- a/nixos/modules/services/networking/firefox/sync-server.nix
+++ b/nixos/modules/services/networking/firefox/sync-server.nix
@@ -33,6 +33,8 @@ let
 in
 
 {
+  meta.maintainers = with lib.maintainers; [ nadrieril ];
+
   options = {
     services.firefox.syncserver = {
       enable = mkOption {
@@ -70,18 +72,6 @@ in
         '';
       };
 
-      user = mkOption {
-        type = types.str;
-        default = "syncserver";
-        description = "User account under which syncserver runs.";
-      };
-
-      group = mkOption {
-        type = types.str;
-        default = "syncserver";
-        description = "Group account under which syncserver runs.";
-      };
-
       publicUrl = mkOption {
         type = types.str;
         default = "http://localhost:5000/";
@@ -137,7 +127,9 @@ in
   config = mkIf cfg.enable {
 
     systemd.services.syncserver = let
-      syncServerEnv = pkgs.python.withPackages(ps: with ps; [ syncserver pasteScript ]);
+      syncServerEnv = pkgs.python.withPackages(ps: with ps; [ syncserver pasteScript requests ]);
+      user = "syncserver";
+      group = "syncserver";
     in {
       after = [ "network.target" ];
       description = "Firefox Sync Server";
@@ -145,43 +137,43 @@ in
       path = [ pkgs.coreutils syncServerEnv ];
 
       serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
+        User = user;
+        Group = group;
         PermissionsStartOnly = true;
       };
 
       preStart = ''
         if ! test -e ${cfg.privateConfig}; then
-          mkdir -m 700 -p $(dirname ${cfg.privateConfig})
+          mkdir -p $(dirname ${cfg.privateConfig})
           echo  > ${cfg.privateConfig} '[syncserver]'
+          chmod 600 ${cfg.privateConfig}
           echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')"
         fi
-        chown ${cfg.user}:${cfg.group} ${cfg.privateConfig}
+        chmod 600 ${cfg.privateConfig}
+        chmod 755 $(dirname ${cfg.privateConfig})
+        chown ${user}:${group} ${cfg.privateConfig}
+
       '' + optionalString (cfg.sqlUri == defaultSqlUri) ''
         if ! test -e $(dirname ${defaultDbLocation}); then
           mkdir -m 700 -p $(dirname ${defaultDbLocation})
-          chown ${cfg.user}:${cfg.group} $(dirname ${defaultDbLocation})
+          chown ${user}:${group} $(dirname ${defaultDbLocation})
         fi
+
         # Move previous database file if it exists
         oldDb="/var/db/firefox-sync-server.db"
         if test -f $oldDb; then
           mv $oldDb ${defaultDbLocation}
-          chown ${cfg.user}:${cfg.group} ${defaultDbLocation}
+          chown ${user}:${group} ${defaultDbLocation}
         fi
       '';
       serviceConfig.ExecStart = "${syncServerEnv}/bin/paster serve ${syncServerIni}";
     };
 
-    users.extraUsers = optionalAttrs (cfg.user == "syncserver")
-      (singleton {
-        name = "syncserver";
-        group = cfg.group;
-        isSystemUser = true;
-      });
-
-    users.extraGroups = optionalAttrs (cfg.group == "syncserver")
-      (singleton {
-        name = "syncserver";
-      });
+    users.users.syncserver = {
+      group = "syncserver";
+      isSystemUser = true;
+    };
+
+    users.groups.syncserver = {};
   };
 }
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 9bd88ca1707b..20c0b0acf165 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -54,7 +54,7 @@ let
     '';
 
   writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${text}
   ''; in "${dir}/bin/${name}";
 
@@ -125,6 +125,9 @@ let
       ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
       ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
 
+      # Allows this host to act as a DHCP4 client without first having to use APIPA
+      iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
+
       # Allows this host to act as a DHCPv4 server
       iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
 
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
index 5ba85178179b..5b83ce131389 100644
--- a/nixos/modules/services/networking/flashpolicyd.nix
+++ b/nixos/modules/services/networking/flashpolicyd.nix
@@ -22,7 +22,7 @@ let
 
   flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
       exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
         --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
         2> /dev/null
diff --git a/nixos/modules/services/networking/freeradius.nix b/nixos/modules/services/networking/freeradius.nix
new file mode 100644
index 000000000000..45cba1ce2770
--- /dev/null
+++ b/nixos/modules/services/networking/freeradius.nix
@@ -0,0 +1,72 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.freeradius;
+
+  freeradiusService = cfg:
+  {
+    description = "FreeRadius server";
+    wantedBy = ["multi-user.target"];
+    after = ["network-online.target"];
+    wants = ["network-online.target"];
+    preStart = ''
+      ${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout
+    '';
+
+    serviceConfig = {
+        ExecStart = "${pkgs.freeradius}/bin/radiusd -f -d ${cfg.configDir} -l stdout -xx";
+        ExecReload = [
+          "${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout"
+          "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
+        ];
+        User = "radius";
+        ProtectSystem = "full";
+        ProtectHome = "on";
+        Restart = "on-failure";
+        RestartSec = 2;
+    };
+  };
+
+  freeradiusConfig = {
+    enable = mkEnableOption "the freeradius server";
+
+    configDir = mkOption {
+      type = types.path;
+      default = "/etc/raddb";
+      description = ''
+        The path of the freeradius server configuration directory.
+      '';
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.freeradius = freeradiusConfig;
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.enable) {
+
+    users = {
+      extraUsers.radius = {
+        /*uid = config.ids.uids.radius;*/
+        description = "Radius daemon user";
+      };
+    };
+
+    systemd.services.freeradius = freeradiusService cfg;
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/gnunet.nix b/nixos/modules/services/networking/gnunet.nix
index 03ee54af4334..02cd53c6fa38 100644
--- a/nixos/modules/services/networking/gnunet.nix
+++ b/nixos/modules/services/networking/gnunet.nix
@@ -137,6 +137,8 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.gnunet pkgs.miniupnpc ];
+      environment.TMPDIR = "/tmp";
+      serviceConfig.PrivateTemp = true;
       serviceConfig.ExecStart = "${pkgs.gnunet}/lib/gnunet/libexec/gnunet-service-arm -c ${configFile}";
       serviceConfig.User = "gnunet";
       serviceConfig.UMask = "0007";
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index ca2e2a065dcf..8f5aeee4a16b 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -126,6 +126,7 @@ let
         [${tun.name}]
         type = client
         destination = ${tun.destination}
+        destinationport = ${toString tun.destinationPort}
         keys = ${tun.keys}
         address = ${tun.address}
         port = ${toString tun.port}
@@ -137,15 +138,15 @@ let
       '')
     }
     ${flip concatMapStrings
-      (collect (tun: tun ? port && tun ? host) cfg.inTunnels)
-      (tun: let portStr = toString tun.port; in ''
+      (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
+      (tun: ''
         [${tun.name}]
         type = server
         destination = ${tun.destination}
         keys = ${tun.keys}
         host = ${tun.address}
-        port = ${tun.port}
-        inport = ${tun.inPort}
+        port = ${toString tun.port}
+        inport = ${toString tun.inPort}
         accesslist = ${builtins.concatStringsSep "," tun.accessList}
       '')
     }
@@ -405,7 +406,13 @@ in
         default = {};
         type = with types; loaOf (submodule (
           { name, config, ... }: {
-            options = commonTunOpts name;
+            options = {
+              destinationPort = mkOption {
+                type = types.int;
+                default = 0;
+                description = "Connect to particular port at destination.";
+              };
+            } // commonTunOpts name;
             config = {
               name = mkDefault name;
             };
diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix
index 23787bce9911..344212ad8329 100644
--- a/nixos/modules/services/networking/iwd.nix
+++ b/nixos/modules/services/networking/iwd.nix
@@ -26,7 +26,7 @@ in {
       wants = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      serviceConfig.ExecStart = "${pkgs.iwd}/bin/iwd";
+      serviceConfig.ExecStart = "${pkgs.iwd}/libexec/iwd";
     };
   };
 
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index 18e2ab9aebf1..aac02b811d71 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -43,7 +43,16 @@ in
       type = with types; listOf str;
       default = [ "::1" "127.0.0.1" ];
       description = ''
-        What addresses the server should listen on.
+        What addresses the server should listen on. (UDP+TCP 53)
+      '';
+    };
+    listenTLS = mkOption {
+      type = with types; listOf str;
+      default = [];
+      example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ];
+      description = ''
+        Addresses on which kresd should provide DNS over TLS (see RFC 7858).
+        For detailed syntax see ListenStream in man systemd.socket.
       '';
     };
     # TODO: perhaps options for more common stuff like cache size or forwarding
@@ -72,6 +81,19 @@ in
         (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
         cfg.interfaces;
       socketConfig.ListenDatagram = listenStreams;
+      socketConfig.FreeBind = true;
+    };
+
+    systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec {
+      wantedBy = [ "sockets.target" ];
+      before = wantedBy;
+      partOf = [ "kresd.socket" ];
+      listenStreams = cfg.listenTLS;
+      socketConfig = {
+        FileDescriptorName = "tls";
+        FreeBind = true;
+        Service = "kresd.service";
+      };
     };
 
     systemd.sockets.kresd-control = rec {
@@ -82,20 +104,11 @@ in
       socketConfig = {
         FileDescriptorName = "control";
         Service = "kresd.service";
-        SocketMode = "0660"; # only root user/group may connect
+        SocketMode = "0660"; # only root user/group may connect and control kresd
       };
     };
 
-    # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch.
-    systemd.services.kresd-cachedir = {
-      serviceConfig.Type = "oneshot";
-      script = ''
-        if [ ! -d '${cfg.cacheDir}' ]; then
-          mkdir -p '${cfg.cacheDir}'
-          chown kresd:kresd '${cfg.cacheDir}'
-        fi
-      '';
-    };
+    systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ];
 
     systemd.services.kresd = {
       description = "Knot-resolver daemon";
@@ -104,16 +117,17 @@ in
         User = "kresd";
         Type = "notify";
         WorkingDirectory = cfg.cacheDir;
+        Restart = "on-failure";
+        Sockets = [ "kresd.socket" "kresd-control.socket" ]
+          ++ optional (cfg.listenTLS != []) "kresd-tls.socket";
       };
 
+      # Trust anchor goes from dns-root-data by default.
       script = ''
-        exec '${package}/bin/kresd' --config '${configFile}' \
-          -k '${cfg.cacheDir}/root.key'
+        exec '${package}/bin/kresd' --config '${configFile}' --forks=1
       '';
 
-      after = [ "kresd-cachedir.service" ];
-      requires = [ "kresd.socket" "kresd-cachedir.service" ];
-      wantedBy = [ "sockets.target" ];
+      requires = [ "kresd.socket" ];
     };
   };
 }
diff --git a/nixos/modules/services/networking/lldpd.nix b/nixos/modules/services/networking/lldpd.nix
index ba4e1b1542fe..db1534edfd7c 100644
--- a/nixos/modules/services/networking/lldpd.nix
+++ b/nixos/modules/services/networking/lldpd.nix
@@ -24,6 +24,7 @@ in
       description = "lldpd user";
       group = "_lldpd";
       home = "/var/run/lldpd";
+      isSystemUser = true;
     };
     users.extraGroups._lldpd = {};
 
diff --git a/nixos/modules/services/networking/monero.nix b/nixos/modules/services/networking/monero.nix
new file mode 100644
index 000000000000..31379189f5de
--- /dev/null
+++ b/nixos/modules/services/networking/monero.nix
@@ -0,0 +1,238 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.services.monero;
+  dataDir = "/var/lib/monero";
+
+  listToConf = option: list:
+    concatMapStrings (value: "${option}=${value}\n") list;
+
+  login = (cfg.rpc.user != null && cfg.rpc.password != null);
+
+  configFile = with cfg; pkgs.writeText "monero.conf" ''
+    log-file=/dev/stdout
+    data-dir=${dataDir}
+
+    ${optionalString mining.enable ''
+      start-mining=${mining.address}
+      mining-threads=${toString mining.threads}
+    ''}
+
+    rpc-bind-ip=${rpc.address}
+    rpc-bind-port=${toString rpc.port}
+    ${optionalString login ''
+      rpc-login=${rpc.user}:${rpc.password}
+    ''}
+    ${optionalString rpc.restricted ''
+      restrict-rpc=1
+    ''}
+
+    limit-rate-up=${toString limits.upload}
+    limit-rate-down=${toString limits.download}
+    max-concurrency=${toString limits.threads}
+    block-sync-size=${toString limits.syncSize}
+
+    ${listToConf "add-peer" extraNodes}
+    ${listToConf "add-priority-node" priorityNodes}
+    ${listToConf "add-exclusive-node" exclusiveNodes}
+
+    ${extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.monero = {
+
+      enable = mkEnableOption "Monero node daemon.";
+
+      mining.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to mine moneroj.
+        '';
+      };
+
+      mining.address = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Monero address where to send mining rewards.
+        '';
+      };
+
+      mining.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Number of threads used for mining.
+          Set to <literal>0</literal> to use all available.
+        '';
+      };
+
+      rpc.user = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          User name for RPC connections.
+        '';
+      };
+
+      rpc.password = mkOption {
+        type = types.str;
+        default = null;
+        description = ''
+          Password for RPC connections.
+        '';
+      };
+
+      rpc.address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = ''
+          IP address the RPC server will bind to.
+        '';
+      };
+
+      rpc.port = mkOption {
+        type = types.int;
+        default = 18081;
+        description = ''
+          Port the RPC server will bind to.
+        '';
+      };
+
+      rpc.restricted = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to restrict RPC to view only commands.
+        '';
+      };
+
+      limits.upload = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the upload rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.download = mkOption {
+        type = types.addCheck types.int (x: x>=-1);
+        default = -1;
+        description = ''
+          Limit of the download rate in kB/s.
+          Set to <literal>-1</literal> to leave unlimited.
+        '';
+      };
+
+      limits.threads = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of threads used for a parallel job.
+          Set to <literal>0</literal> to leave unlimited.
+        '';
+      };
+
+      limits.syncSize = mkOption {
+        type = types.addCheck types.int (x: x>=0);
+        default = 0;
+        description = ''
+          Maximum number of blocks to sync at once.
+          Set to <literal>0</literal> for adaptive.
+        '';
+      };
+
+      extraNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of additional peer IP addresses to add to the local list.
+        '';
+      };
+
+      priorityNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to and
+          attempt to keep the connection open.
+        '';
+      };
+
+      exclusiveNodes = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          List of peer IP addresses to connect to *only*.
+          If given the other peer options will be ignored.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to be added verbatim to monerod configuration.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton {
+      name = "monero";
+      uid  = config.ids.uids.monero;
+      description = "Monero daemon user";
+      home = dataDir;
+      createHome = true;
+    };
+
+    users.extraGroups = singleton {
+      name = "monero";
+      gid  = config.ids.gids.monero;
+    };
+
+    systemd.services.monero = {
+      description = "monero daemon";
+      after    = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User  = "monero";
+        Group = "monero";
+        ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive";
+        Restart = "always";
+        SuccessExitStatus = [ 0 1 ];
+      };
+    };
+
+   assertions = singleton {
+     assertion = cfg.mining.enable -> cfg.mining.address != "";
+     message   = ''
+       You need a Monero address to receive mining rewards:
+       specify one using option monero.mining.address.
+    '';
+   };
+
+  };
+
+}
+
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 81915b5a2ef8..d8135f4d0ffa 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -12,6 +12,10 @@ let
     keyfile ${cfg.ssl.keyfile}
   '';
 
+  passwordConf = optionalString cfg.checkPasswords ''
+    password_file ${cfg.dataDir}/passwd
+  '';
+
   mosquittoConf = pkgs.writeText "mosquitto.conf" ''
     pid_file /run/mosquitto/pid
     acl_file ${aclFile}
@@ -19,6 +23,7 @@ let
     allow_anonymous ${boolToString cfg.allowAnonymous}
     bind_address ${cfg.host}
     port ${toString cfg.port}
+    ${passwordConf}
     ${listenerConf}
     ${cfg.extraConf}
   '';
@@ -153,6 +158,15 @@ in
         '';
       };
 
+      checkPasswords = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Refuse connection when clients provide incorrect passwords.
+        '';
+      };
+
       extraConf = mkOption {
         default = "";
         type = types.lines;
@@ -198,7 +212,7 @@ in
       '' + concatStringsSep "\n" (
         mapAttrsToList (n: c:
           if c.hashedPassword != null then
-            "echo '${n}:${c.hashedPassword}' > ${cfg.dataDir}/passwd"
+            "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
           else optionalString (c.password != null)
             "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} ${c.password}"
         ) cfg.users);
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
index 13d7c3254f9d..873d62dbf341 100644
--- a/nixos/modules/services/networking/murmur.nix
+++ b/nixos/modules/services/networking/murmur.nix
@@ -80,7 +80,7 @@ in
 
       pidfile = mkOption {
         type = types.path;
-        default = "/tmp/murmurd.pid";
+        default = "/run/murmur/murmurd.pid";
         description = "Path to PID file for Murmur daemon.";
       };
 
@@ -252,6 +252,7 @@ in
 
       serviceConfig = {
         Type      = "forking";
+        RuntimeDirectory = "murmur";
         PIDFile   = cfg.pidfile;
         Restart   = "always";
         User      = "murmur";
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 366bb2ed7a80..da3827c35e63 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -19,6 +19,8 @@ let
     iptables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true
     iptables -w -t nat -F nixos-nat-post 2>/dev/null || true
     iptables -w -t nat -X nixos-nat-post 2>/dev/null || true
+
+    ${cfg.extraStopCommands}
   '';
 
   setupNat = ''
@@ -51,8 +53,40 @@ let
         -i ${cfg.externalInterface} -p ${fwd.proto} \
         --dport ${builtins.toString fwd.sourcePort} \
         -j DNAT --to-destination ${fwd.destination}
+
+      ${concatMapStrings (loopbackip:
+        let
+          m                = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
+          destinationIP    = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
+          destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 1;
+        in ''
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
+          iptables -w -t nat -A OUTPUT \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT
+          iptables -w -t nat -A nixos-nat-pre \
+            -d ${loopbackip} -p ${fwd.proto} \
+            --dport ${builtins.toString fwd.sourcePort} \
+            -j DNAT --to-destination ${fwd.destination}
+
+          iptables -w -t nat -A nixos-nat-post \
+            -d ${destinationIP} -p ${fwd.proto} \
+            --dport ${destinationPorts} \
+            -j SNAT --to-source ${loopbackip}
+        '') fwd.loopbackIPs}
     '') cfg.forwardPorts}
 
+    ${optionalString (cfg.dmzHost != null) ''
+      iptables -w -t nat -A nixos-nat-pre \
+        -i ${cfg.externalInterface} -j DNAT \
+        --to-destination ${cfg.dmzHost}
+    ''}
+
+    ${cfg.extraCommands}
+
     # Append our chains to the nat tables
     iptables -w -t nat -A PREROUTING -j nixos-nat-pre
     iptables -w -t nat -A POSTROUTING -j nixos-nat-post
@@ -125,15 +159,15 @@ in
       type = with types; listOf (submodule {
         options = {
           sourcePort = mkOption {
-            type = types.int;
+            type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+");
             example = 8080;
-            description = "Source port of the external interface";
+            description = "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")";
           };
 
           destination = mkOption {
             type = types.str;
             example = "10.0.0.1:80";
-            description = "Forward connection to destination ip:port";
+            description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end";
           };
 
           proto = mkOption {
@@ -142,6 +176,13 @@ in
             example = "udp";
             description = "Protocol of forwarded connection";
           };
+
+          loopbackIPs = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = literalExample ''[ "55.1.2.3" ]'';
+            description = "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort' from the host itself and from other hosts behind NAT";
+          };
         };
       });
       default = [];
@@ -153,6 +194,39 @@ in
         '';
     };
 
+    networking.nat.dmzHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      example = "10.0.0.1";
+      description =
+        ''
+          The local IP address to which all traffic that does not match any
+          forwarding rule is forwarded.
+        '';
+    };
+
+    networking.nat.extraCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -A INPUT -p icmp -j ACCEPT";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          initialisation script.
+        '';
+    };
+
+    networking.nat.extraStopCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = "iptables -D INPUT -p icmp -j ACCEPT || true";
+      description =
+        ''
+          Additional shell commands executed as part of the nat
+          teardown script.
+        '';
+    };
+
   };
 
 
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 6bdae32f72bb..10e96eb40362 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -133,10 +133,9 @@ in {
       basePackages = mkOption {
         type = types.attrsOf types.package;
         default = { inherit networkmanager modemmanager wpa_supplicant
-                            networkmanager_openvpn networkmanager_vpnc
-                            networkmanager_openconnect networkmanager_fortisslvpn
-                            networkmanager_pptp networkmanager_l2tp
-                            networkmanager_iodine; };
+                            networkmanager-openvpn networkmanager-vpnc
+                            networkmanager-openconnect networkmanager-fortisslvpn
+                            networkmanager-l2tp networkmanager-iodine; };
         internal = true;
       };
 
@@ -241,6 +240,19 @@ in {
           A list of scripts which will be executed in response to  network  events.
         '';
       };
+
+      enableStrongSwan = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the StrongSwan plugin.
+          </para><para>
+          If you enable this option the
+          <literal>networkmanager_strongswan</literal> plugin will be added to
+          the <option>networking.networkmanager.packages</option> option
+          so you don't need to to that yourself.
+        '';
+      };
     };
   };
 
@@ -254,34 +266,29 @@ in {
       message = "You can not use networking.networkmanager with networking.wireless";
     }];
 
-    boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
-
     environment.etc = with cfg.basePackages; [
       { source = configFile;
         target = "NetworkManager/NetworkManager.conf";
       }
-      { source = "${networkmanager_openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
+      { source = "${networkmanager-openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
         target = "NetworkManager/VPN/nm-openvpn-service.name";
       }
-      { source = "${networkmanager_vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
+      { source = "${networkmanager-vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
         target = "NetworkManager/VPN/nm-vpnc-service.name";
       }
-      { source = "${networkmanager_openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
+      { source = "${networkmanager-openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
         target = "NetworkManager/VPN/nm-openconnect-service.name";
       }
-      { source = "${networkmanager_fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
+      { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name";
         target = "NetworkManager/VPN/nm-fortisslvpn-service.name";
       }
-      { source = "${networkmanager_pptp}/etc/NetworkManager/VPN/nm-pptp-service.name";
-        target = "NetworkManager/VPN/nm-pptp-service.name";
-      }
-      { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
+      { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
         target = "NetworkManager/VPN/nm-l2tp-service.name";
       }
       { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name";
         target = "NetworkManager/VPN/nm-strongswan-service.name";
       }
-      { source = "${networkmanager_iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
+      { source = "${networkmanager-iodine}/etc/NetworkManager/VPN/nm-iodine-service.name";
         target = "NetworkManager/VPN/nm-iodine-service.name";
       }
     ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
@@ -322,6 +329,7 @@ in {
 
       preStart = ''
         mkdir -m 700 -p /etc/NetworkManager/system-connections
+        mkdir -m 700 -p /etc/ipsec.d
         mkdir -m 755 -p ${stateDirs}
       '';
     };
@@ -333,13 +341,13 @@ in {
       wireless.enable = lib.mkDefault false;
     };
 
-    powerManagement.resumeCommands = ''
-      ${config.systemd.package}/bin/systemctl restart network-manager
-    '';
-
     security.polkit.extraConfig = polkitConf;
 
-    services.dbus.packages = cfg.packages;
+    networking.networkmanager.packages =
+      mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ];
+
+    services.dbus.packages =
+      optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages;
 
     services.udev.packages = cfg.packages;
   };
diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix
index 56b942054140..ad7c013a5449 100644
--- a/nixos/modules/services/networking/nftables.nix
+++ b/nixos/modules/services/networking/nftables.nix
@@ -116,7 +116,7 @@ in
           include "${cfg.rulesetFile}"
         '';
         checkScript = pkgs.writeScript "nftables-check" ''
-          #! ${pkgs.stdenv.shell} -e
+          #! ${pkgs.runtimeShell} -e
           if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
             echo "Unload ip_tables before using nftables!" 1>&2
             exit 1
diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix
index 3e865e3b76a8..8499e7c0f7c4 100644
--- a/nixos/modules/services/networking/nix-serve.nix
+++ b/nixos/modules/services/networking/nix-serve.nix
@@ -55,6 +55,8 @@ in
       environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile;
 
       serviceConfig = {
+        Restart = "always";
+        RestartSec = "5s";
         ExecStart = "${pkgs.nix-serve}/bin/nix-serve " +
           "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}";
         User = "nix-serve";
diff --git a/nixos/modules/services/networking/nixops-dns.nix b/nixos/modules/services/networking/nixops-dns.nix
new file mode 100644
index 000000000000..2bb1263b7fa2
--- /dev/null
+++ b/nixos/modules/services/networking/nixops-dns.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  pkg = pkgs.nixops-dns;
+  cfg = config.services.nixops-dns;
+in
+
+{
+  options = {
+    services.nixops-dns = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the nixops-dns resolution
+          of NixOps virtual machines via dnsmasq and fake domain name.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user the nixops-dns daemon should run as.
+          This should be the user, which is also used for nixops and
+          have the .nixops directory in its home.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.str;
+        description = ''
+          Fake domain name to resolve to NixOps virtual machines.
+
+          For example "ops" will resolve "vm.ops".
+        '';
+        example = "ops";
+        default = "ops";
+      };
+
+      dnsmasq = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enable dnsmasq forwarding to nixops-dns. This allows to use
+          nixops-dns for `services.nixops-dns.domain` resolution
+          while forwarding the rest of the queries to original resolvers.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.nixops-dns = {
+      description = "nixops-dns: DNS server for resolving NixOps machines";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        ExecStart="${pkg}/bin/nixops-dns --domain=.${cfg.domain}";
+      };
+    };
+
+    services.dnsmasq = mkIf cfg.dnsmasq {
+      enable = true;
+      resolveLocalQueries = true;
+      servers = [
+        "/${cfg.domain}/127.0.0.1#5300"
+      ];
+      extraConfig = ''
+        bind-interfaces
+        listen-address=127.0.0.1
+      '';
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index c8b8ed547ebb..4241e6fcceab 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -11,6 +11,8 @@ let
 
   # build nsd with the options needed for the given config
   nsdPkg = pkgs.nsd.override {
+    configFile = "${configFile}/nsd.conf";
+
     bind8Stats = cfg.bind8Stats;
     ipv6 = cfg.ipv6;
     ratelimit = cfg.ratelimit.enable;
@@ -788,6 +790,8 @@ in
 
   config = mkIf cfg.enable {
 
+    environment.systemPackages = [ nsdPkg ];
+
     users.extraGroups = singleton {
       name = username;
       gid = config.ids.gids.nsd;
@@ -845,4 +849,6 @@ in
     };
 
   };
+
+  meta.maintainers = with lib.maintainers; [ hrdinka ];
 }
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 3fbf5a9f0227..a418839d22b8 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -50,6 +50,11 @@ let
               "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
           ${optionalString (cfg.down != "" || cfg.updateResolvConf)
               "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+          ${optionalString (cfg.authUserPass != null)
+              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+                ${cfg.authUserPass.username}
+                ${cfg.authUserPass.password}
+              ''}"}
         '';
 
     in {
@@ -60,7 +65,7 @@ let
 
       path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
 
-      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}";
+      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}";
       serviceConfig.Restart = "always";
       serviceConfig.Type = "notify";
     };
@@ -161,6 +166,29 @@ in
             '';
           };
 
+          authUserPass = mkOption {
+            default = null;
+            description = ''
+              This option can be used to store the username / password credentials
+              with the "auth-user-pass" authentication method.
+
+              WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+            '';
+            type = types.nullOr (types.submodule {
+
+              options = {
+                username = mkOption {
+                  description = "The username to store inside the credentials file.";
+                  type = types.string;
+                };
+
+                password = mkOption {
+                  description = "The password to store inside the credentials file.";
+                  type = types.string;
+                };
+              };
+            });
+          };
         };
 
       });
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index fb9c9dc67f24..1b4f81f6b56e 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -10,98 +10,229 @@ let
 
     options = {
 
-      # TODO: require attribute
       key = mkOption {
-        type = types.str;
-        description = "Path to the key file";
+        type = types.path;
+        description = "Path to the key file.";
       };
 
-      # TODO: require attribute
+      # TODO: rename to certificate to match the prosody config
       cert = mkOption {
-        type = types.str;
-        description = "Path to the certificate file";
+        type = types.path;
+        description = "Path to the certificate file.";
       };
+
+      extraOptions = mkOption {
+        type = types.attrs;
+        default = {};
+        description = "Extra SSL configuration options.";
+      };
+
     };
   };
 
   moduleOpts = {
-
+    # Generally required
     roster = mkOption {
+      type = types.bool;
       default = true;
       description = "Allow users to have a roster";
     };
 
     saslauth = mkOption {
+      type = types.bool;
       default = true;
       description = "Authentication for clients and servers. Recommended if you want to log in.";
     };
 
     tls = mkOption {
+      type = types.bool;
       default = true;
       description = "Add support for secure TLS on c2s/s2s connections";
     };
 
     dialback = mkOption {
+      type = types.bool;
       default = true;
       description = "s2s dialback support";
     };
 
     disco = mkOption {
+      type = types.bool;
       default = true;
       description = "Service discovery";
     };
 
-    legacyauth = mkOption {
+    # Not essential, but recommended
+    carbons = mkOption {
+      type = types.bool;
       default = true;
-      description = "Legacy authentication. Only used by some old clients and bots";
+      description = "Keep multiple clients in sync";
     };
 
+    pep = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enables users to publish their mood, activity, playing music and more";
+    };
+
+    private = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Private XML storage (for room bookmarks, etc.)";
+    };
+
+    blocklist = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to block communications with other users";
+    };
+
+    vcard = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to set vCards";
+    };
+
+    # Nice to have
     version = mkOption {
+      type = types.bool;
       default = true;
       description = "Replies to server version requests";
     };
 
     uptime = mkOption {
+      type = types.bool;
       default = true;
       description = "Report how long server has been running";
     };
 
     time = mkOption {
+      type = types.bool;
       default = true;
       description = "Let others know the time here on this server";
     };
 
     ping = mkOption {
+      type = types.bool;
       default = true;
       description = "Replies to XMPP pings with pongs";
     };
 
-    console = mkOption {
+    register = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allow users to register on this server using a client and change passwords";
+    };
+
+    mam = mkOption {
+      type = types.bool;
       default = false;
-      description = "telnet to port 5582";
+      description = "Store messages in an archive and allow users to access it";
     };
 
+    # Admin interfaces
+    admin_adhoc = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Allows administration via an XMPP client that supports ad-hoc commands";
+    };
+
+    admin_telnet = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Opens telnet console interface on localhost port 5582";
+    };
+
+    # HTTP modules
     bosh = mkOption {
+      type = types.bool;
       default = false;
       description = "Enable BOSH clients, aka 'Jabber over HTTP'";
     };
 
-    httpserver = mkOption {
+    websocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable WebSocket support";
+    };
+
+    http_files = mkOption {
+      type = types.bool;
       default = false;
       description = "Serve static files from a directory over HTTP";
     };
 
-    websocket = mkOption {
+    # Other specific functionality
+    limits = mkOption {
+      type = types.bool;
       default = false;
-      description = "Enable WebSocket support";
+      description = "Enable bandwidth limiting for XMPP connections";
+    };
+
+    groups = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Shared roster support";
+    };
+
+    server_contact_info = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Publish contact information for this service";
+    };
+
+    announce = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send announcement to all online users";
+    };
+
+    welcome = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Welcome users who register accounts";
+    };
+
+    watchregistrations = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Alert admins of registrations";
+    };
+
+    motd = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Send a message to users when they log in";
+    };
+
+    legacyauth = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Legacy authentication. Only used by some old clients and bots";
+    };
+
+    proxy65 = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enables a file transfer proxy service which clients behind NAT can use";
     };
 
   };
 
-  createSSLOptsStr = o:
-    if o ? key && o ? cert then
-      ''ssl = { key = "${o.key}"; certificate = "${o.cert}"; };''
-    else "";
+  toLua = x:
+    if builtins.isString x then ''"${x}"''
+    else if builtins.isBool x then (if x == true then "true" else "false")
+    else if builtins.isInt x then toString x
+    else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }''
+    else throw "Invalid Lua value";
+
+  createSSLOptsStr = o: ''
+    ssl = {
+      key = "${o.key}";
+      certificate = "${o.cert}";
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)}
+    };
+  '';
 
   vHostOpts = { ... }: {
 
@@ -114,18 +245,20 @@ let
       };
 
       enabled = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable the virtual host";
       };
 
       ssl = mkOption {
-        description = "Paths to SSL files";
+        type = types.nullOr (types.submodule sslOpts);
         default = null;
-        options = [ sslOpts ];
+        description = "Paths to SSL files";
       };
 
       extraConfig = mkOption {
-        default = '''';
+        type = types.lines;
+        default = "";
         description = "Additional virtual host specific configuration";
       };
 
@@ -144,20 +277,113 @@ in
     services.prosody = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable the prosody server";
       };
 
+      package = mkOption {
+        type = types.package;
+        description = "Prosody package to use";
+        default = pkgs.prosody;
+        defaultText = "pkgs.prosody";
+        example = literalExample ''
+          pkgs.prosody.override {
+            withExtraLibs = [ pkgs.luaPackages.lpty ];
+            withCommunityModules = [ "auth_external" ];
+          };
+        '';
+      };
+
+      dataDir = mkOption {
+        type = types.string;
+        description = "Directory where Prosody stores its data";
+        default = "/var/lib/prosody";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "User account under which prosody runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "prosody";
+        description = "Group account under which prosody runs.";
+      };
+
       allowRegistration = mkOption {
+        type = types.bool;
         default = false;
         description = "Allow account creation";
       };
 
+      c2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force clients to use encrypted connections? This option will
+          prevent clients from authenticating unless they are using encryption.
+        '';
+      };
+
+      s2sRequireEncryption = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Force servers to use encrypted connections? This option will
+          prevent servers from authenticating unless they are using encryption.
+          Note that this is different from authentication.
+        '';
+      };
+
+      s2sSecureAuth = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Force certificate authentication for server-to-server connections?
+          This provides ideal security, but requires servers you communicate
+          with to support encryption AND present valid, trusted certificates.
+          For more information see https://prosody.im/doc/s2s#security
+        '';
+      };
+
+      s2sInsecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "insecure.example.com" ];
+        description = ''
+          Some servers have invalid or self-signed certificates. You can list
+          remote domains here that will not be required to authenticate using
+          certificates. They will be authenticated using DNS instead, even
+          when s2s_secure_auth is enabled.
+        '';
+      };
+
+      s2sSecureDomains = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "jabber.org" ];
+        description = ''
+          Even if you leave s2s_secure_auth disabled, you can still require valid
+          certificates for some domains by specifying a list here.
+        '';
+      };
+
+
       modules = moduleOpts;
 
       extraModules = mkOption {
+        type = types.listOf types.str;
+        default = [];
         description = "Enable custom modules";
+      };
+
+      extraPluginPaths = mkOption {
+        type = types.listOf types.path;
         default = [];
+        description = "Addtional path in which to look find plugins/modules";
       };
 
       virtualHosts = mkOption {
@@ -183,20 +409,21 @@ in
       };
 
       ssl = mkOption {
-        description = "Paths to SSL files";
+        type = types.nullOr (types.submodule sslOpts);
         default = null;
-        options = [ sslOpts ];
+        description = "Paths to SSL files";
       };
 
       admins = mkOption {
-        description = "List of administrators of the current host";
-        example = [ "admin1@example.com" "admin2@example.com" ];
+        type = types.listOf types.str;
         default = [];
+        example = [ "admin1@example.com" "admin2@example.com" ];
+        description = "List of administrators of the current host";
       };
 
       extraConfig = mkOption {
         type = types.lines;
-        default = '''';
+        default = "";
         description = "Additional prosody configuration";
       };
 
@@ -208,37 +435,47 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.prosody ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc."prosody/prosody.cfg.lua".text = ''
 
-      pidfile = "/var/lib/prosody/prosody.pid"
-
+      pidfile = "/run/prosody/prosody.pid"
 
       log = "*syslog"
 
-      data_path = "/var/lib/prosody"
-
-      allow_registration = ${boolToString cfg.allowRegistration};
-
-      ${ optionalString cfg.modules.console "console_enabled = true;" }
+      data_path = "${cfg.dataDir}"
+      plugin_paths = {
+        ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) }
+      }
 
       ${ optionalString  (cfg.ssl != null) (createSSLOptsStr cfg.ssl) }
 
-      admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } };
+      admins = ${toLua cfg.admins}
+
+      -- we already build with libevent, so we can just enable it for a more performant server
+      use_libevent = true
 
       modules_enabled = {
 
         ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList
-          (name: val: optionalString val ''"${name}";'')
+          (name: val: optionalString val "${toLua name};")
         cfg.modules) }
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)}
+        ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
+      };
 
-        ${ optionalString cfg.allowRegistration "\"register\"\;" }
+      allow_registration = ${toLua cfg.allowRegistration}
 
-        ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)}
+      c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
+
+      s2s_require_encryption = ${toLua cfg.s2sRequireEncryption}
+
+      s2s_secure_auth = ${toLua cfg.s2sSecureAuth}
+
+      s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains}
+
+      s2s_secure_domains = ${toLua cfg.s2sSecureDomains}
 
-        "posix";
-      };
 
       ${ cfg.extraConfig }
 
@@ -250,30 +487,32 @@ in
         '') cfg.virtualHosts) }
     '';
 
-    users.extraUsers.prosody = {
+    users.extraUsers.prosody = mkIf (cfg.user == "prosody") {
       uid = config.ids.uids.prosody;
       description = "Prosody user";
       createHome = true;
-      group = "prosody";
-      home = "/var/lib/prosody";
+      inherit (cfg) group;
+      home = "${cfg.dataDir}";
     };
 
-    users.extraGroups.prosody = {
+    users.extraGroups.prosody = mkIf (cfg.group == "prosody") {
       gid = config.ids.gids.prosody;
     };
 
     systemd.services.prosody = {
-
       description = "Prosody XMPP server";
       after = [ "network-online.target" ];
       wants = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
       serviceConfig = {
-        User = "prosody";
-        PIDFile = "/var/lib/prosody/prosody.pid";
-        ExecStart = "${pkgs.prosody}/bin/prosodyctl start";
+        User = cfg.user;
+        Group = cfg.group;
+        Type = "forking";
+        RuntimeDirectory = [ "prosody" ];
+        PIDFile = "/run/prosody/prosody.pid";
+        ExecStart = "${cfg.package}/bin/prosodyctl start";
       };
-
     };
 
   };
diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix
index aab58cc77b90..22204e53203c 100644
--- a/nixos/modules/services/networking/quagga.nix
+++ b/nixos/modules/services/networking/quagga.nix
@@ -133,7 +133,7 @@ in
     users.groups = {
       quagga = {};
       # Members of the quaggavty group can use vtysh to inspect the Quagga daemons
-      quaggavty = {};
+      quaggavty = { members = [ "quagga" ]; };
     };
 
     systemd.services =
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index 0199502163a3..85d7f9e4a41b 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -59,24 +59,11 @@ in
 
     systemd.services.radvd =
       { description = "IPv6 Router Advertisement Daemon";
-
         wantedBy = [ "multi-user.target" ];
-
         after = [ "network.target" ];
-
-        path = [ pkgs.radvd ];
-
-        preStart = ''
-          mkdir -m 755 -p /run/radvd
-          chown radvd /run/radvd
-        '';
-
         serviceConfig =
-          { ExecStart = "@${pkgs.radvd}/sbin/radvd radvd"
-              + " -p /run/radvd/radvd.pid -m syslog -u radvd -C ${confFile}";
+          { ExecStart = "@${pkgs.radvd}/bin/radvd radvd -n -u radvd -C ${confFile}";
             Restart = "always";
-            Type = "forking";
-            PIDFile = "/run/radvd/radvd.pid";
           };
       };
 
diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix
index 95833d31e99d..a102242eae71 100644
--- a/nixos/modules/services/networking/rdnssd.nix
+++ b/nixos/modules/services/networking/rdnssd.nix
@@ -6,7 +6,7 @@
 with lib;
 let
   mergeHook = pkgs.writeScript "rdnssd-merge-hook" ''
-    #! ${pkgs.stdenv.shell} -e
+    #! ${pkgs.runtimeShell} -e
     ${pkgs.openresolv}/bin/resolvconf -u
   '';
 in
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index 6d2b7bdbca1b..2956a5ecbc04 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -17,7 +17,7 @@ let
 
     search_lan = entry.searchLAN;
     use_sync_trash = entry.useSyncTrash;
-    known_hosts = knownHosts;
+    known_hosts = entry.knownHosts;
   }) cfg.sharedFolders;
 
   configFile = pkgs.writeText "config.json" (builtins.toJSON ({
@@ -50,12 +50,7 @@ in
         description = ''
           If enabled, start the Resilio Sync daemon. Once enabled, you can
           interact with the service through the Web UI, or configure it in your
-          NixOS configuration. Enabling the <literal>resilio</literal> service
-          also installs a systemd user unit which can be used to start
-          user-specific copies of the daemon. Once installed, you can use
-          <literal>systemctl --user start resilio</literal> as your user to start
-          the daemon using the configuration file located at
-          <literal>$HOME/.config/resilio-sync/config.json</literal>.
+          NixOS configuration.
         '';
       };
 
diff --git a/nixos/modules/services/networking/rxe.nix b/nixos/modules/services/networking/rxe.nix
new file mode 100644
index 000000000000..a6a069ec50c0
--- /dev/null
+++ b/nixos/modules/services/networking/rxe.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.rxe;
+
+  runRxeCmd = cmd: ifcs:
+    concatStrings ( map (x: "${pkgs.rdma-core}/bin/rxe_cfg -n ${cmd} ${x};") ifcs);
+
+  startScript = pkgs.writeShellScriptBin "rxe-start" ''
+    ${pkgs.rdma-core}/bin/rxe_cfg -n start
+    ${runRxeCmd "add" cfg.interfaces}
+    ${pkgs.rdma-core}/bin/rxe_cfg
+  '';
+
+  stopScript = pkgs.writeShellScriptBin "rxe-stop" ''
+    ${runRxeCmd "remove" cfg.interfaces }
+    ${pkgs.rdma-core}/bin/rxe_cfg -n stop
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    networking.rxe = {
+      enable = mkEnableOption "RDMA over converged ethernet";
+      interfaces = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "eth0" ];
+        description = ''
+          Enable RDMA on the listed interfaces. The corresponding virtual
+          RDMA interfaces will be named rxe0 ... rxeN where the ordering
+          will be as they are named in the list. UDP port 4791 must be
+          open on the respective ethernet interfaces.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.rxe = {
+      path = with pkgs; [ kmod rdma-core ];
+      description = "RoCE interfaces";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-modules-load.service" "network-online.target" ];
+      wants = [ "network-pre.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${startScript}/bin/rxe-start";
+        ExecStop = "${stopScript}/bin/rxe-stop";
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/networking/shadowsocks.nix b/nixos/modules/services/networking/shadowsocks.nix
new file mode 100644
index 000000000000..fe6d65a5f963
--- /dev/null
+++ b/nixos/modules/services/networking/shadowsocks.nix
@@ -0,0 +1,112 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.shadowsocks;
+
+  opts = {
+    server = cfg.localAddress;
+    server_port = cfg.port;
+    method = cfg.encryptionMethod;
+    mode = cfg.mode;
+    user = "nobody";
+    fast_open = true;
+  } // optionalAttrs (cfg.password != null) { password = cfg.password; };
+
+  configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.shadowsocks = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run shadowsocks-libev shadowsocks server.
+        '';
+      };
+
+      localAddress = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        description = ''
+          Local address to which the server binds.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 8388;
+        description = ''
+          Port which the server uses.
+        '';
+      };
+
+      password = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Password for connecting clients.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Password file with a password for connecting clients.
+        '';
+      };
+
+      mode = mkOption {
+        type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ];
+        default = "tcp_and_udp";
+        description = ''
+          Relay protocols.
+        '';
+      };
+
+      encryptionMethod = mkOption {
+        type = types.str;
+        default = "chacha20-ietf-poly1305";
+        description = ''
+          Encryption method. See <link xlink:href="https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers"/>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    assertions = singleton
+      { assertion = cfg.password == null || cfg.passwordFile == null;
+        message = "Cannot use both password and passwordFile for shadowsocks-libev";
+      };
+
+    systemd.services.shadowsocks-libev = {
+      description = "shadowsocks-libev Daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq;
+      serviceConfig.PrivateTmp = true;
+      script = ''
+        ${optionalString (cfg.passwordFile != null) ''
+          cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
+        ''}
+        exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix
index 9087b75c29c1..65df93a00da9 100644
--- a/nixos/modules/services/networking/softether.nix
+++ b/nixos/modules/services/networking/softether.nix
@@ -5,6 +5,8 @@ with lib;
 let
   cfg = config.services.softether;
 
+  package = cfg.package.override { dataDir = cfg.dataDir; };
+
 in
 {
 
@@ -49,7 +51,7 @@ in
 
       dataDir = mkOption {
         type = types.string;
-        default = "${cfg.package.dataDir}";
+        default = "/var/lib/softether";
         description = ''
           Data directory for SoftEther VPN.
         '';
@@ -64,11 +66,8 @@ in
   config = mkIf cfg.enable (
 
     mkMerge [{
-      environment.systemPackages = [
-          (pkgs.lib.overrideDerivation cfg.package (attrs: {
-            dataDir = cfg.dataDir;
-          }))
-        ];
+      environment.systemPackages = [ package ];
+
       systemd.services."softether-init" = {
         description = "SoftEther VPN services initial task";
         wantedBy = [ "network.target" ];
@@ -80,11 +79,11 @@ in
             for d in vpnserver vpnbridge vpnclient vpncmd; do
                 if ! test -e ${cfg.dataDir}/$d; then
                     ${pkgs.coreutils}/bin/mkdir -m0700 -p ${cfg.dataDir}/$d
-                    install -m0600 ${cfg.package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2
+                    install -m0600 ${package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2
                 fi
             done
             rm -rf ${cfg.dataDir}/vpncmd/vpncmd
-            ln -s ${cfg.package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd
+            ln -s ${package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd
         '';
       };
     }
@@ -97,12 +96,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnserver start";
-          ExecStop = "${cfg.package}/bin/vpnserver stop";
+          ExecStart = "${package}/bin/vpnserver start";
+          ExecStop = "${package}/bin/vpnserver stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnserver/vpnserver
-            ln -s ${cfg.package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver
+            ln -s ${package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver
         '';
         postStop = ''
             rm -rf ${cfg.dataDir}/vpnserver/vpnserver
@@ -118,12 +117,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnbridge start";
-          ExecStop = "${cfg.package}/bin/vpnbridge stop";
+          ExecStart = "${package}/bin/vpnbridge start";
+          ExecStop = "${package}/bin/vpnbridge stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
-            ln -s ${cfg.package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge
+            ln -s ${package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge
         '';
         postStop = ''
             rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge
@@ -139,12 +138,12 @@ in
         wantedBy = [ "network.target" ];
         serviceConfig = {
           Type = "forking";
-          ExecStart = "${cfg.package}/bin/vpnclient start";
-          ExecStop = "${cfg.package}/bin/vpnclient stop";
+          ExecStart = "${package}/bin/vpnclient start";
+          ExecStop = "${package}/bin/vpnclient stop";
         };
         preStart = ''
             rm -rf ${cfg.dataDir}/vpnclient/vpnclient
-            ln -s ${cfg.package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient
+            ln -s ${package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient
         '';
         postStart = ''
             sleep 1
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 8828429a8178..aab1203086ce 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -21,7 +21,7 @@ let
           daemon reads in addition to the the user's authorized_keys file.
           You can combine the <literal>keys</literal> and
           <literal>keyFiles</literal> options.
-          Warning: If you are using <literal>NixOps</literal> then don't use this 
+          Warning: If you are using <literal>NixOps</literal> then don't use this
           option since it will replace the key required for deployment via ssh.
         '';
       };
@@ -54,8 +54,6 @@ let
     ));
   in listToAttrs (map mkAuthKeyFile usersWithKeys);
 
-  supportOldHostKeys = !versionAtLeast config.system.stateVersion "15.07";
-
 in
 
 {
@@ -139,6 +137,14 @@ in
         '';
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically open the specified ports in the firewall.
+        '';
+      };
+
       listenAddresses = mkOption {
         type = with types; listOf (submodule {
           options = {
@@ -191,9 +197,6 @@ in
         default =
           [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; }
             { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
-          ] ++ optionals supportOldHostKeys
-          [ { type = "dsa"; path = "/etc/ssh/ssh_host_dsa_key"; }
-            { type = "ecdsa"; bits = 521; path = "/etc/ssh/ssh_host_ecdsa_key"; }
           ];
         description = ''
           NixOS can automatically generate SSH host keys.  This option
@@ -210,6 +213,65 @@ in
         description = "Files from which authorized keys are read.";
       };
 
+      kexAlgorithms = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "curve25519-sha256@libssh.org"
+          "diffie-hellman-group-exchange-sha256"
+        ];
+        description = ''
+          Allowed key exchange algorithms
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      ciphers = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "chacha20-poly1305@openssh.com"
+          "aes256-gcm@openssh.com"
+          "aes128-gcm@openssh.com"
+          "aes256-ctr"
+          "aes192-ctr"
+          "aes128-ctr"
+        ];
+        description = ''
+          Allowed ciphers
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
+      macs = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "hmac-sha2-512-etm@openssh.com"
+          "hmac-sha2-256-etm@openssh.com"
+          "umac-128-etm@openssh.com"
+          "hmac-sha2-512"
+          "hmac-sha2-256"
+          "umac-128@openssh.com"
+        ];
+        description = ''
+          Allowed MACs
+          </para>
+          <para>
+          Defaults to recommended settings from both
+          <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" />
+          and
+          <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" />
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -253,13 +315,10 @@ in
       let
         service =
           { description = "SSH Daemon";
-
             wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target";
-
+            after = [ "network.target" ];
             stopIfChanged = false;
-
             path = [ cfgc.package pkgs.gawk ];
-
             environment.LD_LIBRARY_PATH = nssModulesPath;
 
             preStart =
@@ -310,7 +369,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = cfg.ports;
+    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
 
     security.pam.services.sshd =
       { startSession = true;
@@ -363,14 +422,13 @@ in
           HostKey ${k.path}
         '')}
 
-        # Allow DSA client keys for now. (These were deprecated
-        # in OpenSSH 7.0.)
-        PubkeyAcceptedKeyTypes +ssh-dss
+        KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
+        Ciphers ${concatStringsSep "," cfg.ciphers}
+        MACs ${concatStringsSep "," cfg.macs}
 
-        # Re-enable DSA host keys for now.
-        ${optionalString supportOldHostKeys ''
-          HostKeyAlgorithms +ssh-dss
-        ''}
+        # LogLevel VERBOSE logs user's key fingerprint on login.
+        # Needed to have a clear audit track of which key was used to log in.
+        LogLevel VERBOSE
       '';
 
     assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
diff --git a/nixos/modules/services/networking/strongswan.nix b/nixos/modules/services/networking/strongswan.nix
index 3a3f64221c42..707d24b9220f 100644
--- a/nixos/modules/services/networking/strongswan.nix
+++ b/nixos/modules/services/networking/strongswan.nix
@@ -32,13 +32,13 @@ let
       ${caConf}
     '';
 
-  strongswanConf = {setup, connections, ca, secrets, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
+  strongswanConf = {setup, connections, ca, secretsFile, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
     charon {
       ${if managePlugins then "load_modular = no" else ""}
       ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""}
       plugins {
         stroke {
-          secrets_file = ${ipsecSecrets secrets}
+          secrets_file = ${secretsFile}
         }
       }
     }
@@ -135,7 +135,18 @@ in
     };
   };
 
-  config = with cfg; mkIf enable {
+
+  config = with cfg;
+  let
+    secretsFile = ipsecSecrets cfg.secrets;
+  in
+  mkIf enable
+    {
+
+    # here we should use the default strongswan ipsec.secrets and
+    # append to it (default one is empty so not a pb for now)
+    environment.etc."ipsec.secrets".source = secretsFile;
+
     systemd.services.strongswan = {
       description = "strongSwan IPSec Service";
       wantedBy = [ "multi-user.target" ];
@@ -143,11 +154,15 @@ in
       wants = [ "keys.target" ];
       after = [ "network-online.target" "keys.target" ];
       environment = {
-        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secrets managePlugins enabledPlugins; };
+        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; };
       };
       serviceConfig = {
         ExecStart  = "${pkgs.strongswan}/sbin/ipsec start --nofork";
       };
+      preStart = ''
+        # with 'nopeerdns' setting, ppp writes into this folder
+        mkdir -m 700 -p /etc/ppp
+      '';
     };
   };
 }
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
new file mode 100644
index 000000000000..89a14966eca7
--- /dev/null
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -0,0 +1,221 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.stunnel;
+  yesNo = val: if val then "yes" else "no";
+
+  verifyChainPathAssert = n: c: {
+    assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+    message =  "stunnel: \"${n}\" client configuration - hostname verification " +
+      "is not possible without either verifyChain or verifyPeer enabled";
+  };
+
+  serverConfig = {
+    options = {
+      accept = mkOption {
+        type = types.int;
+        description = "On which port stunnel should listen for incoming TLS connections.";
+      };
+
+      connect = mkOption {
+        type = types.int;
+        description = "To which port the decrypted connection should be forwarded.";
+      };
+
+      cert = mkOption {
+        type = types.path;
+        description = "File containing both the private and public keys.";
+      };
+    };
+  };
+
+  clientConfig = {
+    options = {
+      accept = mkOption {
+        type = types.string;
+        description = "IP:Port on which connections should be accepted.";
+      };
+
+      connect = mkOption {
+        type = types.string;
+        description = "IP:Port destination to connect to.";
+      };
+
+      verifyChain = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
+      };
+
+      verifyPeer = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Check if the provided certificate is contained in CAPath.";
+      };
+
+      CAPath = mkOption {
+        type = types.path;
+        default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+        description = "Path to a file containing certificates to validate against.";
+      };
+
+      verifyHostname = mkOption {
+        type = with types; nullOr string;
+        default = null;
+        description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
+      };
+    };
+  };
+
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.stunnel = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the stunnel TLS tunneling service.";
+      };
+
+      user = mkOption {
+        type = with types; nullOr string;
+        default = "nobody";
+        description = "The user under which stunnel runs.";
+      };
+
+      group = mkOption {
+        type = with types; nullOr string;
+        default = "nogroup";
+        description = "The group under which stunnel runs.";
+      };
+
+      logLevel = mkOption {
+        type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ];
+        default = "info";
+        description = "Verbosity of stunnel output.";
+      };
+
+      fipsMode = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable FIPS 140-2 mode required for compliance.";
+      };
+
+      enableInsecureSSLv3 = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable support for the insecure SSLv3 protocol.";
+      };
+
+
+      servers = mkOption {
+        description = "Define the server configuations.";
+        type = with types; attrsOf (submodule serverConfig);
+        example = {
+          fancyWebserver = {
+            enable = true;
+            accept = 443;
+            connect = 8080;
+            cert = "/path/to/pem/file";
+          };
+        };
+        default = { };
+      };
+
+      clients = mkOption {
+        description = "Define the client configurations.";
+        type = with types; attrsOf (submodule clientConfig);
+        example = {
+          foobar = {
+            accept = "0.0.0.0:8080";
+            connect = "nixos.org:443";
+            verifyChain = false;
+          };
+        };
+        default = { };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = concatLists [
+      (singleton {
+        assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0);
+        message = "stunnel: At least one server- or client-configuration has to be present.";
+      })
+
+      (mapAttrsToList verifyChainPathAssert cfg.clients)
+    ];
+
+    environment.systemPackages = [ pkgs.stunnel ];
+
+    environment.etc."stunnel.cfg".text = ''
+      ${ if cfg.user != null then "setuid = ${cfg.user}" else "" }
+      ${ if cfg.group != null then "setgid = ${cfg.group}" else "" }
+
+      debug = ${cfg.logLevel}
+
+      ${ optionalString cfg.fipsMode "fips = yes" }
+      ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
+
+      ; ----- SERVER CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               accept = ${toString v.accept}
+               connect = ${toString v.connect}
+               cert = ${v.cert}
+
+             '')
+           cfg.servers)
+      }
+
+      ; ----- CLIENT CONFIGURATIONS -----
+      ${ lib.concatStringsSep "\n"
+           (lib.mapAttrsToList
+             (n: v: ''
+               [${n}]
+               client = yes
+               accept = ${v.accept}
+               connect = ${v.connect}
+               verifyChain = ${yesNo v.verifyChain}
+               verifyPeer = ${yesNo v.verifyPeer}
+               ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
+               ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
+               OCSPaia = yes
+
+             '')
+           cfg.clients)
+      }
+    '';
+
+    systemd.services.stunnel = {
+      description = "stunnel TLS tunneling service";
+      after = [ "network.target" ];
+      wants = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ config.environment.etc."stunnel.cfg".source ];
+      serviceConfig = {
+        ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}";
+        Type = "forking";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index dcdc203bdc62..e485c073cbdd 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -16,12 +16,6 @@ in {
         available on http://127.0.0.1:8384/.
       '';
 
-      useInotify = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Provide syncthing-inotify as a service.";
-      };
-
       systemService = mkOption {
         type = types.bool;
         default = true;
@@ -90,6 +84,12 @@ in {
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule ["services" "syncthing" "useInotify"] ''
+      This option was removed because syncthing now has the inotify functionality included under the name "fswatcher".
+      It can be enabled on a per-folder basis through the webinterface.
+    '')
+  ];
 
   ###### implementation
 
@@ -100,8 +100,7 @@ in {
       allowedUDPPorts = [ 21027 ];
     };
 
-    systemd.packages = [ pkgs.syncthing ]
-                       ++ lib.optional cfg.useInotify pkgs.syncthing-inotify;
+    systemd.packages = [ pkgs.syncthing ];
 
     users = mkIf (cfg.user == defaultUser) {
       extraUsers."${defaultUser}" =
@@ -125,7 +124,6 @@ in {
           STNOUPGRADE = "yes";
           inherit (cfg) all_proxy;
         } // config.networking.proxy.envVars;
-        wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ];
         wantedBy = [ "multi-user.target" ];
         serviceConfig = {
           Restart = "on-failure";
@@ -141,20 +139,6 @@ in {
       syncthing-resume = {
         wantedBy = [ "suspend.target" ];
       };
-
-      syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) {
-        description = "Syncthing Inotify File Watcher service";
-        after = [ "network.target" "syncthing.service" ];
-        requires = [ "syncthing.service" ];
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          SuccessExitStatus = "2";
-          RestartForceExitStatus = "3";
-          Restart = "on-failure";
-          User = cfg.user;
-          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0";
-        };
-      };
     };
   };
 }
diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix
index 2f304165eb4b..ee005e11aa32 100644
--- a/nixos/modules/services/networking/tcpcrypt.nix
+++ b/nixos/modules/services/networking/tcpcrypt.nix
@@ -44,9 +44,9 @@ in
       path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ];
 
       preStart = ''
-        mkdir -p /var/run/tcpcryptd
-        chown tcpcryptd /var/run/tcpcryptd
-        sysctl -n net.ipv4.tcp_ecn >/run/pre-tcpcrypt-ecn-state
+        mkdir -p /run/tcpcryptd
+        chown tcpcryptd /run/tcpcryptd
+        sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state
         sysctl -w net.ipv4.tcp_ecn=0
 
         iptables -t raw -N nixos-tcpcrypt
@@ -61,8 +61,8 @@ in
       script = "tcpcryptd -x 0x10";
 
       postStop = ''
-        if [ -f /run/pre-tcpcrypt-ecn-state ]; then
-          sysctl -w net.ipv4.tcp_ecn=$(cat /run/pre-tcpcrypt-ecn-state)
+        if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then
+          sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state)
         fi
 
         iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index adb80ea29840..e3c9b5282b8c 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -178,6 +178,8 @@ in
         preStart = ''
           mkdir -p /etc/tinc/${network}/hosts
           chown tinc.${network} /etc/tinc/${network}/hosts
+          mkdir -p /etc/tinc/${network}/invitations
+          chown tinc.${network} /etc/tinc/${network}/invitations
 
           # Determine how we should generate our keys
           if type tinc >/dev/null 2>&1; then
diff --git a/nixos/modules/services/networking/tox-bootstrapd.nix b/nixos/modules/services/networking/tox-bootstrapd.nix
index c1f945773e23..cb0e6b158651 100644
--- a/nixos/modules/services/networking/tox-bootstrapd.nix
+++ b/nixos/modules/services/networking/tox-bootstrapd.nix
@@ -69,7 +69,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig =
-        { ExecStart = "${pkg}/bin/tox-bootstrapd ${cfgFile}";
+        { ExecStart = "${pkg}/bin/tox-bootstrapd --config=${cfgFile}";
           Type = "forking";
           inherit PIDFile;
           User = "tox-bootstrapd";
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index 545ee327d596..f069a9883a7f 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -112,7 +112,7 @@ in
         mkdir -m 0755 -p ${stateDir}/dev/
         cp ${confFile} ${stateDir}/unbound.conf
         ${optionalString cfg.enableRootTrustAnchor ''
-        ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile}
+        ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
         chown unbound ${stateDir} ${rootTrustAnchorFile}
         ''}
         touch ${stateDir}/dev/random
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 86e0204ec2f7..cd1617b8e2ba 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -7,6 +7,16 @@ let
 in
 {
   options.services.zerotierone.enable = mkEnableOption "ZeroTierOne";
+
+  options.services.zerotierone.joinNetworks = mkOption {
+    default = [];
+    example = [ "a8a2c3c10c1a68de" ];
+    type = types.listOf types.str;
+    description = ''
+      List of ZeroTier Network IDs to join on startup
+    '';
+  };
+
   options.services.zerotierone.package = mkOption {
     default = pkgs.zerotierone;
     defaultText = "pkgs.zerotierone";
@@ -22,12 +32,13 @@ in
       path = [ cfg.package ];
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      preStart =
-        ''
-        mkdir -p /var/lib/zerotier-one
+      preStart = ''
+        mkdir -p /var/lib/zerotier-one/networks.d
         chmod 700 /var/lib/zerotier-one
         chown -R root:root /var/lib/zerotier-one
-        '';
+      '' + (concatMapStrings (netId: ''
+        touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
+      '') cfg.joinNetworks);
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/zerotier-one";
         Restart = "always";
@@ -38,6 +49,9 @@ in
     # ZeroTier does not issue DHCP leases, but some strangers might...
     networking.dhcpcd.denyInterfaces = [ "zt0" ];
 
+    # ZeroTier receives UDP transmissions on port 9993 by default
+    networking.firewall.allowedUDPPorts = [ 9993 ];
+
     environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 4c7f58d1d8bc..ecab8cfc7df9 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -124,7 +124,7 @@ in
 
       listenAddresses = mkOption {
         type = types.listOf types.str;
-        default = [ "127.0.0.1:631" ];
+        default = [ "localhost:631" ];
         example = [ "*:631" ];
         description = ''
           A list of addresses and ports on which to listen.
@@ -321,7 +321,10 @@ in
             ''}
           '';
 
-          serviceConfig.PrivateTmp = true;
+          serviceConfig = {
+            PrivateTmp = true;
+            RuntimeDirectory = [ "cups" ];
+          };
       };
 
     systemd.services.cups-browsed = mkIf avahiEnabled
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
index ac589be57736..e3b6b638f5a7 100644
--- a/nixos/modules/services/scheduling/fcron.nix
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -90,16 +90,24 @@ in
       [ (allowdeny "allow" (cfg.allow))
         (allowdeny "deny" cfg.deny)
         # see man 5 fcron.conf
-        { source = pkgs.writeText "fcron.conf" ''
-            fcrontabs   =       /var/spool/fcron
-            pidfile     =       /var/run/fcron.pid
-            fifofile    =       /var/run/fcron.fifo
-            fcronallow  =       /etc/fcron.allow
-            fcrondeny   =       /etc/fcron.deny
-            shell       =       /bin/sh
-            sendmail    =       /run/wrappers/bin/sendmail
-            editor      =       ${pkgs.vim}/bin/vim
-          '';
+        { source =
+            let
+              isSendmailWrapped =
+                lib.hasAttr "sendmail" config.security.wrappers;
+              sendmailPath =
+                if isSendmailWrapped then "/run/wrappers/bin/sendmail"
+                else "${config.system.path}/bin/sendmail";
+            in
+            pkgs.writeText "fcron.conf" ''
+              fcrontabs   =       /var/spool/fcron
+              pidfile     =       /var/run/fcron.pid
+              fifofile    =       /var/run/fcron.fifo
+              fcronallow  =       /etc/fcron.allow
+              fcrondeny   =       /etc/fcron.deny
+              shell       =       /bin/sh
+              sendmail    =       ${sendmailPath}
+              editor      =       ${pkgs.vim}/bin/vim
+            '';
           target = "fcron.conf";
           gid = config.ids.gids.fcron;
           mode = "0644";
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index c51dd5d94655..d61f588205af 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -6,6 +6,7 @@ let
   cfg = config.services.elasticsearch;
 
   es5 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "5" >= 0;
+  es6 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "6" >= 0;
 
   esConfig = ''
     network.host: ${cfg.listenAddress}
@@ -31,8 +32,11 @@ let
       (if es5 then (pkgs.writeTextDir "log4j2.properties" cfg.logging)
               else (pkgs.writeTextDir "logging.yml" cfg.logging))
     ];
-    # Elasticsearch 5.x won't start when the scripts directory does not exist
-    postBuild = if es5 then "${pkgs.coreutils}/bin/mkdir -p $out/scripts" else "";
+    postBuild = concatStringsSep "\n" (concatLists [
+      # Elasticsearch 5.x won't start when the scripts directory does not exist
+      (optional es5 "${pkgs.coreutils}/bin/mkdir -p $out/scripts")
+      (optional es6 "ln -s ${cfg.package}/config/jvm.options $out/jvm.options")
+    ]);
   };
 
   esPlugins = pkgs.buildEnv {
@@ -92,8 +96,6 @@ in {
         node.name: "elasticsearch"
         node.master: true
         node.data: false
-        index.number_of_shards: 5
-        index.number_of_replicas: 1
       '';
     };
 
@@ -165,7 +167,10 @@ in {
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
-        ES_JAVA_OPTS = toString ([ "-Des.path.conf=${configDir}" ] ++ cfg.extraJavaOptions);
+        ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
+                                  ++ cfg.extraJavaOptions);
+      } // optionalAttrs es6 {
+        ES_PATH_CONF = configDir;
       };
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index f71f30fee97a..4161c61ed375 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -76,8 +76,9 @@ in
     };
   };
 
-  config = mkIf cfg.updater.enable or cfg.daemon.enable {
+  config = mkIf (cfg.updater.enable || cfg.daemon.enable) {
     environment.systemPackages = [ pkg ];
+
     users.extraUsers = singleton {
       name = clamavUser;
       uid = config.ids.uids.clamav;
@@ -94,10 +95,10 @@ in
     environment.etc."clamav/freshclam.conf".source = freshclamConfigFile;
     environment.etc."clamav/clamd.conf".source = clamdConfigFile;
 
-    systemd.services.clamav-daemon = mkIf cfg.daemon.enable {
+    systemd.services.clamav-daemon = optionalAttrs cfg.daemon.enable {
       description = "ClamAV daemon (clamd)";
-      after = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
-      requires = mkIf cfg.updater.enable [ "clamav-freshclam.service" ];
+      after = optional cfg.updater.enable "clamav-freshclam.service";
+      requires = optional cfg.updater.enable "clamav-freshclam.service";
       wantedBy = [ "multi-user.target" ];
       restartTriggers = [ clamdConfigFile ];
 
@@ -115,7 +116,7 @@ in
       };
     };
 
-    systemd.timers.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.timers.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "Timer for ClamAV virus database updater (freshclam)";
       wantedBy = [ "timers.target" ];
       timerConfig = {
@@ -124,7 +125,7 @@ in
       };
     };
 
-    systemd.services.clamav-freshclam = mkIf cfg.updater.enable {
+    systemd.services.clamav-freshclam = optionalAttrs cfg.updater.enable {
       description = "ClamAV virus database updater (freshclam)";
       restartTriggers = [ freshclamConfigFile ];
 
diff --git a/nixos/modules/services/security/hologram-agent.nix b/nixos/modules/services/security/hologram-agent.nix
index 6c53a2df6306..39ed506f7617 100644
--- a/nixos/modules/services/security/hologram-agent.nix
+++ b/nixos/modules/services/security/hologram-agent.nix
@@ -35,10 +35,9 @@ in {
   config = mkIf cfg.enable {
     boot.kernelModules = [ "dummy" ];
 
-    networking.interfaces.dummy0 = {
-      ipAddress = "169.254.169.254";
-      prefixLength = 32;
-    };
+    networking.interfaces.dummy0.ipv4.addresses = [
+      { address = "169.254.169.254"; prefixLength = 32; }
+    ];
 
     systemd.services.hologram-agent = {
       description = "Provide EC2 instance credentials to machines outside of EC2";
diff --git a/nixos/modules/services/security/hologram-server.nix b/nixos/modules/services/security/hologram-server.nix
index e267fed27955..bad02c7440ba 100644
--- a/nixos/modules/services/security/hologram-server.nix
+++ b/nixos/modules/services/security/hologram-server.nix
@@ -12,16 +12,20 @@ let
         dn       = cfg.ldapBindDN;
         password = cfg.ldapBindPassword;
       };
-      insecureldap = cfg.ldapInsecure;
-      userattr     = cfg.ldapUserAttr;
-      baseDN       = cfg.ldapBaseDN;
+      insecureldap    = cfg.ldapInsecure;
+      userattr        = cfg.ldapUserAttr;
+      baseDN          = cfg.ldapBaseDN;
+      enableldapRoles = cfg.enableLdapRoles;
+      roleAttr        = cfg.roleAttr;
+      groupClassAttr  = cfg.groupClassAttr;
     };
     aws = {
       account     = cfg.awsAccount;
       defaultrole = cfg.awsDefaultRole;
     };
-    stats  = cfg.statsAddress;
-    listen = cfg.listenAddress;
+    stats        = cfg.statsAddress;
+    listen       = cfg.listenAddress;
+    cachetimeout = cfg.cacheTimeoutSeconds;
   });
 in {
   options = {
@@ -70,6 +74,24 @@ in {
         description = "Password of account to use to query the LDAP server";
       };
 
+      enableLdapRoles = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = "Whether to assign user roles based on the user's LDAP group memberships";
+      };
+
+      groupClassAttr = mkOption {
+        type = types.str;
+        default = "groupOfNames";
+        description = "The objectclass attribute to search for groups when enableLdapRoles is true";
+      };
+
+      roleAttr = mkOption {
+        type        = types.str;
+        default     = "businessCategory";
+        description = "Which LDAP group attribute to search for authorized role ARNs";
+      };
+
       awsAccount = mkOption {
         type        = types.str;
         description = "AWS account number";
@@ -85,6 +107,12 @@ in {
         default     = "";
         description = "Address of statsd server";
       };
+
+      cacheTimeoutSeconds = mkOption {
+        type        = types.int;
+        default     = 3600;
+        description = "How often (in seconds) to refresh the LDAP cache";
+      };
     };
   };
 
diff --git a/nixos/modules/services/security/physlock.nix b/nixos/modules/services/security/physlock.nix
index 30224d7fc6ba..97fbd6aae6e0 100644
--- a/nixos/modules/services/security/physlock.nix
+++ b/nixos/modules/services/security/physlock.nix
@@ -30,6 +30,20 @@ in
         '';
       };
 
+      allowAnyUser = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to allow any user to lock the screen. This will install a
+          setuid wrapper to allow any user to start physlock as root, which
+          is a minor security risk. Call the physlock binary to use this instead
+          of using the systemd service.
+
+          Note that you might need to relog to have the correct binary in your
+          PATH upon changing this option.
+        '';
+      };
+
       disableSysRq = mkOption {
         type = types.bool;
         default = true;
@@ -79,28 +93,36 @@ in
 
   ###### implementation
 
-  config = mkIf cfg.enable {
-
-    # for physlock -l and physlock -L
-    environment.systemPackages = [ pkgs.physlock ];
-
-    systemd.services."physlock" = {
-      enable = true;
-      description = "Physlock";
-      wantedBy = optional cfg.lockOn.suspend   "suspend.target"
-              ++ optional cfg.lockOn.hibernate "hibernate.target"
-              ++ cfg.lockOn.extraTargets;
-      before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
-              ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
-              ++ cfg.lockOn.extraTargets;
-      serviceConfig.Type = "forking";
-      script = ''
-        ${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}
-      '';
-    };
+  config = mkIf cfg.enable (mkMerge [
+    {
+
+      # for physlock -l and physlock -L
+      environment.systemPackages = [ pkgs.physlock ];
+
+      systemd.services."physlock" = {
+        enable = true;
+        description = "Physlock";
+        wantedBy = optional cfg.lockOn.suspend   "suspend.target"
+                ++ optional cfg.lockOn.hibernate "hibernate.target"
+                ++ cfg.lockOn.extraTargets;
+        before   = optional cfg.lockOn.suspend   "systemd-suspend.service"
+                ++ optional cfg.lockOn.hibernate "systemd-hibernate.service"
+                ++ cfg.lockOn.extraTargets;
+        serviceConfig = {
+          Type = "forking";
+          ExecStart = "${pkgs.physlock}/bin/physlock -d${optionalString cfg.disableSysRq "s"}";
+        };
+      };
 
-    security.pam.services.physlock = {};
+      security.pam.services.physlock = {};
 
-  };
+    }
+
+    (mkIf cfg.allowAnyUser {
+
+      security.wrappers.physlock = { source = "${pkgs.physlock}/bin/physlock"; user = "root"; };
+
+    })
+  ]);
 
 }
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index bc79d9f2a590..806252f49b8d 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -5,10 +5,31 @@ with lib;
 let
   cfg = config.services.tor;
   torDirectory = "/var/lib/tor";
+  torRunDirectory = "/run/tor";
 
   opt    = name: value: optionalString (value != null) "${name} ${value}";
   optint = name: value: optionalString (value != null && value != 0)    "${name} ${toString value}";
 
+  isolationOptions = {
+    type = types.listOf (types.enum [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ]);
+    default = [];
+    example = [
+      "IsolateClientAddr"
+      "IsolateSOCKSAuth"
+      "IsolateClientProtocol"
+      "IsolateDestPort"
+      "IsolateDestAddr"
+    ];
+    description = "Tor isolation options";
+  };
+
+
   torRc = ''
     User tor
     DataDirectory ${torDirectory}
@@ -18,12 +39,23 @@ let
     ''}
 
     ${optint "ControlPort" cfg.controlPort}
+    ${optionalString cfg.controlSocket.enable "ControlSocket ${torRunDirectory}/control GroupWritable RelaxDirModeCheck"}
   ''
   # Client connection config
-  + optionalString cfg.client.enable  ''
-    SOCKSPort ${cfg.client.socksListenAddress} IsolateDestAddr
+  + optionalString cfg.client.enable ''
+    SOCKSPort ${cfg.client.socksListenAddress} ${toString cfg.client.socksIsolationOptions}
     SOCKSPort ${cfg.client.socksListenAddressFaster}
     ${opt "SocksPolicy" cfg.client.socksPolicy}
+
+    ${optionalString cfg.client.transparentProxy.enable ''
+    TransPort ${cfg.client.transparentProxy.listenAddress} ${toString cfg.client.transparentProxy.isolationOptions}
+    ''}
+
+    ${optionalString cfg.client.dns.enable ''
+    DNSPort ${cfg.client.dns.listenAddress} ${toString cfg.client.dns.isolationOptions}
+    AutomapHostsOnResolve 1
+    AutomapHostsSuffixes ${concatStringsSep "," cfg.client.dns.automapHostsSuffixes}
+    ''}
   ''
   # Relay config
   + optionalString cfg.relay.enable ''
@@ -58,6 +90,9 @@ let
     ${flip concatMapStrings v.map (p: ''
       HiddenServicePort ${toString p.port} ${p.destination}
     '')}
+    ${optionalString (v.authorizeClient != null) ''
+      HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames}
+    ''}
   ''))
   + cfg.extraConfig;
 
@@ -107,6 +142,17 @@ in
         '';
       };
 
+      controlSocket = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Wheter to enable Tor control socket. Control socket is created
+            in <literal>${torRunDirectory}/control</literal>
+          '';
+        };
+      };
+
       client = {
         enable = mkOption {
           type = types.bool;
@@ -154,6 +200,55 @@ in
           '';
         };
 
+        socksIsolationOptions = mkOption (isolationOptions // {
+          default = ["IsolateDestAddr"];
+        });
+
+        transparentProxy = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor transaprent proxy";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9040";
+            example = "192.168.0.1:9040";
+            description = ''
+              Bind transparent proxy to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+        };
+
+        dns = {
+          enable = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Whether to enable tor dns resolver";
+          };
+
+          listenAddress = mkOption {
+            type = types.str;
+            default = "127.0.0.1:9053";
+            example = "192.168.0.1:9053";
+            description = ''
+              Bind tor dns to this address.
+            '';
+          };
+
+          isolationOptions = mkOption isolationOptions;
+
+          automapHostsSuffixes = mkOption {
+            type = types.listOf types.str;
+            default = [".onion" ".exit"];
+            example = [".onion"];
+            description = "List of suffixes to use with automapHostsOnResolve";
+          };
+        };
+
         privoxy.enable = mkOption {
           type = types.bool;
           default = true;
@@ -540,6 +635,33 @@ in
                }));
              };
 
+             authorizeClient = mkOption {
+               default = null;
+               description = "If configured, the hidden service is accessible for authorized clients only.";
+               type = types.nullOr (types.submodule ({config, ...}: {
+
+                 options = {
+
+                   authType = mkOption {
+                     type = types.enum [ "basic" "stealth" ];
+                     description = ''
+                       Either <literal>"basic"</literal> for a general-purpose authorization protocol
+                       or <literal>"stealth"</literal> for a less scalable protocol
+                       that also hides service activity from unauthorized clients.
+                     '';
+                   };
+
+                   clientNames = mkOption {
+                     type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+");
+                     description = ''
+                       Only clients that are listed here are authorized to access the hidden service.
+                       Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>.
+                       Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>.
+                     '';
+                   };
+                 };
+               }));
+             };
           };
 
           config = {
@@ -581,14 +703,10 @@ in
         after    = [ "network.target" ];
         restartTriggers = [ torRcFile ];
 
-        # Translated from the upstream contrib/dist/tor.service.in
-        preStart = ''
-          install -o tor -g tor -d ${torDirectory}/onion
-          ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config
-        '';
-
         serviceConfig =
           { Type         = "simple";
+            # Translated from the upstream contrib/dist/tor.service.in
+            ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config";
             ExecStart    = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0";
             ExecReload   = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
             KillSignal   = "SIGINT";
@@ -603,11 +721,13 @@ in
             #   DeviceAllow /dev/urandom r
             # .. but we can't specify DeviceAllow multiple times. 'closed'
             # is close enough.
+            RuntimeDirectory        = "tor";
+            StateDirectory          = [ "tor" "tor/onion" ];
             PrivateTmp              = "yes";
             DevicePolicy            = "closed";
             InaccessibleDirectories = "/home";
             ReadOnlyDirectories     = "/";
-            ReadWriteDirectories    = torDirectory;
+            ReadWriteDirectories    = [torDirectory torRunDirectory];
             NoNewPrivileges         = "yes";
           };
       };
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
index a29cb3f33dae..08da726437ea 100644
--- a/nixos/modules/services/security/torify.nix
+++ b/nixos/modules/services/security/torify.nix
@@ -7,7 +7,7 @@ let
   torify = pkgs.writeTextFile {
     name = "tsocks";
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.tsocks.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
index 1b5a05b21e77..c60c745443bc 100644
--- a/nixos/modules/services/security/torsocks.nix
+++ b/nixos/modules/services/security/torsocks.nix
@@ -23,7 +23,7 @@ let
   wrapTorsocks = name: server: pkgs.writeTextFile {
     name = name;
     text = ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (configFile server)} ${pkgs.torsocks}/bin/torsocks "$@"
     '';
     executable = true;
diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix
index 1f2c56a9efa1..5d469cabe2cb 100644
--- a/nixos/modules/services/security/usbguard.nix
+++ b/nixos/modules/services/security/usbguard.nix
@@ -56,7 +56,7 @@ in {
       };
 
       rules = mkOption {
-        type = types.nullOr types.str;
+        type = types.nullOr types.lines;
         default = null;
         example = ''
           allow with-interface equals { 08:*:* }
@@ -192,7 +192,7 @@ in {
 
       serviceConfig = {
         Type = "simple";
-        ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -d -k -c ${daemonConfFile}'';
+        ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -P -d -k -c ${daemonConfFile}'';
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/system/localtime.nix b/nixos/modules/services/system/localtime.nix
new file mode 100644
index 000000000000..b9355bbb9441
--- /dev/null
+++ b/nixos/modules/services/system/localtime.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.localtime;
+in {
+  options = {
+    services.localtime = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable <literal>localtime</literal>, simple daemon for keeping the system
+          timezone up-to-date based on the current location. It uses geoclue2 to
+          determine the current location and systemd-timedated to actually set
+          the timezone.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.geoclue2.enable = true;
+
+    security.polkit.extraConfig = ''
+     polkit.addRule(function(action, subject) {
+       if (action.id == "org.freedesktop.timedate1.set-timezone"
+           && subject.user == "localtimed") {
+         return polkit.Result.YES;
+       }
+     });
+    '';
+
+    users.users = [{
+      name = "localtimed";
+      description = "Taskserver user";
+    }];
+
+    systemd.services.localtime = {
+      description = "localtime service";
+      wantedBy = [ "multi-user.target" ];
+      partOf = [ "geoclue.service "];
+
+      serviceConfig = {
+        Restart                 = "on-failure";
+        # TODO: make it work with dbus
+        #DynamicUser             = true;
+        Nice                    = 10;
+        User                    = "localtimed";
+        PrivateTmp              = "yes";
+        PrivateDevices          = true;
+        PrivateNetwork          = "yes";
+        NoNewPrivileges         = "yes";
+        ProtectSystem           = "strict";
+        ProtectHome             = true;
+        ExecStart               = "${pkgs.localtime}/bin/localtimed";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index dd6b585b7e23..3564afd77f41 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -21,6 +21,19 @@ let
 
   # for users in group "transmission" to have access to torrents
   fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings;
+
+  # Directories transmission expects to exist and be ug+rwx.
+  directoriesToManage = [ homeDir settingsDir fullSettings.download-dir fullSettings.incomplete-dir ];
+
+  preStart = pkgs.writeScript "transmission-pre-start" ''
+    #!${pkgs.runtimeShell}
+    set -ex
+    for DIR in ${escapeShellArgs directoriesToManage}; do
+      mkdir -p "$DIR"
+      chmod 770 "$DIR"
+    done
+    cp -f ${settingsFile} ${settingsDir}/settings.json
+  '';
 in
 {
   options = {
@@ -59,8 +72,8 @@ in
           time the service starts). String values must be quoted, integer and
           boolean values must not.
 
-          See https://trac.transmissionbt.com/wiki/EditConfigFiles for
-          documentation.
+          See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files
+          for documentation.
         '';
       };
 
@@ -89,9 +102,7 @@ in
 
       # 1) Only the "transmission" user and group have access to torrents.
       # 2) Optionally update/force specific fields into the configuration file.
-      serviceConfig.ExecStartPre = ''
-          ${pkgs.stdenv.shell} -c "mkdir -p ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && chmod 770 ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && rm -f ${settingsDir}/settings.json && cp -f ${settingsFile} ${settingsDir}/settings.json"
-      '';
+      serviceConfig.ExecStartPre = preStart;
       serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port}";
       serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       serviceConfig.User = "transmission";
@@ -136,6 +147,7 @@ in
           ${getLib pkgs.libcap}/lib/libcap*.so*            mr,
           ${getLib pkgs.attr}/lib/libattr*.so*             mr,
           ${getLib pkgs.lz4}/lib/liblz4*.so*               mr,
+          ${getLib pkgs.libkrb5}/lib/lib*.so*              mr,
 
           @{PROC}/sys/kernel/random/uuid   r,
           @{PROC}/sys/vm/overcommit_memory r,
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index 3429397d2cc2..b50de496e975 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -64,8 +64,8 @@ in
 
   config = {
     # Note: this is set here rather than up there so that changing
-    # nixosLabel would not rebuild manual pages
-    services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixosLabel} (\m) - \l >>>'';
+    # nixos.label would not rebuild manual pages
+    services.mingetty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>'';
 
     systemd.services."getty@" =
       { serviceConfig.ExecStart = [
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
index 81ee8154326c..13c5951524d9 100644
--- a/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -155,7 +155,7 @@ in
       requires = [ "postgresql.service" ];
       after = [ "postgresql.service" ];
 
-      path = [ cfg.jrePackage ];
+      path = [ cfg.jrePackage pkgs.bash ];
 
       environment = {
         JIRA_USER = cfg.user;
diff --git a/nixos/modules/services/web-apps/piwik-doc.xml b/nixos/modules/services/web-apps/matomo-doc.xml
index a393a182d36a..456aae6cc366 100644
--- a/nixos/modules/services/web-apps/piwik-doc.xml
+++ b/nixos/modules/services/web-apps/matomo-doc.xml
@@ -2,16 +2,16 @@
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns:xi="http://www.w3.org/2001/XInclude"
          version="5.0"
-         xml:id="module-services-piwik">
+         xml:id="module-services-matomo">
 
-  <title>Piwik</title>
+  <title>Matomo</title>
   <para>
-    Piwik is a real-time web analytics application.
-    This module configures php-fpm as backend for piwik, optionally configuring an nginx vhost as well.
+    Matomo is a real-time web analytics application.
+    This module configures php-fpm as backend for Matomo, optionally configuring an nginx vhost as well.
   </para>
 
   <para>
-    An automatic setup is not suported by piwik, so you need to configure piwik itself in the browser-based piwik setup.
+    An automatic setup is not suported by Matomo, so you need to configure Matomo itself in the browser-based Matomo setup.
   </para>
 
 
@@ -19,7 +19,7 @@
     <title>Database Setup</title>
 
     <para>
-      You also need to configure a MariaDB or MySQL database and -user for piwik yourself,
+      You also need to configure a MariaDB or MySQL database and -user for Matomo yourself,
       and enter those credentials in your browser.
       You can use passwordless database authentication via the UNIX_SOCKET authentication plugin
       with the following SQL commands:
@@ -27,20 +27,20 @@
       <programlisting>
         # For MariaDB
         INSTALL PLUGIN unix_socket SONAME 'auth_socket';
-        CREATE DATABASE piwik;
-        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH unix_socket;
-        GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
+        CREATE DATABASE matomo;
+        CREATE USER 'matomo'@'localhost' IDENTIFIED WITH unix_socket;
+        GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
 
         # For MySQL
         INSTALL PLUGIN auth_socket SONAME 'auth_socket.so';
-        CREATE DATABASE piwik;
-        CREATE USER 'piwik'@'localhost' IDENTIFIED WITH auth_socket;
-        GRANT ALL PRIVILEGES ON piwik.* TO 'piwik'@'localhost';
+        CREATE DATABASE matomo;
+        CREATE USER 'matomo'@'localhost' IDENTIFIED WITH auth_socket;
+        GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost';
       </programlisting>
 
-      Then fill in <literal>piwik</literal> as database user and database name, and leave the password field blank.
-      This authentication works by allowing only the <literal>piwik</literal> unix user to authenticate as the 
-      <literal>piwik</literal> database user (without needing a password), but no other users.
+      Then fill in <literal>matomo</literal> as database user and database name, and leave the password field blank.
+      This authentication works by allowing only the <literal>matomo</literal> unix user to authenticate as the
+      <literal>matomo</literal> database user (without needing a password), but no other users.
       For more information on passwordless login, see
       <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/" />.
     </para>
@@ -55,9 +55,9 @@
     <title>Backup</title>
     <para>
       You only need to take backups of your MySQL database and the
-      <filename>/var/lib/piwik/config/config.ini.php</filename> file.
-      Use a user in the <literal>piwik</literal> group or root to access the file.
-      For more information, see <link xlink:href="https://piwik.org/faq/how-to-install/faq_138/" />.
+      <filename>/var/lib/matomo/config/config.ini.php</filename> file.
+      Use a user in the <literal>matomo</literal> group or root to access the file.
+      For more information, see <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/" />.
     </para>
   </section>
 
@@ -67,14 +67,14 @@
     <itemizedlist>
       <listitem>
         <para>
-          Piwik's file integrity check will warn you.
+          Matomo's file integrity check will warn you.
           This is due to the patches necessary for NixOS, you can safely ignore this.
         </para>
       </listitem>
 
       <listitem>
         <para>
-          Piwik will warn you that the JavaScript tracker is not writable.
+          Matomo will warn you that the JavaScript tracker is not writable.
           This is because it's located in the read-only nix store.
           You can safely ignore this, unless you need a plugin that needs JavaScript tracker access.
         </para>
@@ -88,7 +88,7 @@
 
     <para>
       You can use other web servers by forwarding calls for <filename>index.php</filename> and
-      <filename>piwik.php</filename> to the <literal>/run/phpfpm-piwik.sock</literal> fastcgi unix socket.
+      <filename>piwik.php</filename> to the <literal>/run/phpfpm-matomo.sock</literal> fastcgi unix socket.
       You can use the nginx configuration in the module code as a reference to what else should be configured.
     </para>
   </section>
diff --git a/nixos/modules/services/web-apps/piwik.nix b/nixos/modules/services/web-apps/matomo.nix
index ce86c6873dd4..ef6ac9698e21 100644
--- a/nixos/modules/services/web-apps/piwik.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -1,10 +1,11 @@
 { config, lib, pkgs, services, ... }:
 with lib;
 let
-  cfg = config.services.piwik;
+  cfg = config.services.matomo;
 
-  user = "piwik";
+  user = "matomo";
   dataDir = "/var/lib/${user}";
+  deprecatedDataDir = "/var/lib/piwik";
 
   pool = user;
   # it's not possible to use /run/phpfpm/${pool}.sock because /run/phpfpm/ is root:root 0770,
@@ -13,17 +14,22 @@ let
   phpExecutionUnit = "phpfpm-${pool}";
   databaseService = "mysql.service";
 
+  fqdn =
+    let
+      join = hostName: domain: hostName + optionalString (domain != null) ".${domain}";
+     in join config.networking.hostName config.networking.domain;
+
 in {
   options = {
-    services.piwik = {
+    services.matomo = {
       # NixOS PR for database setup: https://github.com/NixOS/nixpkgs/pull/6963
-      # piwik issue for automatic piwik setup: https://github.com/piwik/piwik/issues/10257
-      # TODO: find a nice way to do this when more NixOS MySQL and / or piwik automatic setup stuff is implemented.
+      # matomo issue for automatic matomo setup: https://github.com/matomo-org/matomo/issues/10257
+      # TODO: find a nice way to do this when more NixOS MySQL and / or matomo automatic setup stuff is implemented.
       enable = mkOption {
         type = types.bool;
         default = false;
         description = ''
-          Enable piwik web analytics with php-fpm backend.
+          Enable matomo web analytics with php-fpm backend.
           Either the nginx option or the webServerUser option is mandatory.
         '';
       };
@@ -32,8 +38,9 @@ in {
         type = types.nullOr types.str;
         default = null;
         example = "lighttpd";
+        # TODO: piwik.php might get renamed to matomo.php in future releases
         description = ''
-          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for piwik if the nginx
+          Name of the web server user that forwards requests to the ${phpSocket} fastcgi socket for matomo if the nginx
           option is not used. Either this option or the nginx option is mandatory.
           If you want to use another webserver than nginx, you need to set this to that server's user
           and pass fastcgi requests to `index.php` and `piwik.php` to this socket.
@@ -55,7 +62,7 @@ in {
           catch_workers_output = yes
         '';
         description = ''
-          Settings for phpfpm's process manager. You might need to change this depending on the load for piwik.
+          Settings for phpfpm's process manager. You might need to change this depending on the load for matomo.
         '';
       };
 
@@ -65,7 +72,7 @@ in {
             (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
             {
               # enable encryption by default,
-              # as sensitive login and piwik data should not be transmitted in clear text.
+              # as sensitive login and matomo data should not be transmitted in clear text.
               options.forceSSL.default = true;
               options.enableACME.default = true;
             }
@@ -73,15 +80,19 @@ in {
         );
         default = null;
         example = {
-          serverName = "stats.$\{config.networking.hostName\}";
+          serverAliases = [
+            "matomo.$\{config.networking.domain\}"
+            "stats.$\{config.networking.domain\}"
+          ];
           enableACME = false;
         };
         description = ''
-            With this option, you can customize an nginx virtualHost which already has sensible defaults for piwik.
+            With this option, you can customize an nginx virtualHost which already has sensible defaults for matomo.
             Either this option or the webServerUser option is mandatory.
             Set this to {} to just enable the virtualHost if you don't need any customization.
-            If enabled, then by default, the serverName is piwik.$\{config.networking.hostName\}, SSL is active,
-            and certificates are acquired via ACME.
+            If enabled, then by default, the <option>serverName</option> is
+            <literal>${user}.$\{config.networking.hostName\}.$\{config.networking.domain\}</literal>,
+            SSL is active, and certificates are acquired via ACME.
             If this is set to null (the default), no nginx virtualHost will be configured.
         '';
       };
@@ -90,12 +101,12 @@ in {
 
   config = mkIf cfg.enable {
     warnings = mkIf (cfg.nginx != null && cfg.webServerUser != null) [
-      "If services.piwik.nginx is set, services.piwik.nginx.webServerUser is ignored and should be removed."
+      "If services.matomo.nginx is set, services.matomo.nginx.webServerUser is ignored and should be removed."
     ];
 
     assertions = [ {
         assertion = cfg.nginx != null || cfg.webServerUser != null;
-        message = "Either services.piwik.nginx or services.piwik.nginx.webServerUser is mandatory";
+        message = "Either services.matomo.nginx or services.matomo.nginx.webServerUser is mandatory";
     }];
 
     users.extraUsers.${user} = {
@@ -106,19 +117,20 @@ in {
     };
     users.extraGroups.${user} = {};
 
-    systemd.services.piwik_setup_update = {
-      # everything needs to set up and up to date before piwik php files are executed
+    systemd.services.matomo_setup_update = {
+      # everything needs to set up and up to date before matomo php files are executed
       requiredBy = [ "${phpExecutionUnit}.service" ];
       before = [ "${phpExecutionUnit}.service" ];
       # the update part of the script can only work if the database is already up and running
       requires = [ databaseService ];
       after = [ databaseService ];
-      path = [ pkgs.piwik ];
+      path = [ pkgs.matomo ];
       serviceConfig = {
         Type = "oneshot";
         User = user;
         # hide especially config.ini.php from other
         UMask = "0007";
+        # TODO: might get renamed to MATOMO_USER_PATH in future versions
         Environment = "PIWIK_USER_PATH=${dataDir}";
         # chown + chmod in preStart needs root
         PermissionsStartOnly = true;
@@ -127,27 +139,32 @@ in {
       # e.g. after restoring from backup or moving from another system.
       # Note that ${dataDir}/config/config.ini.php might contain the MySQL password.
       preStart = ''
+        # migrate data from piwik to matomo folder
+        if [ -d ${deprecatedDataDir} ]; then
+          echo "Migrating from ${deprecatedDataDir} to ${dataDir}"
+          mv -T ${deprecatedDataDir} ${dataDir}
+        fi
         chown -R ${user}:${user} ${dataDir}
         chmod -R ug+rwX,o-rwx ${dataDir}
         '';
       script = ''
-            # Use User-Private Group scheme to protect piwik data, but allow administration / backup via piwik group
+            # Use User-Private Group scheme to protect matomo data, but allow administration / backup via matomo group
             # Copy config folder
             chmod g+s "${dataDir}"
-            cp -r "${pkgs.piwik}/config" "${dataDir}/"
+            cp -r "${pkgs.matomo}/config" "${dataDir}/"
             chmod -R u+rwX,g+rwX,o-rwx "${dataDir}"
 
             # check whether user setup has already been done
             if test -f "${dataDir}/config/config.ini.php"; then
               # then execute possibly pending database upgrade
-              piwik-console core:update --yes
+              matomo-console core:update --yes
             fi
       '';
     };
 
     systemd.services.${phpExecutionUnit} = {
-      # stop phpfpm on package upgrade, do database upgrade via piwik_setup_update, and then restart
-      restartTriggers = [ pkgs.piwik ];
+      # stop phpfpm on package upgrade, do database upgrade via matomo_setup_update, and then restart
+      restartTriggers = [ pkgs.matomo ];
       # stop config.ini.php from getting written with read permission for others
       serviceConfig.UMask = "0007";
     };
@@ -175,14 +192,14 @@ in {
       # References:
       # https://fralef.me/piwik-hardening-with-nginx-and-php-fpm.html
       # https://github.com/perusio/piwik-nginx
-      "${user}.${config.networking.hostName}" = mkMerge [ cfg.nginx {
-        # don't allow to override the root easily, as it will almost certainly break piwik.
+      "${user}.${fqdn}" = mkMerge [ cfg.nginx {
+        # don't allow to override the root easily, as it will almost certainly break matomo.
         # disadvantage: not shown as default in docs.
-        root = mkForce "${pkgs.piwik}/share";
+        root = mkForce "${pkgs.matomo}/share";
 
         # define locations here instead of as the submodule option's default
         # so that they can easily be extended with additional locations if required
-        # without needing to redefine the piwik ones.
+        # without needing to redefine the matomo ones.
         # disadvantage: not shown as default in docs.
         locations."/" = {
           index = "index.php";
@@ -191,6 +208,7 @@ in {
         locations."= /index.php".extraConfig = ''
           fastcgi_pass unix:${phpSocket};
         '';
+        # TODO: might get renamed to matomo.php in future versions
         # allow piwik.php for tracking
         locations."= /piwik.php".extraConfig = ''
           fastcgi_pass unix:${phpSocket};
@@ -212,6 +230,7 @@ in {
         locations."= /robots.txt".extraConfig = ''
           return 200 "User-agent: *\nDisallow: /\n";
         '';
+        # TODO: might get renamed to matomo.js in future versions
         # let browsers cache piwik.js
         locations."= /piwik.js".extraConfig = ''
           expires 1M;
@@ -221,7 +240,7 @@ in {
   };
 
   meta = {
-    doc = ./piwik-doc.xml;
+    doc = ./matomo-doc.xml;
     maintainers = with stdenv.lib.maintainers; [ florianjacob ];
   };
 }
diff --git a/nixos/modules/services/web-apps/nexus.nix b/nixos/modules/services/web-apps/nexus.nix
index a750aa66b27c..f6a5ce73a12b 100644
--- a/nixos/modules/services/web-apps/nexus.nix
+++ b/nixos/modules/services/web-apps/nexus.nix
@@ -11,7 +11,7 @@ in
 {
   options = {
     services.nexus = {
-      enable = mkEnableOption "SonarType Nexus3 OSS service";
+      enable = mkEnableOption "Sonatype Nexus3 OSS service";
 
       user = mkOption {
         type = types.str;
@@ -54,7 +54,7 @@ in
     users.extraGroups."${cfg.group}" = {};
 
     systemd.services.nexus = {
-      description = "SonarType Nexus3";
+      description = "Sonatype Nexus3";
 
       wantedBy = [ "multi-user.target" ];
 
diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix
deleted file mode 100644
index 0592d01bf369..000000000000
--- a/nixos/modules/services/web-apps/nixbot.nix
+++ /dev/null
@@ -1,149 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.nixbot;
-  pyramidIni = ''
-    ###
-    # app configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html
-    ###
-
-    [app:main]
-    use = egg:nixbot
-
-    nixbot.github_token = ${cfg.githubToken}
-    nixbot.bot_name = ${cfg.botName}
-    nixbot.repo = ${cfg.repo}
-    nixbot.pr_repo = ${cfg.prRepo}
-    nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo}
-    nixbot.github_secret = justnotsorandom
-    nixbot.public_url = ${cfg.publicUrl}
-    nixbot.repo_dir = ${cfg.repoDir}
-
-    pyramid.reload_templates = false
-    pyramid.debug_authorization = false
-    pyramid.debug_notfound = false
-    pyramid.debug_routematch = false
-    pyramid.default_locale_name = en
-
-    # By default, the toolbar only appears for clients from IP addresses
-    # '127.0.0.1' and '::1'.
-    # debugtoolbar.hosts = 127.0.0.1 ::1
-
-    ###
-    # wsgi server configuration
-    ###
-
-    [server:main]
-    use = egg:waitress#main
-    host = 0.0.0.0
-    port = 6543
-
-    ###
-    # logging configuration
-    # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html
-    ###
-
-    [loggers]
-    keys = root, nixbot
-
-    [handlers]
-    keys = console
-
-    [formatters]
-    keys = generic
-
-    [logger_root]
-    level = INFO
-    handlers = console
-
-    [logger_nixbot]
-    level = INFO
-    handlers =
-    qualname = nixbot
-
-    [handler_console]
-    class = StreamHandler
-    args = (sys.stderr,)
-    level = NOTSET
-    formatter = generic
-
-    [formatter_generic]
-    format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
-  '';
-in {
-  options = {
-    services.nixbot = {
-      enable = mkEnableOption "nixbot";
-
-      botName = mkOption {
-        type = types.str;
-        description = "The bot's github user account name.";
-        default = "nixbot";
-      };
-
-      githubToken = mkOption {
-        type = types.str;
-        description = "The bot's github user account token.";
-        example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
-      };
-
-      repo = mkOption {
-        type = types.str;
-        description = "The github repository to check for PRs.";
-        example = "nixos/nixpkgs";
-      };
-
-      prRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the testing branches to.";
-        example = "nixos/nixpkgs-pr";
-      };
-
-      hydraJobsetsRepo = mkOption {
-        type = types.str;
-        description = "The github repository to push the hydra jobset definitions to.";
-        example = "nixos/hydra-jobsets";
-      };
-
-      publicUrl = mkOption {
-        type = types.str;
-        description = "The public URL the bot is reachable at (Github hook endpoint).";
-        example = "https://nixbot.nixos.org";
-      };
-
-      repoDir = mkOption {
-        type = types.path;
-        description = "The directory the repositories are stored in.";
-        default = "/var/lib/nixbot";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    users.extraUsers.nixbot = {
-      createHome = true;
-      home = cfg.repoDir;
-    };
-
-    systemd.services.nixbot = let
-      env = pkgs.python3.buildEnv.override {
-        extraLibs = [ pkgs.nixbot ];
-      };
-    in {
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      script = ''
-        ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni}
-      '';
-
-      serviceConfig = {
-        User = "nixbot";
-        Group = "nogroup";
-        PermissionsStartOnly = true;
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/web-apps/pump.io-configure.js b/nixos/modules/services/web-apps/pump.io-configure.js
deleted file mode 100644
index 1fbf346a34c4..000000000000
--- a/nixos/modules/services/web-apps/pump.io-configure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var fs = require('fs');
-
-var opts = JSON.parse(fs.readFileSync("/dev/stdin").toString());
-var config = opts.config;
-
-var readSecret = function(filename) {
-  return fs.readFileSync(filename).toString().trim();
-};
-
-if (opts.secretFile) {
-  config.secret = readSecret(opts.secretFile);
-}
-if (opts.dbPasswordFile) {
-  config.params.dbpass = readSecret(opts.dbPasswordFile);
-}
-if (opts.smtpPasswordFile) {
-  config.smtppass = readSecret(opts.smtpPasswordFile);
-}
-if (opts.spamClientSecretFile) {
-  config.spamclientsecret = readSecret(opts.opts.spamClientSecretFile);
-}
-
-fs.writeFileSync(opts.outputFile, JSON.stringify(config));
diff --git a/nixos/modules/services/web-apps/pump.io.nix b/nixos/modules/services/web-apps/pump.io.nix
deleted file mode 100644
index 27ae68516367..000000000000
--- a/nixos/modules/services/web-apps/pump.io.nix
+++ /dev/null
@@ -1,438 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.pumpio;
-  dataDir = "/var/lib/pump.io";
-  runDir = "/run/pump.io";
-  user = "pumpio";
-
-  optionalSet = condition: value: if condition then value else {};
-
-  configScript = ./pump.io-configure.js;
-  configOptions = {
-    outputFile = "${runDir}/config.json";
-    config =
-      (optionalSet (cfg.driver != "disk") {
-        driver = cfg.driver;
-      }) //
-      {
-        params = (optionalSet (cfg.driver == "disk") { dir = dataDir; }) //
-                 (optionalSet (cfg.driver == "mongodb" || cfg.driver == "redis") {
-                   host = cfg.dbHost;
-                   port = cfg.dbPort;
-                   dbname = cfg.dbName;
-                   dbuser = cfg.dbUser;
-                   dbpass = cfg.dbPassword;
-                 }) //
-                 (optionalSet (cfg.driver == "memcached") {
-                   host = cfg.dbHost;
-                   port = cfg.dbPort;
-                 }) // cfg.driverParams;
-        secret = cfg.secret;
-
-        address = cfg.address;
-        port = cfg.port;
-
-        noweb = false;
-        urlPort = cfg.urlPort;
-        hostname = cfg.hostname;
-        favicon = cfg.favicon;
-
-        site = cfg.site;
-        owner = cfg.owner;
-        ownerURL = cfg.ownerURL;
-
-        key = cfg.sslKey;
-        cert = cfg.sslCert;
-        bounce = false;
-
-        spamhost = cfg.spamHost;
-        spamclientid = cfg.spamClientId;
-        spamclientsecret = cfg.spamClientSecret;
-
-        requireEmail = cfg.requireEmail;
-        smtpserver = cfg.smtpHost;
-        smtpport = cfg.smtpPort;
-        smtpuser = cfg.smtpUser;
-        smtppass = cfg.smtpPassword;
-        smtpusessl = cfg.smtpUseSSL;
-        smtpfrom = cfg.smtpFrom;
-
-        nologger = false;
-        enableUploads = cfg.enableUploads;
-        datadir = dataDir;
-        debugClient = false;
-        firehose = cfg.firehose;
-        disableRegistration = cfg.disableRegistration;
-
-        inherit (cfg) secretFile dbPasswordFile smtpPasswordFile spamClientSecretFile;
-      } //
-      (optionalSet (cfg.port < 1024) {
-        serverUser = user;  # have pump.io listen then drop privileges
-      }) // cfg.extraConfig;
-}; in {
-  options = {
-
-    services.pumpio = {
-
-      enable = mkEnableOption "Pump.io social streams server";
-
-      secret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "my dog has fleas";
-        description = ''
-          A session-generating secret, server-wide password.  Warning:
-          this is stored in cleartext in the Nix store!
-        '';
-      };
-
-      secretFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-secret";
-        description = ''
-          A file containing the session-generating secret,
-          server-wide password.
-        '';
-      };
-
-      site = mkOption {
-        type = types.str;
-        example = "Awesome Sauce";
-        description = "Name of the server";
-      };
-
-      owner = mkOption {
-        type = types.str;
-        default = "";
-        example = "Awesome Inc.";
-        description = "Name of owning entity, if you want to link to it.";
-      };
-
-      ownerURL = mkOption {
-        type = types.str;
-        default = "";
-        example = "https://pump.io";
-        description = "URL of owning entity, if you want to link to it.";
-      };
-
-      address = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = ''
-          Web server listen address.
-        '';
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 31337;
-        description = ''
-          Port to listen on. Defaults to 31337, which is suitable for
-          running behind a reverse proxy. For a standalone server,
-          use 443.
-        '';
-      };
-
-      hostname = mkOption {
-        type = types.nullOr types.str;
-        default = "localhost";
-        description = ''
-          The hostname of the server, used for generating
-          URLs. Defaults to "localhost" which doesn't do much for you.
-        '';
-      };
-
-      urlPort = mkOption {
-        type = types.int;
-        default = 443;
-        description = ''
-          Port to use for generating URLs. This basically has to be
-          either 80 or 443 because the host-meta and Webfinger
-          protocols don't make any provision for HTTP/HTTPS servers
-          running on other ports.
-        '';
-      };
-
-      favicon = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = ''
-          Local filesystem path to the favicon.ico file to use. This
-          will be served as "/favicon.ico" by the server.
-        '';
-      };
-
-      enableUploads = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          If you want to disable file uploads, set this to false. Uploaded files will be stored
-          in ${dataDir}/uploads.
-        '';
-      };
-
-      sslKey = mkOption {
-        type = types.path;
-        example = "${dataDir}/myserver.key";
-        default = "";
-        description = ''
-          The path to the server certificate private key. The
-          certificate is required, but it can be self-signed.
-        '';
-      };
-
-      sslCert = mkOption {
-        type = types.path;
-        example = "${dataDir}/myserver.crt";
-        default = "";
-        description = ''
-          The path to the server certificate. The certificate is
-          required, but it can be self-signed.
-        '';
-      };
-
-      firehose = mkOption {
-        type = types.str;
-        default = "ofirehose.com";
-        description = ''
-          Firehose host running the ofirehose software. Defaults to
-          "ofirehose.com". Public notices will be ping this firehose
-          server and from there go out to search engines and the
-          world. If you want to disconnect from the public web, set
-          this to something falsy.
-        '';
-      };
-
-      disableRegistration = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Disables registering new users on the site through the Web
-          or the API.
-        '';
-      };
-
-      requireEmail = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Require an e-mail address to register.";
-      };
-
-      extraConfig = mkOption {
-        default = { };
-        description = ''
-          Extra configuration options which are serialized to json and added
-          to the pump.io.json config file.
-        '';
-      };
-
-      driver = mkOption {
-        type = types.enum [ "mongodb" "disk" "lrucache" "memcached" "redis" ];
-        default = "mongodb";
-        description = "Type of database. Corresponds to a nodejs databank driver.";
-      };
-
-      driverParams = mkOption {
-        default = { };
-        description = "Extra parameters for the driver.";
-      };
-
-      dbHost = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = "The database host to connect to.";
-      };
-
-      dbPort = mkOption {
-        type = types.int;
-        default = 27017;
-        description = "The port that the database is listening on.";
-      };
-
-      dbName = mkOption {
-        type = types.str;
-        default = "pumpio";
-        description = "The name of the database to use.";
-      };
-
-      dbUser = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          The username. Defaults to null, meaning no authentication.
-        '';
-      };
-
-      dbPassword = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          The password corresponding to dbUser.  Warning: this is
-          stored in cleartext in the Nix store!
-        '';
-      };
-
-      dbPasswordFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-dbpassword";
-        description = ''
-          A file containing the password corresponding to dbUser.
-        '';
-      };
-
-      smtpHost = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "localhost";
-        description = ''
-          Server to use for sending transactional email. If it's not
-          set up, no email is sent and features like password recovery
-          and email notification won't work.
-        '';
-      };
-
-      smtpPort = mkOption {
-        type = types.int;
-        default = 25;
-        description = ''
-          Port to connect to on SMTP server.
-        '';
-      };
-
-      smtpUser = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Username to use to connect to SMTP server. Might not be
-          necessary for some servers.
-        '';
-      };
-
-      smtpPassword = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Password to use to connect to SMTP server. Might not be
-          necessary for some servers.  Warning: this is stored in
-          cleartext in the Nix store!
-        '';
-      };
-
-      smtpPasswordFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-smtppassword";
-        description = ''
-          A file containing the password used to connect to SMTP
-          server. Might not be necessary for some servers.
-        '';
-      };
-
-
-      smtpUseSSL = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Only use SSL with the SMTP server. By default, a SSL
-          connection is negotiated using TLS. You may need to change
-          the smtpPort value if you set this.
-        '';
-      };
-
-      smtpFrom = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Email address to use in the "From:" header of outgoing
-          notifications. Defaults to 'no-reply@' plus the site
-          hostname.
-        '';
-      };
-
-      spamHost = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          Host running activityspam software to use to test updates
-          for spam.
-        '';
-      };
-      spamClientId = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = "OAuth pair for spam server.";
-      };
-      spamClientSecret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = ''
-          OAuth pair for spam server.  Warning: this is
-          stored in cleartext in the Nix store!
-        '';
-      };
-      spamClientSecretFile = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        example = "/run/keys/pump.io-spamclientsecret";
-        description = ''
-          A file containing the OAuth key for the spam server.
-        '';
-      };
-    };
-
-  };
-
-  config = mkIf cfg.enable {
-    warnings = let warn = k: optional (cfg.${k} != null)
-                 "config.services.pumpio.${k} is insecure. Use ${k}File instead.";
-               in concatMap warn [ "secret" "dbPassword" "smtpPassword" "spamClientSecret" ];
-
-    assertions = [
-      { assertion = !(isNull cfg.secret && isNull cfg.secretFile);
-        message = "pump.io needs a secretFile configured";
-      }
-    ];
-
-    systemd.services."pump.io" =
-      { description = "Pump.io - stream server that does most of what people really want from a social network";
-        after = [ "network.target" ];
-        wantedBy = [ "multi-user.target" ];
-
-        preStart = ''
-          mkdir -p ${dataDir}/uploads
-          mkdir -p ${runDir}
-          chown pumpio:pumpio ${dataDir}/uploads ${runDir}
-          chmod 770 ${dataDir}/uploads ${runDir}
-
-          ${pkgs.nodejs}/bin/node ${configScript} <<EOF
-          ${builtins.toJSON configOptions}
-          EOF
-
-          chgrp pumpio ${configOptions.outputFile}
-          chmod 640 ${configOptions.outputFile}
-        '';
-
-        serviceConfig = {
-          ExecStart = "${pkgs.pumpio}/bin/pump -c ${configOptions.outputFile}";
-          PermissionsStartOnly = true;
-          User = if cfg.port < 1024 then "root" else user;
-          Group = user;
-        };
-        environment = { NODE_ENV = "production"; };
-      };
-
-      users.extraGroups.pumpio.gid = config.ids.gids.pumpio;
-      users.extraUsers.pumpio = {
-        group = "pumpio";
-        uid = config.ids.uids.pumpio;
-        description = "Pump.io user";
-        home = dataDir;
-        createHome = true;
-      };
-  };
-}
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
new file mode 100644
index 000000000000..cee725e8fe5f
--- /dev/null
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -0,0 +1,384 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+# TODO: are these php-packages needed?
+#imagick
+#php-geoip -> php.ini: extension = geoip.so
+#expat
+
+let
+  cfg = config.services.restya-board;
+
+  runDir = "/run/restya-board";
+
+  poolName = "restya-board";
+  phpfpmSocketName = "/var/run/phpfpm/${poolName}.sock";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.restya-board = {
+
+      enable = mkEnableOption "restya-board";
+
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/restya-board";
+        example = "/var/lib/restya-board";
+        description = ''
+          Data of the application.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "restya-board";
+        example = "restya-board";
+        description = ''
+          User account under which the web-application runs.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nginx";
+        example = "nginx";
+        description = ''
+          Group account under which the web-application runs.
+        '';
+      };
+
+      virtualHost = {
+        serverName = mkOption {
+          type = types.str;
+          default = "restya.board";
+          description = ''
+            Name of the nginx virtualhost to use.
+          '';
+        };
+
+        listenHost = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            Listen address for the virtualhost to use.
+          '';
+        };
+
+        listenPort = mkOption {
+          type = types.int;
+          default = 3000;
+          description = ''
+            Listen port for the virtualhost to use.
+          '';
+        };
+      };
+
+      database = {
+        host = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Host of the database. Leave 'null' to use a local PostgreSQL database.
+            A local PostgreSQL database is initialized automatically.
+          '';
+        };
+
+        port = mkOption {
+          type = types.nullOr types.int;
+          default = 5432;
+          description = ''
+            The database's port.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            Name of the database. The database must exist.
+          '';
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "restya_board";
+          description = ''
+            The database user. The user must exist and have access to
+            the specified database.
+          '';
+        };
+
+        passwordFile = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            The database user's password. 'null' if no password is set.
+          '';
+        };
+      };
+
+      email = {
+        server = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "localhost";
+          description = ''
+            Hostname to send outgoing mail. Null to use the system MTA.
+          '';
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 25;
+          description = ''
+            Port used to connect to SMTP server.
+          '';
+        };
+
+        login = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication login used when sending outgoing mail.
+          '';
+        };
+
+        password = mkOption {
+          type = types.str;
+          default = "";
+          description = ''
+            SMTP authentication password used when sending outgoing mail.
+
+            ATTENTION: The password is stored world-readable in the nix-store!
+          '';
+        };
+      };
+
+      timezone = mkOption {
+        type = types.lines;
+        default = "GMT";
+        description = ''
+          Timezone the web-app runs in.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.phpfpm.poolConfigs = {
+      "${poolName}" = ''
+        listen = "${phpfpmSocketName}";
+        listen.owner = nginx
+        listen.group = nginx
+        listen.mode = 0600
+        user = ${cfg.user}
+        group = ${cfg.group}
+        pm = dynamic
+        pm.max_children = 75
+        pm.start_servers = 10
+        pm.min_spare_servers = 5
+        pm.max_spare_servers = 20
+        pm.max_requests = 500
+        catch_workers_output = 1
+      '';
+    };
+
+    services.phpfpm.phpOptions = ''
+      date.timezone = "CET"
+
+      ${optionalString (!isNull cfg.email.server) ''
+        SMTP = ${cfg.email.server}
+        smtp_port = ${toString cfg.email.port}
+        auth_username = ${cfg.email.login}
+        auth_password = ${cfg.email.password}
+      ''}
+    '';
+
+    services.nginx.enable = true;
+    services.nginx.virtualHosts."${cfg.virtualHost.serverName}" = {
+      listen = [ { addr = cfg.virtualHost.listenHost; port = cfg.virtualHost.listenPort; } ];
+      serverName = cfg.virtualHost.serverName;
+      root = runDir;
+      extraConfig = ''
+        index index.html index.php;
+
+        gzip on;
+        gzip_disable "msie6";
+
+        gzip_comp_level 6;
+        gzip_min_length  1100;
+        gzip_buffers 16 8k;
+        gzip_proxied any;
+        gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss;
+
+        client_max_body_size 300M;
+
+        rewrite ^/oauth/authorize$ /server/php/authorize.php last;
+        rewrite ^/oauth_callback/([a-zA-Z0-9_\.]*)/([a-zA-Z0-9_\.]*)$ /server/php/oauth_callback.php?plugin=$1&code=$2 last;
+        rewrite ^/download/([0-9]*)/([a-zA-Z0-9_\.]*)$ /server/php/download.php?id=$1&hash=$2 last;
+        rewrite ^/ical/([0-9]*)/([0-9]*)/([a-z0-9]*).ics$ /server/php/ical.php?board_id=$1&user_id=$2&hash=$3 last;
+        rewrite ^/api/(.*)$ /server/php/R/r.php?_url=$1&$args last;
+        rewrite ^/api_explorer/api-docs/$ /client/api_explorer/api-docs/index.php last;
+      '';
+
+      locations."/".root = "${runDir}/client";
+
+      locations."~ \.php$" = {
+        tryFiles = "$uri =404";
+        extraConfig = ''
+          include ${pkgs.nginx}/conf/fastcgi_params;
+          fastcgi_pass    unix:${phpfpmSocketName};
+          fastcgi_index   index.php;
+          fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
+          fastcgi_param   PHP_VALUE "upload_max_filesize=9G \n post_max_size=9G \n max_execution_time=200 \n max_input_time=200 \n memory_limit=256M";
+        '';
+      };
+
+      locations."~* \.(css|js|less|html|ttf|woff|jpg|jpeg|gif|png|bmp|ico)" = {
+        root = "${runDir}/client";
+        extraConfig = ''
+          if (-f $request_filename) {
+                  break;
+          }
+          rewrite ^/img/([a-zA-Z_]*)/([a-zA-Z_]*)/([a-zA-Z0-9_\.]*)$ /server/php/image.php?size=$1&model=$2&filename=$3 last;
+          add_header        Cache-Control public;
+          add_header        Cache-Control must-revalidate;
+          expires           7d;
+        '';
+      };
+    };
+
+    systemd.services.restya-board-init = {
+      description = "Restya board initialization";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.RemainAfterExit = true;
+
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "postgresql.service" ];
+      after = [ "network.target" "postgresql.service" ];
+
+      script = ''
+        rm -rf "${runDir}"
+        mkdir -m 750 -p "${runDir}"
+        cp -r "${pkgs.restya-board}/"* "${runDir}"
+        sed -i "s/@restya.com/@${cfg.virtualHost.serverName}/g" "${runDir}/sql/restyaboard_with_empty_data.sql"
+        rm -rf "${runDir}/media"
+        rm -rf "${runDir}/client/img"
+        chmod -R 0750 "${runDir}"
+
+        sed -i "s@^php@${config.services.phpfpm.phpPackage}/bin/php@" "${runDir}/server/php/shell/"*.sh
+
+        ${if (isNull cfg.database.host) then ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', 'localhost');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', 'restya');/g" "${runDir}/server/php/config.inc.php"
+        '' else ''
+          sed -i "s/^.*'R_DB_HOST'.*$/define('R_DB_HOST', '${cfg.database.host}');/g" "${runDir}/server/php/config.inc.php"
+          sed -i "s/^.*'R_DB_PASSWORD'.*$/define('R_DB_PASSWORD', '$(<${cfg.database.dbPassFile})');/g" "${runDir}/server/php/config.inc.php"
+        ''}
+        sed -i "s/^.*'R_DB_PORT'.*$/define('R_DB_PORT', '${toString cfg.database.port}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_NAME'.*$/define('R_DB_NAME', '${cfg.database.name}');/g" "${runDir}/server/php/config.inc.php"
+        sed -i "s/^.*'R_DB_USER'.*$/define('R_DB_USER', '${cfg.database.user}');/g" "${runDir}/server/php/config.inc.php"
+
+        chmod 0400 "${runDir}/server/php/config.inc.php"
+
+        ln -sf "${cfg.dataDir}/media" "${runDir}/media"
+        ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
+
+        chmod g+w "${runDir}/tmp/cache"
+        chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
+
+
+        mkdir -m 0750 -p "${cfg.dataDir}"
+        mkdir -m 0750 -p "${cfg.dataDir}/media"
+        mkdir -m 0750 -p "${cfg.dataDir}/client/img"
+        cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
+        cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
+        chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
+        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
+
+        ${optionalString (isNull cfg.database.host) ''
+          if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE USER ${cfg.database.user} WITH ENCRYPTED PASSWORD 'restya'"
+
+            ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
+              ${config.services.postgresql.package}/bin/psql -U ${config.services.postgresql.superUser} \
+              -c "CREATE DATABASE ${cfg.database.name} OWNER ${cfg.database.user} ENCODING 'UTF8' TEMPLATE template0"
+
+            ${pkgs.sudo}/bin/sudo -u ${cfg.user} \
+              ${config.services.postgresql.package}/bin/psql -U ${cfg.database.user} \
+              -d ${cfg.database.name} -f "${runDir}/sql/restyaboard_with_empty_data.sql"
+
+            touch "${cfg.dataDir}/.db-initialized"
+          fi
+        ''}
+      '';
+    };
+
+    systemd.timers.restya-board = {
+      description = "restya-board scripts for e.g. email notification";
+      wantedBy = [ "timers.target" ];
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+      timerConfig = {
+        OnUnitInactiveSec = "60s";
+        Unit = "restya-board-timers.service";
+      };
+    };
+
+    systemd.services.restya-board-timers = {
+      description = "restya-board scripts for e.g. email notification";
+      serviceConfig.Type = "oneshot";
+      serviceConfig.User = cfg.user;
+
+      after = [ "restya-board-init.service" ];
+      requires = [ "restya-board-init.service" ];
+
+      script = ''
+        /bin/sh ${runDir}/server/php/shell/instant_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/periodic_email_notification.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/imap.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/webhook.sh 2> /dev/null || true
+        /bin/sh ${runDir}/server/php/shell/card_due_notification.sh 2> /dev/null || true
+      '';
+    };
+
+    users.extraUsers.restya-board = {
+      isSystemUser = true;
+      createHome = false;
+      home = runDir;
+      group  = "restya-board";
+    };
+    users.extraGroups.restya-board = {};
+
+    services.postgresql.enable = mkIf (isNull cfg.database.host) true;
+
+    services.postgresql.identMap = optionalString (isNull cfg.database.host)
+      ''
+        restya-board-users restya-board restya_board
+      '';
+
+    services.postgresql.authentication = optionalString (isNull cfg.database.host)
+      ''
+        local restya_board all ident map=restya-board-users
+      '';
+
+  };
+
+}
+
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 76b0ee6da968..610c6463a5eb 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -99,8 +99,8 @@ let
 
       user = mkOption {
         type = types.str;
-        default = "nginx";
-        example = "nginx";
+        default = "tt_rss";
+        example = "tt_rss";
         description = ''
           User account under which both the update daemon and the web-application run.
         '';
@@ -466,26 +466,28 @@ let
       '';
     };
 
-    services.nginx.virtualHosts = mkIf (cfg.virtualHost != null) {
-      "${cfg.virtualHost}" = {
-        root = "${cfg.root}";
-
-        locations."/" = {
-          index = "index.php";
-        };
-
-        locations."~ \.php$" = {
-          extraConfig = ''
-            fastcgi_split_path_info ^(.+\.php)(/.+)$;
-            fastcgi_pass unix:${phpfpmSocketName};
-            fastcgi_index index.php;
-            fastcgi_param SCRIPT_FILENAME ${cfg.root}/$fastcgi_script_name;
-          '';
+    # NOTE: No configuration is done if not using virtual host
+    services.nginx = mkIf (cfg.virtualHost != null) {
+      enable = true;
+      virtualHosts = {
+        "${cfg.virtualHost}" = {
+          root = "${cfg.root}";
+
+          locations."/" = {
+            index = "index.php";
+          };
+
+          locations."~ \.php$" = {
+            extraConfig = ''
+              fastcgi_split_path_info ^(.+\.php)(/.+)$;
+              fastcgi_pass unix:${phpfpmSocketName};
+              fastcgi_index index.php;
+            '';
+          };
         };
       };
     };
 
-
     systemd.services.tt-rss = let
       dbService = if cfg.database.type == "pgsql" then "postgresql.service" else "mysql.service";
     in {
@@ -496,14 +498,14 @@ let
           callSql = e:
               if cfg.database.type == "pgsql" then ''
                   ${optionalString (cfg.database.password != null) "PGPASSWORD=${cfg.database.password}"} \
-                  ${pkgs.postgresql95}/bin/psql \
+                  ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.postgresql.package}/bin/psql \
                     -U ${cfg.database.user} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} --port ${toString dbPort}"} \
                     -c '${e}' \
                     ${cfg.database.name}''
 
               else if cfg.database.type == "mysql" then ''
-                  echo '${e}' | ${pkgs.mysql}/bin/mysql \
+                  echo '${e}' | ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${config.services.mysql.package}/bin/mysql \
                     -u ${cfg.database.user} \
                     ${optionalString (cfg.database.password != null) "-p${cfg.database.password}"} \
                     ${optionalString (cfg.database.host != null) "-h ${cfg.database.host} -P ${toString dbPort}"} \
@@ -521,6 +523,14 @@ let
         ''
 
         + (optionalString (cfg.database.type == "pgsql") ''
+          ${optionalString (cfg.database.host == null && cfg.database.password == null) ''
+            if ! [ -e ${cfg.root}/.db-created ]; then
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser ${cfg.database.user}
+              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O ${cfg.database.user} ${cfg.database.name}
+              touch ${cfg.root}/.db-created
+            fi
+          ''}
+
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -554,5 +564,28 @@ let
         requires = ["${dbService}"];
         after = ["network.target" "${dbService}"];
     };
+
+    services.mysql = optionalAttrs (cfg.database.type == "mysql") {
+      enable = true;
+      package = mkDefault pkgs.mysql;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [
+        {
+          name = cfg.user;
+          ensurePermissions = {
+            "${cfg.database.name}.*" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+    };
+
+    services.postgresql = optionalAttrs (cfg.database.type == "pgsql") {
+      enable = mkDefault true;
+    };
+
+    users = optionalAttrs (cfg.user == "tt_rss") {
+      extraUsers.tt_rss.group = "tt_rss";
+      extraGroups.tt_rss = {};
+    };
   };
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
index 94e85f1f4289..82b8bf3e30db 100644
--- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix
@@ -188,8 +188,7 @@ let
       /* date format to be used while writing to the owncloud logfile */
       'logdateformat' => 'F d, Y H:i:s',
 
-      /* timezone used while writing to the owncloud logfile (default: UTC) */
-      'logtimezone' => '${serverInfo.fullConfig.time.timeZone}',
+      ${tzSetting}
 
       /* Append all database queries and parameters to the log file.
        (watch out, this option can increase the size of your log file)*/
@@ -339,6 +338,31 @@ let
 
     '';
 
+  tzSetting = let tz = serverInfo.fullConfig.time.timeZone; in optionalString (!isNull tz) ''
+    /* timezone used while writing to the owncloud logfile (default: UTC) */
+    'logtimezone' => '${tz}',
+  '';
+
+  postgresql = serverInfo.fullConfig.services.postgresql.package;
+
+  setupDb = pkgs.writeScript "setup-owncloud-db" ''
+    #!${pkgs.runtimeShell}
+    PATH="${postgresql}/bin"
+    createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+    createdb "${config.dbName}" -O "${config.dbUser}" || true
+    psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
+
+    QUERY="CREATE TABLE appconfig
+             ( appid       VARCHAR( 255 ) NOT NULL
+             , configkey   VARCHAR( 255 ) NOT NULL
+             , configvalue VARCHAR( 255 ) NOT NULL
+             );
+           GRANT ALL ON appconfig TO ${config.dbUser};
+           ALTER TABLE appconfig OWNER TO ${config.dbUser};"
+
+    psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+  '';
+
 in
 
 rec {
@@ -353,7 +377,7 @@ rec {
       ''}
 
       <Directory ${config.package}>
-        ${builtins.readFile "${config.package}/.htaccess"}
+        Include ${config.package}/.htaccess
       </Directory>
     '';
 
@@ -373,7 +397,7 @@ rec {
       defaultText = "pkgs.owncloud70";
       example = literalExample "pkgs.owncloud70";
       description = ''
-          PostgreSQL package to use.
+          ownCloud package to use.
       '';
     };
 
@@ -574,13 +598,7 @@ rec {
       chmod -R o-rwx ${config.dataDir}
       chown -R wwwrun:wwwrun ${config.dataDir}
 
-      ${pkgs.postgresql}/bin/createuser -s -r postgres
-      ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
-      ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" || true
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
-
-      QUERY="CREATE TABLE appconfig (appid VARCHAR( 255 ) NOT NULL ,configkey VARCHAR( 255 ) NOT NULL ,configvalue VARCHAR( 255 ) NOT NULL); GRANT ALL ON appconfig TO ${config.dbUser}; ALTER TABLE appconfig OWNER TO ${config.dbUser};"
-      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+      ${pkgs.sudo}/bin/sudo -u postgres ${setupDb}
     fi
 
     if [ -e ${config.package}/config/ca-bundle.crt ]; then
@@ -591,7 +609,11 @@ rec {
 
     chown wwwrun:wwwrun ${config.dataDir}/owncloud.log || true
 
-    QUERY="INSERT INTO groups (gid) values('admin'); INSERT INTO users (uid,password) values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}'); INSERT INTO group_user (gid,uid) values('admin','${config.adminUser}');"
-    ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
+    QUERY="INSERT INTO groups (gid) values('admin');
+           INSERT INTO users (uid,password)
+             values('${config.adminUser}','${builtins.hashString "sha1" config.adminPassword}');
+           INSERT INTO group_user (gid,uid)
+             values('admin','${config.adminUser}');"
+    ${pkgs.sudo}/bin/sudo -u postgres ${postgresql}/bin/psql -h "/tmp" -U postgres -d ${config.dbName} -Atw -c "$QUERY" || true
   '';
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
index 1d53ce659005..4bbd041b6e04 100644
--- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -118,7 +118,7 @@ with lib;
     default = [];
     example = [
       { urlPath = "/foo/bar.png";
-        files = "/home/eelco/some-file.png";
+        file = "/home/eelco/some-file.png";
       }
     ];
     description = ''
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index 700b4469c565..d23e810dcc62 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -50,11 +50,14 @@ let
     "mod_geoip"
     "mod_magnet"
     "mod_mysql_vhost"
+    "mod_openssl"  # since v1.4.46
     "mod_scgi"
     "mod_setenv"
     "mod_trigger_b4_dl"
     "mod_uploadprogress"
+    "mod_vhostdb"  # since v1.4.46
     "mod_webdav"
+    "mod_wstunnel"  # since v1.4.46
   ];
 
   maybeModuleString = moduleName:
diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
index c8d9836b0b68..c494d6966a7f 100644
--- a/nixos/modules/services/web-servers/lighttpd/gitweb.nix
+++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -3,12 +3,10 @@
 with lib;
 
 let
-  cfg = config.services.lighttpd.gitweb;
-  gitwebConfigFile = pkgs.writeText "gitweb.conf" ''
-    # path to git projects (<project>.git)
-    $projectroot = "${cfg.projectroot}";
-    ${cfg.extraConfig}
-  '';
+  cfg = config.services.gitweb;
+  package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+    gitwebTheme = true;
+  });
 
 in
 {
@@ -23,26 +21,9 @@ in
       '';
     };
 
-    projectroot = mkOption {
-      default = "/srv/git";
-      type = types.path;
-      description = ''
-        Path to git projects (bare repositories) that should be served by
-        gitweb. Must not end with a slash.
-      '';
-    };
-
-    extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Verbatim configuration text appended to the generated gitweb.conf file.
-      '';
-    };
-
   };
 
-  config = mkIf cfg.enable {
+  config = mkIf config.services.lighttpd.gitweb.enable {
 
     # declare module dependencies
     services.lighttpd.enableModules = [ "mod_cgi" "mod_redirect" "mod_alias" "mod_setenv" ];
@@ -56,11 +37,11 @@ in
               "^/gitweb$" => "/gitweb/"
           )
           alias.url = (
-              "/gitweb/static/" => "${pkgs.git}/share/gitweb/static/",
-              "/gitweb/"        => "${pkgs.git}/share/gitweb/gitweb.cgi"
+              "/gitweb/static/" => "${package}/static/",
+              "/gitweb/"        => "${package}/gitweb.cgi"
           )
           setenv.add-environment = (
-              "GITWEB_CONFIG" => "${gitwebConfigFile}",
+              "GITWEB_CONFIG" => "${cfg.gitwebConfigFile}",
               "HOME" => "${cfg.projectroot}"
           )
       }
diff --git a/nixos/modules/services/web-servers/lighttpd/inginious.nix b/nixos/modules/services/web-servers/lighttpd/inginious.nix
index 669e81d0f14b..8c813d116a52 100644
--- a/nixos/modules/services/web-servers/lighttpd/inginious.nix
+++ b/nixos/modules/services/web-servers/lighttpd/inginious.nix
@@ -113,7 +113,6 @@ in
 
     tasksDirectory = mkOption {
       type = types.path;
-      default = "${inginious}/lib/python2.7/site-packages/inginious/tasks";
       example = "/var/lib/INGInious/tasks";
       description = ''
         Path to the tasks folder.
@@ -218,6 +217,7 @@ in
 
       # Common
       {
+        services.lighttpd.inginious.tasksDirectory = mkDefault "${inginious}/lib/python2.7/site-packages/inginious/tasks";
         # To access inginous tools (like inginious-test-task)
         environment.systemPackages = [ inginious ];
 
diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix
new file mode 100644
index 000000000000..a888f623616e
--- /dev/null
+++ b/nixos/modules/services/web-servers/mighttpd2.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.mighttpd2;
+  configFile = pkgs.writeText "mighty-config" cfg.config;
+  routingFile = pkgs.writeText "mighty-routing" cfg.routing;
+in {
+  options.services.mighttpd2 = {
+    enable = mkEnableOption "Mighttpd2 web server";
+
+    config = mkOption {
+      default = "";
+      example = ''
+        # Example configuration for Mighttpd 2
+        Port: 80
+        # IP address or "*"
+        Host: *
+        Debug_Mode: Yes # Yes or No
+        # If available, "nobody" is much more secure for User:.
+        User: root
+        # If available, "nobody" is much more secure for Group:.
+        Group: root
+        Pid_File: /var/run/mighty.pid
+        Logging: Yes # Yes or No
+        Log_File: /var/log/mighty # The directory must be writable by User:
+        Log_File_Size: 16777216 # bytes
+        Log_Backup_Number: 10
+        Index_File: index.html
+        Index_Cgi: index.cgi
+        Status_File_Dir: /usr/local/share/mighty/status
+        Connection_Timeout: 30 # seconds
+        Fd_Cache_Duration: 10 # seconds
+        # Server_Name: Mighttpd/3.x.y
+        Tls_Port: 443
+        Tls_Cert_File: cert.pem # should change this with an absolute path
+        # should change this with comma-separated absolute paths
+        Tls_Chain_Files: chain.pem
+        # Currently, Tls_Key_File must not be encrypted.
+        Tls_Key_File: privkey.pem # should change this with an absolute path
+        Service: 0 # 0 is HTTP only, 1 is HTTPS only, 2 is both
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim config file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    routing = mkOption {
+      default = "";
+      example = ''
+        # Example routing for Mighttpd 2
+
+        # Domain lists
+        [localhost www.example.com]
+
+        # Entries are looked up in the specified order
+        # All paths must end with "/"
+
+        # A path to CGI scripts should be specified with "=>"
+        /~alice/cgi-bin/ => /home/alice/public_html/cgi-bin/
+
+        # A path to static files should be specified with "->"
+        /~alice/         -> /home/alice/public_html/
+        /cgi-bin/        => /export/cgi-bin/
+
+        # Reverse proxy rules should be specified with ">>"
+        # /path >> host:port/path2
+        # Either "host" or ":port" can be committed, but not both.
+        /app/cal/        >> example.net/calendar/
+        # Yesod app in the same server
+        /app/wiki/       >> 127.0.0.1:3000/
+
+        /                -> /export/www/
+      '';
+      type = types.lines;
+      description = ''
+        Verbatim routing file to use
+        (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html)
+      '';
+    };
+
+    cores = mkOption {
+      default = null;
+      type = types.nullOr types.int;
+      description = ''
+        How many cores to use.
+        If null it will be determined automatically
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.routing != "";
+          message = "You need at least one rule in mighttpd2.routing";
+        }
+      ];
+    systemd.services.mighttpd2 = {
+      description = "Mighttpd2 web server";
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.haskellPackages.mighttpd2}/bin/mighty \
+            ${configFile} \
+            ${routingFile} \
+            +RTS -N${optionalString (cfg.cores != null) "${cfg.cores}"}
+        '';
+        Type = "simple";
+        User = "mighttpd2";
+        Group = "mighttpd2";
+        Restart = "on-failure";
+        AmbientCapabilities = "cap_net_bind_service";
+        CapabilityBoundingSet = "cap_net_bind_service";
+      };
+    };
+
+    users.extraUsers.mighttpd2 = {
+      group = "mighttpd2";
+      uid = config.ids.uids.mighttpd2;
+      isSystemUser = true;
+    };
+
+    users.extraGroups.mighttpd2.gid = config.ids.gids.mighttpd2;
+  };
+
+  meta.maintainers = with lib.maintainers; [ fgaz ];
+}
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 97511aac9737..938a8a1fe334 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -9,12 +9,16 @@ let
       serverName = if vhostConfig.serverName != null
         then vhostConfig.serverName
         else vhostName;
+      acmeDirectory = config.security.acme.directory;
     in
     vhostConfig // {
       inherit serverName;
     } // (optionalAttrs vhostConfig.enableACME {
-      sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem";
-      sslCertificateKey = "/var/lib/acme/${serverName}/key.pem";
+      sslCertificate = "${acmeDirectory}/${serverName}/fullchain.pem";
+      sslCertificateKey = "${acmeDirectory}/${serverName}/key.pem";
+    }) // (optionalAttrs (vhostConfig.useACMEHost != null) {
+      sslCertificate = "${acmeDirectory}/${vhostConfig.useACMEHost}/fullchain.pem";
+      sslCertificateKey = "${acmeDirectory}/${vhostConfig.useACMEHost}/key.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -167,13 +171,14 @@ let
 
         listenString = { addr, port, ssl, ... }:
           "listen ${addr}:${toString port} "
-          + optionalString ssl "ssl http2 "
+          + optionalString ssl "ssl "
+          + optionalString (ssl && vhost.http2) "http2 "
           + optionalString vhost.default "default_server "
           + ";";
 
         redirectListen = filter (x: !x.ssl) defaultListen;
 
-        acmeLocation = ''
+        acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) ''
           location /.well-known/acme-challenge {
             ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"}
             root ${vhost.acmeRoot};
@@ -193,7 +198,7 @@ let
             ${concatMapStringsSep "\n" listenString redirectListen}
 
             server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-            ${optionalString vhost.enableACME acmeLocation}
+            ${acmeLocation}
             location / {
               return 301 https://$host$request_uri;
             }
@@ -203,7 +208,7 @@ let
         server {
           ${concatMapStringsSep "\n" listenString hostListen}
           server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases};
-          ${optionalString vhost.enableACME acmeLocation}
+          ${acmeLocation}
           ${optionalString (vhost.root != null) "root ${vhost.root};"}
           ${optionalString (vhost.globalRedirect != null) ''
             return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
@@ -554,6 +559,14 @@ in
           are mutually exclusive.
         '';
       }
+
+      {
+        assertion = all (conf: !(conf.enableACME && conf.useACMEHost != null)) (attrValues virtualHosts);
+        message = ''
+          Options services.nginx.service.virtualHosts.<name>.enableACME and
+          services.nginx.virtualHosts.<name>.useACMEHost are mutually exclusive.
+        '';
+      }
     ];
 
     systemd.services.nginx = {
@@ -566,6 +579,7 @@ in
         mkdir -p ${cfg.stateDir}/logs
         chmod 700 ${cfg.stateDir}
         chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
+        ${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir} -t
         '';
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir}";
@@ -579,7 +593,7 @@ in
     security.acme.certs = filterAttrs (n: v: v != {}) (
       let
         vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
-        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME) vhostsConfigs;
+        acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME && vhostConfig.useACMEHost == null) vhostsConfigs;
         acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = {
             user = cfg.user;
             group = lib.mkDefault cfg.group;
diff --git a/nixos/modules/services/web-servers/nginx/gitweb.nix b/nixos/modules/services/web-servers/nginx/gitweb.nix
new file mode 100644
index 000000000000..272fd1480185
--- /dev/null
+++ b/nixos/modules/services/web-servers/nginx/gitweb.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.gitweb;
+  package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme {
+    gitwebTheme = true;
+  });
+
+in
+{
+
+  options.services.nginx.gitweb = {
+
+    enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        If true, enable gitweb in nginx. Access it at http://yourserver/gitweb
+      '';
+    };
+
+  };
+
+  config = mkIf config.services.nginx.gitweb.enable {
+
+    systemd.services.gitweb = {
+      description = "GitWeb service";
+      script = "${package}/gitweb.cgi --fastcgi --nproc=1";
+      environment  = {
+        FCGI_SOCKET_PATH = "/run/gitweb/gitweb.sock";
+      };
+      serviceConfig = {
+        User = "nginx";
+        Group = "nginx";
+        RuntimeDirectory = [ "gitweb" ];
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    services.nginx = {
+      virtualHosts.default = {
+        locations."/gitweb/static/" = {
+          alias = "${package}/static/";
+        };
+        locations."/gitweb/" = {
+          extraConfig = ''
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            fastcgi_param GITWEB_CONFIG ${cfg.gitwebConfigFile};
+            fastcgi_pass unix:/run/gitweb/gitweb.sock;
+          '';
+        };
+      };
+    };
+
+  };
+
+  meta.maintainers = with maintainers; [ gnidorah ];
+
+}
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 801601aafd9d..bf18108a1a3c 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -48,7 +48,21 @@ with lib;
     enableACME = mkOption {
       type = types.bool;
       default = false;
-      description = "Whether to ask Let's Encrypt to sign a certificate for this vhost.";
+      description = ''
+        Whether to ask Let's Encrypt to sign a certificate for this vhost.
+        Alternately, you can use an existing certificate through <option>useACMEHost</option>.
+      '';
+    };
+
+    useACMEHost = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        A host of an existing Let's Encrypt certificate to use.
+        This is useful if you have many subdomains and want to avoid hitting the
+        <link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>.
+        Alternately, you can generate a certificate through <option>enableACME</option>.
+      '';
     };
 
     acmeRoot = mkOption {
@@ -114,6 +128,20 @@ with lib;
       description = "Path to server SSL certificate key.";
     };
 
+    http2 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to enable HTTP 2.
+        Note that (as of writing) due to nginx's implementation, to disable
+        HTTP 2 you have to disable it on all vhosts that use a given
+        IP address / port.
+        If there is one server block configured to enable http2,then it is
+        enabled for all server blocks on this IP.
+        See https://stackoverflow.com/a/39466948/263061.
+      '';
+    };
+
     root = mkOption {
       type = types.nullOr types.path;
       default = null;
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index 943415e08c64..aa94e0e976c9 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -19,27 +19,43 @@ in
   options = {
 
     services.tomcat = {
-
-      enable = mkOption {
-        default = false;
-        description = "Whether to enable Apache Tomcat";
-      };
+      enable = mkEnableOption "Apache Tomcat";
 
       package = mkOption {
         type = types.package;
         default = pkgs.tomcat85;
         defaultText = "pkgs.tomcat85";
-        example = lib.literalExample "pkgs.tomcatUnstable";
+        example = lib.literalExample "pkgs.tomcat9";
         description = ''
           Which tomcat package to use.
         '';
       };
 
       baseDir = mkOption {
+        type = lib.types.path;
         default = "/var/tomcat";
         description = "Location where Tomcat stores configuration files, webapplications and logfiles";
       };
 
+      logDirs = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Directories to create in baseDir/logs/";
+      };
+
+      extraConfigFiles = mkOption {
+        default = [];
+        type = types.listOf types.path;
+        description = "Extra configuration files to pull into the tomcat conf directory";
+      };
+
+      extraEnvironment = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ENVIRONMENT=production" ];
+        description = "Environment Variables to pass to the tomcat service";
+      };
+
       extraGroups = mkOption {
         default = [];
         example = [ "users" ];
@@ -47,31 +63,46 @@ in
       };
 
       user = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "User account under which Apache Tomcat runs.";
       };
 
       group = mkOption {
+        type = types.str;
         default = "tomcat";
         description = "Group account under which Apache Tomcat runs.";
       };
 
       javaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
       };
 
       catalinaOpts = mkOption {
+        type = types.either (types.listOf types.str) types.str;
         default = "";
         description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
       };
 
       sharedLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
       };
 
+      serverXml = mkOption {
+        type = types.lines;
+        default = "";
+        description = "
+          Verbatim server.xml configuration.
+          This is mutually exclusive with the virtualHosts options.
+        ";
+      };
+
       commonLibs = mkOption {
+        type = types.listOf types.str;
         default = [];
         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
       };
@@ -84,11 +115,21 @@ in
       };
 
       virtualHosts = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            name = mkOption {
+              type = types.listOf types.str;
+              description = "name of the virtualhost";
+              default = [];
+            };
+          };
+        });
         default = [];
         description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
       };
 
       logPerVirtualHost = mkOption {
+        type = types.bool;
         default = false;
         description = "Whether to enable logging per virtual host.";
       };
@@ -104,11 +145,13 @@ in
 
         enable = mkOption {
           default = false;
+          type = types.bool;
           description = "Whether to enable an Apache Axis2 container";
         };
 
         services = mkOption {
           default = [];
+          type = types.listOf types.str;
           description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
         };
 
@@ -140,130 +183,104 @@ in
       description = "Apache Tomcat server";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      serviceConfig.Type = "oneshot";
-      serviceConfig.RemainAfterExit = true;
 
       preStart = ''
         # Create the base directory
-        mkdir -p ${cfg.baseDir}
+        mkdir -p \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
+        chown ${cfg.user}:${cfg.group} \
+          ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
 
         # Create a symlink to the bin directory of the tomcat component
         ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
 
-        # Create a conf/ directory
-        mkdir -p ${cfg.baseDir}/conf
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf
-
         # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
-        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml)
-        do
-            ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
+        for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
+          ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
         done
 
-        # Create subdirectory for virtual hosts
-        mkdir -p ${cfg.baseDir}/virtualhosts
+        ${if cfg.extraConfigFiles != [] then ''
+          for i in ${toString cfg.extraConfigFiles}; do
+            ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
+          done
+        '' else ""}
 
         # Create a modified catalina.properties file
         # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
         sed -e 's|''${catalina.home}|''${catalina.base}|g' \
-            -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
-            ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
-
-        # Create a modified server.xml which also includes all virtual hosts
-        sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${
-                     toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
-            ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
-
-        # Create a logs/ directory
-        mkdir -p ${cfg.baseDir}/logs
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs
-        ${if cfg.logPerVirtualHost then
-           toString (map (h: ''
-                                mkdir -p ${cfg.baseDir}/logs/${h.name}
-                                chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
-                             '') cfg.virtualHosts) else ''''}
-
-        # Create a temp/ directory
-        mkdir -p ${cfg.baseDir}/temp
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp
-
-        # Create a lib/ directory
-        mkdir -p ${cfg.baseDir}/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib
-
-        # Create a shared/lib directory
-        mkdir -p ${cfg.baseDir}/shared/lib
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib
-
-        # Create a webapps/ directory
-        mkdir -p ${cfg.baseDir}/webapps
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps
+          -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
+          ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
+
+        ${if cfg.serverXml != "" then ''
+          cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
+          '' else ''
+          # Create a modified server.xml which also includes all virtual hosts
+          sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
+                ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
+          ''
+        }
+        ${optionalString (cfg.logDirs != []) ''
+          for i in ${toString cfg.logDirs}; do
+            mkdir -p ${cfg.baseDir}/logs/$i
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
+          done
+        ''}
+        ${optionalString cfg.logPerVirtualHost (toString (map (h: ''
+          mkdir -p ${cfg.baseDir}/logs/${h.name}
+          chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
+        '') cfg.virtualHosts))}
 
         # Symlink all the given common libs files or paths into the lib/ directory
-        for i in ${tomcat} ${toString cfg.commonLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
-                done
-            fi
+        for i in ${tomcat} ${toString cfg.commonLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given shared libs files or paths into the shared/lib/ directory
-        for i in ${toString cfg.sharedLibs}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the common/lib/ directory
-                ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/shared/lib/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
-                done
-            fi
+        for i in ${toString cfg.sharedLibs}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the common/lib/ directory
+            ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/shared/lib/*; do
+              ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
+            done
+          fi
         done
 
         # Symlink all the given web applications files or paths into the webapps/ directory
-        for i in ${toString cfg.webapps}
-        do
-            if [ -f $i ]
-            then
-                # If the given web application is a file, symlink it into the webapps/ directory
-                ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
-            elif [ -d $i ]
-            then
-                # If the given web application is a directory, then iterate over the files
-                # in the special purpose directories and symlink them into the tomcat tree
-
-                for j in $i/webapps/*
-                do
-                    ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
-                done
+        for i in ${toString cfg.webapps}; do
+          if [ -f $i ]; then
+            # If the given web application is a file, symlink it into the webapps/ directory
+            ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
+          elif [ -d $i ]; then
+            # If the given web application is a directory, then iterate over the files
+            # in the special purpose directories and symlink them into the tomcat tree
+
+            for j in $i/webapps/*; do
+              ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
+            done
 
-                # Also symlink the configuration files if they are included
-                if [ -d $i/conf/Catalina ]
-                then
-                    for j in $i/conf/Catalina/*
-                    do
-                        mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
-                        ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                    done
-                fi
+            # Also symlink the configuration files if they are included
+            if [ -d $i/conf/Catalina ]; then
+              for j in $i/conf/Catalina/*; do
+                mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
+                ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+              done
             fi
+          fi
         done
 
         ${toString (map (virtualHost: ''
@@ -275,94 +292,79 @@ in
 
           # Symlink all the given web applications files or paths into the webapps/ directory
           # of this virtual host
-          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"
-          do
-              if [ -f $i ]
-              then
-                  # If the given web application is a file, symlink it into the webapps/ directory
-                  ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
-              elif [ -d $i ]
-              then
-                  # If the given web application is a directory, then iterate over the files
-                  # in the special purpose directories and symlink them into the tomcat tree
-
-                  for j in $i/webapps/*
-                  do
-                      ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
-                  done
-
-                  # Also symlink the configuration files if they are included
-                  if [ -d $i/conf/Catalina ]
-                  then
-                      for j in $i/conf/Catalina/*
-                      do
-                          mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
-                          ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
-                      done
-                  fi
+          for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"; do
+            if [ -f $i ]; then
+              # If the given web application is a file, symlink it into the webapps/ directory
+              ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/*; do
+                ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
+                done
               fi
+            fi
           done
-
-          ''
-        ) cfg.virtualHosts) }
-
-        # Create a work/ directory
-        mkdir -p ${cfg.baseDir}/work
-        chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work
-
-        ${if cfg.axis2.enable then
-            ''
-            # Copy the Axis2 web application
-            cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
-
-            # Turn off addressing, which causes many errors
-            sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
-
-            # Modify permissions on the Axis2 application
-            chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
-
-            # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
-            for i in ${toString cfg.axis2.services}
-            do
-                if [ -f $i ]
-                then
-                    # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
-                    ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
-                elif [ -d $i ]
-                then
-                    # If the given web application is a directory, then iterate over the files
-                    # in the special purpose directories and symlink them into the tomcat tree
-
-                    for j in $i/webapps/axis2/WEB-INF/services/*
-                    do
-                        ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
-                    done
-
-                    # Also symlink the configuration files if they are included
-                    if [ -d $i/conf/Catalina ]
-                    then
-                        for j in $i/conf/Catalina/*
-                        do
-                            ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
-                        done
-                    fi
-                fi
-            done
-            ''
-        else ""}
-      '';
-
-      script = ''
-          ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
-      '';
-
-      preStop = ''
-        echo "Stopping tomcat..."
-        CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
+        '') cfg.virtualHosts)}
+
+        ${optionalString cfg.axis2.enable ''
+          # Copy the Axis2 web application
+          cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
+
+          # Turn off addressing, which causes many errors
+          sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
+
+          # Modify permissions on the Axis2 application
+          chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
+
+          # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
+          for i in ${toString cfg.axis2.services}; do
+            if [ -f $i ]; then
+              # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
+              ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
+            elif [ -d $i ]; then
+              # If the given web application is a directory, then iterate over the files
+              # in the special purpose directories and symlink them into the tomcat tree
+
+              for j in $i/webapps/axis2/WEB-INF/services/*; do
+                ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
+              done
+
+              # Also symlink the configuration files if they are included
+              if [ -d $i/conf/Catalina ]; then
+                for j in $i/conf/Catalina/*; do
+                  ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+                done
+              fi
+            fi
+          done
+        ''}
       '';
 
+      serviceConfig = {
+        Type = "forking";
+        PermissionsStartOnly = true;
+        PIDFile="/run/tomcat/tomcat.pid";
+        RuntimeDirectory = "tomcat";
+        User = cfg.user;
+        Environment=[
+          "CATALINA_BASE=${cfg.baseDir}"
+          "CATALINA_PID=/run/tomcat/tomcat.pid"
+          "JAVA_HOME='${cfg.jdk}'"
+          "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
+          "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
+        ] ++ cfg.extraEnvironment;
+        ExecStart = "${tomcat}/bin/startup.sh";
+        ExecStop = "${tomcat}/bin/shutdown.sh";
+      };
     };
-
   };
-
 }
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 4ede4fc20967..b6c7fef21fb2 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -64,6 +64,16 @@ in {
       '';
     };
 
+    group = mkOption {
+      default = "traefik";
+      type = types.string;
+      example = "docker";
+      description = ''
+        Set the group that traefik runs under.
+        For the docker backend this needs to be set to <literal>docker</literal> instead.
+      '';
+    };
+
     package = mkOption {
       default = pkgs.traefik;
       defaultText = "pkgs.traefik";
@@ -87,7 +97,7 @@ in {
         ];
         Type = "simple";
         User = "traefik";
-        Group = "traefik";
+        Group = cfg.group;
         Restart = "on-failure";
         StartLimitInterval = 86400;
         StartLimitBurst = 5;
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index c3bc065d4651..bc74d62b116a 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -1,14 +1,27 @@
 { config, lib, pkgs, ...}:
+
+with lib;
+
 let
   cfg = config.services.varnish;
 
+  commandLine = "-f ${pkgs.writeText "default.vcl" cfg.config}" +
+      optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([cfg.package] ++ cfg.extraModules)}' -r vmod_path";
 in
-with lib;
 {
   options = {
     services.varnish = {
       enable = mkEnableOption "Varnish Server";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.varnish5;
+        defaultText = "pkgs.varnish5";
+        description = ''
+          The package to use
+        '';
+      };
+
       http_address = mkOption {
         type = types.str;
         default = "*:6081";
@@ -35,7 +48,7 @@ with lib;
       extraModules = mkOption {
         type = types.listOf types.package;
         default = [];
-        example = literalExample "[ pkgs.varnish-geoip ]";
+        example = literalExample "[ pkgs.varnish5Packages.geoip ]";
         description = "
           Varnish modules (except 'std').
         ";
@@ -69,8 +82,7 @@ with lib;
       serviceConfig = {
         Type = "simple";
         PermissionsStartOnly = true;
-        ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -F ${cfg.extraCommandLine}"
-          + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path";
+        ExecStart = "${cfg.package}/sbin/varnishd -a ${cfg.http_address} -n ${cfg.stateDir} -F ${cfg.extraCommandLine} ${commandLine}";
         Restart = "always";
         RestartSec = "5s";
         User = "varnish";
@@ -81,7 +93,15 @@ with lib;
       };
     };
 
-    environment.systemPackages = [ pkgs.varnish ];
+    environment.systemPackages = [ cfg.package ];
+
+    # check .vcl syntax at compile time (e.g. before nixops deployment)
+    system.extraDependencies = [
+      (pkgs.stdenv.mkDerivation {
+        name = "check-varnish-syntax";
+        buildCommand = "${cfg.package}/sbin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1)";
+      })
+    ];
 
     users.extraUsers.varnish = {
       group = "varnish";
diff --git a/nixos/modules/services/x11/compton.nix b/nixos/modules/services/x11/compton.nix
index 11cbcac6fa85..8641c05de52e 100644
--- a/nixos/modules/services/x11/compton.nix
+++ b/nixos/modules/services/x11/compton.nix
@@ -181,10 +181,10 @@ in {
     };
 
     backend = mkOption {
-      type = types.enum [ "glx" "xrender" ];
+      type = types.enum [ "glx" "xrender" "xr_glx_hybrid" ];
       default = "xrender";
       description = ''
-        Backend to use: <literal>glx</literal> or <literal>xrender</literal>.
+        Backend to use: <literal>glx</literal>, <literal>xrender</literal> or <literal>xr_glx_hybrid</literal>.
       '';
     };
 
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 39b27d4ceb61..f435e85f6b83 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -87,11 +87,11 @@ in
 
       default = mkOption {
         type = types.str;
-        default = "none";
-        example = "plasma5";
+        default = "";
+        example = "none";
         description = "Default desktop manager loaded if none have been chosen.";
         apply = defaultDM:
-          if defaultDM == "none" && cfg.session.list != [] then
+          if defaultDM == "" && cfg.session.list != [] then
             (head cfg.session.list).name
           else if any (w: w.name == defaultDM) cfg.session.list then
             defaultDM
@@ -109,9 +109,5 @@ in
 
   };
 
-  config = {
-    services.xserver.displayManager.session = cfg.session.list;
-    environment.systemPackages =
-      mkIf cfg.session.needBGPackages [ pkgs.feh ]; # xsetroot via xserver.enable
-  };
+  config.services.xserver.displayManager.session = cfg.session.list;
 }
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 8a523f0d8036..da3287aaea6e 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -33,7 +33,7 @@ in
       pkgs.xorg.xauth # used by kdesu
       pkgs.gtk2 # To get GTK+'s themes.
       pkgs.tango-icon-theme
-      pkgs.shared_mime_info
+      pkgs.shared-mime-info
       pkgs.gnome2.gnomeicontheme
       pkgs.xorg.xcursorthemes
     ];
@@ -47,7 +47,7 @@ in
         export GTK_DATA_PREFIX=${config.system.path}
         # find theme engines
         export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
-        export XDG_MENU_PREFIX=enlightenment
+        export XDG_MENU_PREFIX=e-
 
         export GST_PLUGIN_PATH="${GST_PLUGIN_PATH}"
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index e5a79496c7a5..10e8ef0ed381 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -27,7 +27,7 @@ let
   nixos-gsettings-desktop-schemas = pkgs.runCommand "nixos-gsettings-desktop-schemas" {}
     ''
      mkdir -p $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
-     cp -rf ${pkgs.gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
+     cp -rf ${pkgs.gnome3.gsettings-desktop-schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
      ${concatMapStrings (pkg: "cp -rf ${pkg}/share/gsettings-schemas/*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas\n") cfg.extraGSettingsOverridePackages}
 
@@ -60,7 +60,7 @@ in {
         example = literalExample "[ pkgs.gnome3.gpaste ]";
         description = "Additional list of packages to be added to the session search path.
                        Useful for gnome shell extensions or gsettings-conditionated autostart.";
-        apply = list: list ++ [ pkgs.gnome3.gnome_shell pkgs.gnome3.gnome-shell-extensions ];
+        apply = list: list ++ [ pkgs.gnome3.gnome-shell pkgs.gnome3.gnome-shell-extensions ];
       };
 
       extraGSettingsOverrides = mkOption {
@@ -108,6 +108,7 @@ in {
     services.gnome3.seahorse.enable = mkDefault true;
     services.gnome3.sushi.enable = mkDefault true;
     services.gnome3.tracker.enable = mkDefault true;
+    services.gnome3.tracker-miners.enable = mkDefault true;
     hardware.pulseaudio.enable = mkDefault true;
     services.telepathy.enable = mkDefault true;
     networking.networkmanager.enable = mkDefault true;
@@ -117,13 +118,13 @@ in {
     services.packagekit.enable = mkDefault true;
     hardware.bluetooth.enable = mkDefault true;
     services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
-    services.udev.packages = [ pkgs.gnome3.gnome_settings_daemon ];
+    services.udev.packages = [ pkgs.gnome3.gnome-settings-daemon ];
     systemd.packages = [ pkgs.gnome3.vino ];
 
     # If gnome3 is installed, build vim for gtk3 too.
     nixpkgs.config.vim.gui = "gtk3";
 
-    fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell_fonts ];
+    fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell-fonts ];
 
     services.xserver.desktopManager.session = singleton
       { name = "gnome3";
@@ -135,7 +136,7 @@ in {
           # find theme engines
           export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
 
-          export XDG_MENU_PREFIX=gnome
+          export XDG_MENU_PREFIX=gnome-
 
           ${concatMapStrings (p: ''
             if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
@@ -152,7 +153,7 @@ in {
           export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${mimeAppsList}/share
 
           # Override gsettings-desktop-schema
-          export XDG_DATA_DIRS=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS
+          export NIX_GSETTINGS_OVERRIDES_DIR=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas
 
           # Let nautilus find extensions
           export NAUTILUS_EXTENSION_DIR=${config.system.path}/lib/nautilus/extensions-3.0/
@@ -163,7 +164,7 @@ in {
           # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
           ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
 
-          ${pkgs.gnome3.gnome_session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
+          ${pkgs.gnome3.gnome-session}/bin/gnome-session ${optionalString cfg.debug "--debug"} &
           waitPID=$!
         '';
       };
@@ -171,7 +172,7 @@ in {
     services.xserver.updateDbusEnvironment = true;
 
     environment.variables.GIO_EXTRA_MODULES = [ "${lib.getLib pkgs.gnome3.dconf}/lib/gio/modules"
-                                                "${pkgs.gnome3.glib_networking.out}/lib/gio/modules"
+                                                "${pkgs.gnome3.glib-networking.out}/lib/gio/modules"
                                                 "${pkgs.gnome3.gvfs}/lib/gio/modules" ];
     environment.systemPackages = pkgs.gnome3.corePackages ++ cfg.sessionPath
       ++ (removePackagesByName pkgs.gnome3.optionalPackages config.environment.gnome3.excludePackages);
@@ -179,10 +180,9 @@ in {
     # Use the correct gnome3 packageSet
     networking.networkmanager.basePackages =
       { inherit (pkgs) networkmanager modemmanager wpa_supplicant;
-        inherit (pkgs.gnome3) networkmanager_openvpn networkmanager_vpnc
-                              networkmanager_openconnect networkmanager_fortisslvpn
-                              networkmanager_pptp networkmanager_iodine
-                              networkmanager_l2tp; };
+        inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc
+                              networkmanager-openconnect networkmanager-fortisslvpn
+                              networkmanager-iodine networkmanager-l2tp; };
 
     # Needed for themes and backgrounds
     environment.pathsToLink = [ "/share" ];
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
index fb907618d35b..2596ec4ad85c 100644
--- a/nixos/modules/services/x11/desktop-managers/lxqt.nix
+++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix
@@ -61,6 +61,8 @@ in
 
     environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.gvfs}/lib/gio/modules" ];
 
+    services.upower.enable = config.powerManagement.enable;
   };
 
+
 }
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index ab8a0a48b483..db83aaf3c19f 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -12,6 +12,17 @@ let
     in
       filter (x: !(builtins.elem (pkgName x) ysNames)) xs;
 
+  addToXDGDirs = p: ''
+    if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then
+      export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name}
+    fi
+
+    if [ -d "${p}/lib/girepository-1.0" ]; then
+      export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0
+      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib
+    fi
+  '';
+
   xcfg = config.services.xserver;
   cfg = xcfg.desktopManager.mate;
 
@@ -20,10 +31,14 @@ in
 {
   options = {
 
-    services.xserver.desktopManager.mate.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Enable the MATE desktop environment";
+    services.xserver.desktopManager.mate = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the MATE desktop environment";
+      };
+
+      debug = mkEnableOption "mate-session debug messages";
     };
 
     environment.mate.excludePackages = mkOption {
@@ -47,15 +62,34 @@ in
         # Find theme engines
         export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
 
-        export XDG_MENU_PREFIX=mate
+        export XDG_MENU_PREFIX=mate-
 
         # Find the mouse
         export XCURSOR_PATH=~/.icons:${config.system.path}/share/icons
 
+        # Let caja find extensions
+        export CAJA_EXTENSION_DIRS=$CAJA_EXTENSION_DIRS''${CAJA_EXTENSION_DIRS:+:}${config.system.path}/lib/caja/extensions-2.0
+
+        # Let caja extensions find gsettings schemas
+        ${concatMapStrings (p: ''
+          if [ -d "${p}/lib/caja/extensions-2.0" ]; then
+            ${addToXDGDirs p}
+          fi
+          '')
+          config.environment.systemPackages
+        }
+
+        # Let mate-panel find applets
+        export MATE_PANEL_APPLETS_DIR=$MATE_PANEL_APPLETS_DIR''${MATE_PANEL_APPLETS_DIR:+:}${config.system.path}/share/mate-panel/applets
+        export MATE_PANEL_EXTRA_MODULES=$MATE_PANEL_EXTRA_MODULES''${MATE_PANEL_EXTRA_MODULES:+:}${config.system.path}/lib/mate-panel/applets
+
+        # Add mate-control-center paths to some XDG variables because its schemas are needed by mate-settings-daemon, and mate-settings-daemon is a dependency for mate-control-center (that is, they are mutually recursive)
+        ${addToXDGDirs pkgs.mate.mate-control-center}
+
         # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
         ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
 
-        ${pkgs.mate.mate-session-manager}/bin/mate-session &
+        ${pkgs.mate.mate-session-manager}/bin/mate-session ${optionalString cfg.debug "--debug"} &
         waitPID=$!
       '';
     };
@@ -68,12 +102,14 @@ in
 
     services.dbus.packages = [
       pkgs.gnome3.dconf
-      pkgs.at_spi2_core
+      pkgs.at-spi2-core
     ];
 
     services.gnome3.gnome-keyring.enable = true;
     services.upower.enable = config.powerManagement.enable;
 
+    security.pam.services."mate-screensaver".unixAuth = true;
+
     environment.pathsToLink = [ "/share" ];
   };
 
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index bb4f4e868fea..91d091d7d7e2 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -25,8 +25,8 @@ in
         type = types.bool;
         default = true;
         description = ''
-          Enable support for Qt 4-based applications. Particularly, install the
-          Qt 4 version of the Breeze theme and a default backend for Phonon.
+          Enable support for Qt 4-based applications. Particularly, install a
+          default backend for Phonon.
         '';
       };
 
@@ -47,6 +47,18 @@ in
             ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1"
           ''}
 
+          if [ -f "$HOME/.config/kdeglobals" ]
+          then
+              # Remove extraneous font style names.
+              # See also: https://phabricator.kde.org/D9070
+              ${getBin pkgs.gnused}/bin/sed -i "$HOME/.config/kdeglobals" \
+                  -e '/^fixed=/ s/,Regular$//' \
+                  -e '/^font=/ s/,Regular$//' \
+                  -e '/^menuFont=/ s/,Regular$//' \
+                  -e '/^smallestReadableFont=/ s/,Regular$//' \
+                  -e '/^toolBarFont=/ s/,Regular$//'
+          fi
+
           exec "${getBin plasma5.plasma-workspace}/bin/startkde"
         '';
       };
@@ -54,6 +66,10 @@ in
       security.wrappers = {
         kcheckpass.source = "${lib.getBin plasma5.plasma-workspace}/lib/libexec/kcheckpass";
         "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit";
+        kwin_wayland = {
+          source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
+          capabilities = "cap_sys_nice+ep";
+        };
       };
 
       environment.systemPackages = with pkgs; with qt5; with libsForQt5; with plasma5; with kdeApplications;
@@ -138,15 +154,17 @@ in
           print-manager
 
           breeze-icons
-          pkgs.hicolor_icon_theme
+          pkgs.hicolor-icon-theme
 
           kde-gtk-config breeze-gtk
 
+          qtvirtualkeyboard
+
           libsForQt56.phonon-backend-gstreamer
           libsForQt5.phonon-backend-gstreamer
         ]
 
-        ++ lib.optionals cfg.enableQt4Support [ breeze-qt4 pkgs.phonon-backend-gstreamer ]
+        ++ lib.optionals cfg.enableQt4Support [ pkgs.phonon-backend-gstreamer ]
 
         # Optional hardware support features
         ++ lib.optional config.hardware.bluetooth.enable bluedevil
@@ -193,16 +211,6 @@ in
         theme = mkDefault "breeze";
       };
 
-      boot.plymouth = {
-        theme = mkDefault "breeze";
-        themePackages = mkDefault [
-          (pkgs.breeze-plymouth.override {
-            nixosBranding = true;
-            nixosVersion = config.system.nixosRelease;
-          })
-        ];
-      };
-
       security.pam.services.kde = { allowNullPassword = true; };
 
       # Doing these one by one seems silly, but we currently lack a better
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 9c42dc8781b9..7dcc600d2664 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -3,15 +3,11 @@
 with lib;
 
 let
-
-  xcfg = config.services.xserver;
-  cfg = xcfg.desktopManager.xfce;
-
+  cfg = config.services.xserver.desktopManager.xfce;
 in
 
 {
   options = {
-
     services.xserver.desktopManager.xfce = {
       enable = mkOption {
         type = types.bool;
@@ -54,81 +50,93 @@ in
         description = "Application used by XFCE to lock the screen.";
       };
     };
-
   };
 
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs.xfce // pkgs; [
+      # Get GTK+ themes and gtk-update-icon-cache
+      gtk2.out
+
+      # Supplies some abstract icons such as:
+      # utilities-terminal, accessories-text-editor
+      gnome3.defaultIconTheme
+
+      hicolor-icon-theme
+      tango-icon-theme
+      xfce4-icon-theme
+
+      desktop-file-utils
+      shared-mime-info
+
+      # Needed by Xfce's xinitrc script
+      # TODO: replace with command -v
+      which
+
+      exo
+      garcon
+      gtk-xfce-engine
+      gvfs
+      libxfce4ui
+      tumbler
+      xfconf
+
+      mousepad
+      ristretto
+      xfce4-appfinder
+      xfce4-screenshooter
+      xfce4-session
+      xfce4-settings
+      xfce4-terminal
+
+      (thunar.override { thunarPlugins = cfg.thunarPlugins; })
+      thunar-volman # TODO: drop
+    ] ++ (if config.hardware.pulseaudio.enable
+          then [ xfce4-mixer-pulse xfce4-volumed-pulse ]
+	  else [ xfce4-mixer xfce4-volumed ])
+      # TODO: NetworkManager doesn't belong here
+      ++ optionals config.networking.networkmanager.enable [ networkmanagerapplet ]
+      ++ optionals config.powerManagement.enable [ xfce4-power-manager ]
+      ++ optionals cfg.enableXfwm [ xfwm4 ]
+      ++ optionals (!cfg.noDesktop) [
+        xfce4-panel
+        xfce4-notifyd
+        xfdesktop
+      ];
+
+    environment.pathsToLink = [
+      "/share/xfce4"
+      "/share/themes"
+      "/share/mime"
+      "/share/desktop-directories"
+      "/share/gtksourceview-2.0"
+    ];
+
+    environment.variables = {
+      GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
+      GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
+    };
 
-  config = mkIf (xcfg.enable && cfg.enable) {
-
-    services.xserver.desktopManager.session = singleton
-      { name = "xfce";
-        bgSupport = true;
-        start =
-          ''
-            ${cfg.extraSessionCommands}
+    services.xserver.desktopManager.session = [{
+      name = "xfce";
+      bgSupport = true;
+      start = ''
+        ${cfg.extraSessionCommands}
 
-            # Set GTK_PATH so that GTK+ can find the theme engines.
-            export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
+        # Set GTK_PATH so that GTK+ can find the theme engines.
+        export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
 
-            # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
-            export GTK_DATA_PREFIX=${config.system.path}
+        # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
+        export GTK_DATA_PREFIX=${config.system.path}
 
-            ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} &
-            waitPID=$!
-          '';
-      };
+        ${pkgs.runtimeShell} ${pkgs.xfce.xinitrc} &
+        waitPID=$!
+      '';
+    }];
 
     services.xserver.updateDbusEnvironment = true;
 
-    environment.systemPackages =
-      [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache
-        pkgs.hicolor_icon_theme
-        pkgs.tango-icon-theme
-        pkgs.shared_mime_info
-        pkgs.which # Needed by the xfce's xinitrc script.
-        pkgs."${cfg.screenLock}"
-        pkgs.xfce.exo
-        pkgs.xfce.gtk_xfce_engine
-        pkgs.xfce.mousepad
-        pkgs.xfce.ristretto
-        pkgs.xfce.terminal
-       (pkgs.xfce.thunar.override { thunarPlugins = cfg.thunarPlugins; })
-        pkgs.xfce.xfce4icontheme
-        pkgs.xfce.xfce4session
-        pkgs.xfce.xfce4settings
-        pkgs.xfce.xfce4mixer
-        pkgs.xfce.xfce4volumed
-        pkgs.xfce.xfce4-screenshooter
-        pkgs.xfce.xfconf
-        # This supplies some "abstract" icons such as
-        # "utilities-terminal" and "accessories-text-editor".
-        pkgs.gnome3.defaultIconTheme
-        pkgs.desktop_file_utils
-        pkgs.xfce.libxfce4ui
-        pkgs.xfce.garcon
-        pkgs.xfce.thunar_volman
-        pkgs.xfce.gvfs
-        pkgs.xfce.xfce4_appfinder
-        pkgs.xfce.tumbler       # found via dbus
-      ]
-      ++ optional cfg.enableXfwm pkgs.xfce.xfwm4
-      ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager
-      ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet
-      ++ optionals (!cfg.noDesktop)
-         [ pkgs.xfce.xfce4panel
-           pkgs.xfce.xfdesktop
-	   pkgs.xfce.xfce4notifyd  # found via dbus
-         ];
-
-    environment.pathsToLink =
-      [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
-
-    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
-
     # Enable helpful DBus services.
     services.udisks2.enable = true;
     services.upower.enable = config.powerManagement.enable;
-
   };
-
 }
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 3fa482fb6722..43ed21c95fee 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -59,12 +59,6 @@ let
       # Now it should be safe to assume that the script was called with the
       # expected parameters.
 
-      ${optionalString cfg.displayManager.logToJournal ''
-        if [ -z "$_DID_SYSTEMD_CAT" ]; then
-          _DID_SYSTEMD_CAT=1 exec ${config.systemd.package}/bin/systemd-cat -t xsession -- "$0" "$@"
-        fi
-      ''}
-
       . /etc/profile
       cd "$HOME"
 
@@ -72,16 +66,23 @@ let
       sessionType="$1"
       if [ "$sessionType" = default ]; then sessionType=""; fi
 
-      ${optionalString (!cfg.displayManager.job.logsXsession && !cfg.displayManager.logToJournal) ''
-        exec > ~/.xsession-errors 2>&1
-      ''}
-
       ${optionalString cfg.startDbusSession ''
         if test -z "$DBUS_SESSION_BUS_ADDRESS"; then
           exec ${pkgs.dbus.dbus-launch} --exit-with-session "$0" "$sessionType"
         fi
       ''}
 
+      ${optionalString cfg.displayManager.job.logToJournal ''
+        if [ -z "$_DID_SYSTEMD_CAT" ]; then
+          export _DID_SYSTEMD_CAT=1
+          exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$sessionType"
+        fi
+      ''}
+
+      ${optionalString cfg.displayManager.job.logToFile ''
+        exec &> >(tee ~/.xsession-errors)
+      ''}
+
       # Start PulseAudio if enabled.
       ${optionalString (config.hardware.pulseaudio.enable) ''
         ${optionalString (!config.hardware.pulseaudio.systemWide)
@@ -306,26 +307,24 @@ in
           description = "Additional environment variables needed by the display manager.";
         };
 
-        logsXsession = mkOption {
+        logToFile = mkOption {
           type = types.bool;
           default = false;
           description = ''
-            Whether the display manager redirects the
-            output of the session script to
-            <filename>~/.xsession-errors</filename>.
+            Whether the display manager redirects the output of the
+            session script to <filename>~/.xsession-errors</filename>.
           '';
         };
 
-      };
+        logToJournal = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Whether the display manager redirects the output of the
+            session script to the systemd journal.
+          '';
+        };
 
-      logToJournal = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          By default, the stdout/stderr of sessions is written
-          to <filename>~/.xsession-errors</filename>. When this option
-          is enabled, it will instead be written to the journal.
-        '';
       };
 
     };
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index e83f26516f5f..70fc7388c2ac 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -122,11 +122,8 @@ in
       "rc-local.service"
       "systemd-machined.service"
       "systemd-user-sessions.service"
-      "getty@tty1.service"
     ];
 
-    systemd.services."getty@tty1".enable = false;
-    systemd.services.display-manager.conflicts = [ "getty@tty1.service" ];
     systemd.services.display-manager.serviceConfig = {
       # Restart = "always"; - already defined in xserver.nix
       KillMode = "mixed";
@@ -136,7 +133,7 @@ in
       StandardError = "inherit";
     };
 
-    systemd.services.display-manager.path = [ pkgs.gnome3.gnome_session ];
+    systemd.services.display-manager.path = [ pkgs.gnome3.gnome-session ];
 
     services.dbus.packages = [ gdm ];
 
@@ -196,7 +193,7 @@ in
         auth     required       pam_env.so envfile=${config.system.build.pamEnvironment}
 
         auth     required       pam_succeed_if.so uid >= 1000 quiet
-        auth     optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so
+        auth     optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so
         auth     ${if config.security.pam.enableEcryptfs then "required" else "sufficient"} pam_unix.so nullok likeauth
         ${optionalString config.security.pam.enableEcryptfs
           "auth required ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -216,7 +213,7 @@ in
           "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
         session  required       pam_loginuid.so
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
-        session  optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so auto_start
+        session  optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
       '';
 
       gdm-password.text = ''
@@ -224,7 +221,7 @@ in
         auth     required       pam_env.so envfile=${config.system.build.pamEnvironment}
 
         auth     required       pam_succeed_if.so uid >= 1000 quiet
-        auth     optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so
+        auth     optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so
         auth     ${if config.security.pam.enableEcryptfs then "required" else "sufficient"} pam_unix.so nullok likeauth
         ${optionalString config.security.pam.enableEcryptfs
           "auth required ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
@@ -243,7 +240,7 @@ in
           "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
         session  required       pam_loginuid.so
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
-        session  optional       ${pkgs.gnome3.gnome_keyring}/lib/security/pam_gnome_keyring.so auto_start
+        session  optional       ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
       '';
 
       gdm-autologin.text = ''
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
index 1d5dcb2c7cbe..2a71d2338607 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/gtk.nix
@@ -45,6 +45,8 @@ let
     theme-name = ${cfg.theme.name}
     icon-theme-name = ${cfg.iconTheme.name}
     background = ${ldmcfg.background}
+    ${optionalString (cfg.clock-format != null) "clock-format = ${cfg.clock-format}"}
+    ${optionalString (cfg.indicators != null) "indicators = ${concatStringsSep ";" cfg.indicators}"}
     ${cfg.extraConfig}
     '';
 
@@ -66,8 +68,8 @@ in
 
         package = mkOption {
           type = types.package;
-          default = pkgs.gnome3.gnome_themes_standard;
-          defaultText = "pkgs.gnome3.gnome_themes_standard";
+          default = pkgs.gnome3.gnome-themes-standard;
+          defaultText = "pkgs.gnome3.gnome-themes-standard";
           description = ''
             The package path that contains the theme given in the name option.
           '';
@@ -104,6 +106,35 @@ in
 
       };
 
+      clock-format = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "%F";
+        description = ''
+          Clock format string (as expected by strftime, e.g. "%H:%M")
+          to use with the lightdm gtk greeter panel.
+
+          If set to null the default clock format is used.
+        '';
+      };
+
+      indicators = mkOption {
+        type = types.nullOr (types.listOf types.str);
+        default = null;
+        example = [ "~host" "~spacer" "~clock" "~spacer" "~session" "~language" "~a11y" "~power" ];
+        description = ''
+          List of allowed indicator modules to use for the lightdm gtk
+          greeter panel.
+
+          Built-in indicators include "~a11y", "~language", "~session",
+          "~power", "~clock", "~host", "~spacer". Unity indicators can be
+          represented by short name (e.g. "sound", "power"), service file name,
+          or absolute path.
+
+          If set to null the default indicators are used.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 1733f2fd39b2..9d30155a7234 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -9,6 +9,10 @@ let
   xEnv = config.systemd.services."display-manager".environment;
   cfg = dmcfg.lightdm;
 
+  dmDefault = xcfg.desktopManager.default;
+  wmDefault = xcfg.windowManager.default;
+  hasDefaultUserSession = dmDefault != "none" || wmDefault != "none";
+
   inherit (pkgs) stdenv lightdm writeScript writeText;
 
   # lightdm runs with clearenv(), but we need a few things in the enviornment for X to startup
@@ -54,14 +58,13 @@ let
         autologin-user-timeout = ${toString cfg.autoLogin.timeout}
         autologin-session = ${defaultSessionName}
       ''}
+      ${optionalString hasDefaultUserSession ''
+        user-session=${defaultSessionName}
+      ''}
       ${cfg.extraSeatDefaults}
     '';
 
-  defaultSessionName =
-    let
-      dm = xcfg.desktopManager.default;
-      wm = xcfg.windowManager.default;
-    in dm + optionalString (wm != "none") ("+" + wm);
+  defaultSessionName = dmDefault + optionalString (wmDefault != "none") ("+" + wmDefault);
 in
 {
   # Note: the order in which lightdm greeter modules are imported
@@ -179,6 +182,14 @@ in
           default session: ${defaultSessionName} is not valid.
         '';
       }
+      { assertion = hasDefaultUserSession -> elem defaultSessionName dmcfg.session.names;
+        message = ''
+          services.xserver.desktopManager.default and
+          services.xserver.windowMananger.default are not set to valid
+          values. The current default session: ${defaultSessionName}
+          is not valid.
+        '';
+      }
       { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
         message = ''
           LightDM can only run without greeter if automatic login is enabled and the timeout for it
@@ -190,7 +201,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
       execCmd = ''
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index facaea131ae5..2d4cb8aa20a5 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -205,7 +205,7 @@ in
     services.xserver.displayManager.slim.enable = false;
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       environment = {
         # Load themes from system environment
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 0c4dd1973b53..f645a5c2f078 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -14,7 +14,7 @@ let
       default_xserver ${dmcfg.xserverBin}
       xserver_arguments ${toString dmcfg.xserverArgs}
       sessiondir ${dmcfg.session.desktops}
-      login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session"
+      login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.script} "%session"
       halt_cmd ${config.systemd.package}/sbin/shutdown -h now
       reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
       logfile /dev/stderr
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index 8f5ce3dccc6a..b46ede550c16 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -220,7 +220,7 @@ in
     '';
 
     services.xserver.displayManager.job = {
-      logsXsession = true;
+      logToFile = true;
 
       execCmd = ''
         ${optionalString (cfg.pulseaudio)
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 44555cb6e2a9..d0a87f183b6f 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -170,7 +170,7 @@ in {
 
       disableWhileTyping = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
         description =
           ''
             Disable input method while typing.
@@ -198,6 +198,13 @@ in {
 
     environment.systemPackages = [ pkgs.xorg.xf86inputlibinput ];
 
+    environment.etc = [
+      (let cfgPath = "X11/xorg.conf.d/40-libinput.conf"; in {
+        source = pkgs.xorg.xf86inputlibinput.out + "/share/" + cfgPath;
+        target = cfgPath;
+      })
+    ];
+
     services.udev.packages = [ pkgs.libinput ];
 
     services.xserver.config =
diff --git a/nixos/modules/services/x11/window-managers/2bwm.nix b/nixos/modules/services/x11/window-managers/2bwm.nix
index e3f5ec7dbe67..fdbdf35b0f5a 100644
--- a/nixos/modules/services/x11/window-managers/2bwm.nix
+++ b/nixos/modules/services/x11/window-managers/2bwm.nix
@@ -25,12 +25,12 @@ in
       { name = "2bwm";
         start =
           ''
-            ${pkgs."2bwm"}/bin/2bwm &
+            ${pkgs._2bwm}/bin/2bwm &
             waitPID=$!
           '';
       };
 
-    environment.systemPackages = [ pkgs."2bwm" ];
+    environment.systemPackages = [ pkgs._2bwm ];
 
   };
 
diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix
index eb97449c6bd9..71eb02ec5954 100644
--- a/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixos/modules/services/x11/window-managers/awesome.nix
@@ -6,7 +6,11 @@ let
 
   cfg = config.services.xserver.windowManager.awesome;
   awesome = cfg.package;
-  inherit (pkgs.luaPackages) getLuaPath getLuaCPath;
+  getLuaPath = lib : dir : "${lib}/${dir}/lua/${pkgs.luaPackages.lua.luaversion}";
+  makeSearchPath = lib.concatMapStrings (path:
+    " --search " + (getLuaPath path "share") +
+    " --search " + (getLuaPath path "lib")
+  );
 in
 
 {
@@ -46,10 +50,7 @@ in
       { name = "awesome";
         start =
           ''
-            export LUA_CPATH="${lib.concatStringsSep ";" (map getLuaCPath cfg.luaModules)}"
-            export LUA_PATH="${lib.concatStringsSep ";" (map getLuaPath cfg.luaModules)}"
-
-            ${awesome}/bin/awesome &
+            ${awesome}/bin/awesome ${makeSearchPath cfg.luaModules} &
             waitPID=$!
           '';
       };
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 25ba95fccd75..e617e55a7a57 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -12,6 +12,7 @@ in
     ./afterstep.nix
     ./bspwm.nix
     ./dwm.nix
+    ./evilwm.nix
     ./exwm.nix
     ./fluxbox.nix
     ./fvwm.nix
@@ -61,9 +62,7 @@ in
         example = "wmii";
         description = "Default window manager loaded if none have been chosen.";
         apply = defaultWM:
-          if defaultWM == "none" && cfg.session != []  then
-            (head cfg.session).name
-          else if any (w: w.name == defaultWM) cfg.session then
+          if any (w: w.name == defaultWM) cfg.session then
             defaultWM
           else
             throw "Default window manager (${defaultWM}) not found.";
diff --git a/nixos/modules/services/x11/window-managers/evilwm.nix b/nixos/modules/services/x11/window-managers/evilwm.nix
new file mode 100644
index 000000000000..6e19e3572c79
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/evilwm.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.windowManager.evilwm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.evilwm.enable = mkEnableOption "evilwm";
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "evilwm";
+      start = ''
+	${pkgs.evilwm}/bin/evilwm &
+	waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ pkgs.evilwm ];
+  };
+}
diff --git a/nixos/modules/services/x11/xautolock.nix b/nixos/modules/services/x11/xautolock.nix
index 28fc92024bcb..a614559970e9 100644
--- a/nixos/modules/services/x11/xautolock.nix
+++ b/nixos/modules/services/x11/xautolock.nix
@@ -26,9 +26,9 @@ in
         };
 
         locker = mkOption {
-          default = "xlock"; # default according to `man xautolock`
-          example = "i3lock -i /path/to/img";
-          type = types.string;
+          default = "${pkgs.xlockmore}/bin/xlock"; # default according to `man xautolock`
+          example = "${pkgs.i3lock}/bin/i3lock -i /path/to/img";
+          type = types.str;
 
           description = ''
             The script to use when automatically locking the computer.
@@ -37,8 +37,8 @@ in
 
         nowlocker = mkOption {
           default = null;
-          example = "i3lock -i /path/to/img";
-          type = types.nullOr types.string;
+          example = "${pkgs.i3lock}/bin/i3lock -i /path/to/img";
+          type = types.nullOr types.str;
 
           description = ''
             The script to use when manually locking the computer with <command>xautolock -locknow</command>.
@@ -56,10 +56,8 @@ in
 
         notifier = mkOption {
           default = null;
-          example = literalExample ''
-            "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\""
-          '';
-          type = types.nullOr types.string;
+          example = "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\"";
+          type = types.nullOr types.str;
 
           description = ''
             Notification script to be used to warn about the pending autolock.
@@ -68,8 +66,8 @@ in
 
         killer = mkOption {
           default = null; # default according to `man xautolock` is none
-          example = "systemctl suspend";
-          type = types.nullOr types.string;
+          example = "${pkgs.systemd}/bin/systemctl suspend";
+          type = types.nullOr types.str;
 
           description = ''
             The script to use when nothing has happend for as long as <option>killtime</option>
@@ -131,6 +129,12 @@ in
           assertion = cfg.killer != null -> cfg.killtime >= 10;
           message = "killtime has to be at least 10 minutes according to `man xautolock`";
         }
-      ];
+      ] ++ (lib.flip map [ "locker" "notifier" "nowlocker" "killer" ]
+        (option:
+        {
+          assertion = cfg."${option}" != null -> builtins.substring 0 1 cfg."${option}" == "/";
+          message = "Please specify a canonical path for `services.xserver.xautolock.${option}`";
+        })
+      );
     };
   }
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 7d544e153e9a..5f0a0f278452 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -161,15 +161,6 @@ in
         '';
       };
 
-      plainX = mkOption {
-        type = types.bool;
-        default = false;
-        description = ''
-          Whether the X11 session can be plain (without DM/WM) and
-          the Xsession script will be used as fallback or not.
-        '';
-      };
-
       autorun = mkOption {
         type = types.bool;
         default = true;
@@ -548,7 +539,7 @@ in
           knownVideoDrivers;
       in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
 
-    nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
+    nixpkgs.config = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { xorg.abiCompat = "1.18"; };
 
     assertions = [
       { assertion = config.security.polkit.enable;
@@ -561,11 +552,6 @@ in
                 + "${toString (length primaryHeads)} heads set to primary: "
                 + concatMapStringsSep ", " (x: x.output) primaryHeads;
       })
-      { assertion = cfg.desktopManager.default == "none" && cfg.windowManager.default == "none" -> cfg.plainX;
-        message = "Either the desktop manager or the window manager shouldn't be `none`! "
-                + "To explicitly allow this, you can also set `services.xserver.plainX` to `true`. "
-                + "The `default` value looks for enabled WMs/DMs and select the first one.";
-      }
     ];
 
     environment.etc =
@@ -578,6 +564,22 @@ in
             target = "X11/xkb";
           }
         ])
+      # localectl looks into 00-keyboard.conf
+      ++ [
+        {
+          text = ''
+            Section "InputClass"
+              Identifier "Keyboard catchall"
+              MatchIsKeyboard "on"
+              Option "XkbModel" "${cfg.xkbModel}"
+              Option "XkbLayout" "${cfg.layout}"
+              Option "XkbOptions" "${cfg.xkbOptions}"
+              Option "XkbVariant" "${cfg.xkbVariant}"
+            EndSection
+          '';
+          target = "X11/xorg.conf.d/00-keyboard.conf";
+        }
+      ]
       # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5
       ++ (let cfgPath = "/X11/xorg.conf.d/10-evdev.conf"; in
         [{
@@ -624,9 +626,7 @@ in
 
         environment =
           {
-            XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
-            LD_LIBRARY_PATH = concatStringsSep ":" (
-              [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ]
+            LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ]
               ++ concatLists (catAttrs "libPath" cfg.drivers));
           } // cfg.displayManager.job.environment;
 
@@ -697,16 +697,6 @@ in
           ${cfg.monitorSection}
         EndSection
 
-        Section "InputClass"
-          Identifier "Keyboard catchall"
-          MatchIsKeyboard "on"
-          Option "XkbRules" "base"
-          Option "XkbModel" "${cfg.xkbModel}"
-          Option "XkbLayout" "${cfg.layout}"
-          Option "XkbOptions" "${cfg.xkbOptions}"
-          Option "XkbVariant" "${cfg.xkbVariant}"
-        EndSection
-
         # Additional "InputClass" sections
         ${flip concatMapStrings cfg.inputClassSections (inputClassSection: ''
         Section "InputClass"
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index c2ac731d433d..c563614caaaf 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -61,7 +61,7 @@ in
       apply = set: {
         script =
           ''
-            #! ${pkgs.stdenv.shell}
+            #! ${pkgs.runtimeShell}
 
             systemConfig=@out@
 
@@ -117,14 +117,7 @@ in
 
   config = {
 
-    system.activationScripts.stdio =
-      ''
-        # Needed by some programs.
-        ln -sfn /proc/self/fd /dev/fd
-        ln -sfn /proc/self/fd/0 /dev/stdin
-        ln -sfn /proc/self/fd/1 /dev/stdout
-        ln -sfn /proc/self/fd/2 /dev/stderr
-      '';
+    system.activationScripts.stdio = ""; # obsolete
 
     system.activationScripts.var =
       ''
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 29cc60b00324..87a4ab2a586d 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -16,6 +16,10 @@ my $reloadListFile = "/run/systemd/reload-list";
 
 my $action = shift @ARGV;
 
+if ("@localeArchive@" ne "") {
+    $ENV{LOCALE_ARCHIVE} = "@localeArchive@";
+}
+
 if (!defined $action || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) {
     print STDERR <<EOF;
 Usage: $0 [switch|boot|test]
@@ -65,7 +69,8 @@ $SIG{PIPE} = "IGNORE";
 sub getActiveUnits {
     # FIXME: use D-Bus or whatever to query this, since parsing the
     # output of list-units is likely to break.
-    my $lines = `LANG= systemctl list-units --full --no-legend`;
+    # Use current version of systemctl binary before daemon is reexeced.
+    my $lines = `LANG= /run/current-system/sw/bin/systemctl list-units --full --no-legend`;
     my $res = {};
     foreach my $line (split '\n', $lines) {
         chomp $line;
@@ -262,7 +267,8 @@ while (my ($unit, $state) = each %{$activePrev}) {
 
 sub pathToUnitName {
     my ($path) = @_;
-    open my $cmd, "-|", "@systemd@/bin/systemd-escape", "--suffix=mount", "-p", $path
+    # Use current version of systemctl binary before daemon is reexeced.
+    open my $cmd, "-|", "/run/current-system/sw/bin/systemd-escape", "--suffix=mount", "-p", $path
         or die "Unable to escape $path!\n";
     my $escaped = join "", <$cmd>;
     chomp $escaped;
@@ -364,7 +370,8 @@ syslog(LOG_NOTICE, "switching to system configuration $out");
 if (scalar (keys %unitsToStop) > 0) {
     print STDERR "stopping the following units: ", join(", ", @unitsToStopFiltered), "\n"
         if scalar @unitsToStopFiltered;
-    system("systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
+    # Use current version of systemctl binary before daemon is reexeced.
+    system("/run/current-system/sw/bin/systemctl", "stop", "--", sort(keys %unitsToStop)); # FIXME: ignore errors?
 }
 
 print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys %unitsToSkip)), "\n"
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 67cb2264e3f3..091a2e412eed 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -26,11 +26,12 @@ let
      cloner false config.nesting.children
   ++ cloner true config.nesting.clone;
 
-
   systemBuilder =
     let
       kernelPath = "${config.boot.kernelPackages.kernel}/" +
         "${config.system.boot.loader.kernelFile}";
+      initrdPath = "${config.system.build.initialRamdisk}/" +
+        "${config.system.boot.loader.initrdFile}";
     in ''
       mkdir $out
 
@@ -51,7 +52,7 @@ let
 
         echo -n "$kernelParams" > $out/kernel-params
 
-        ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
+        ln -s ${initrdPath} $out/initrd
 
         ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
 
@@ -83,6 +84,7 @@ let
       done
 
       mkdir $out/bin
+      export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
       substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
       chmod +x $out/bin/switch-to-configuration
 
@@ -106,7 +108,7 @@ let
     if [] == failed then pkgs.stdenvNoCC.mkDerivation {
       name = let hn = config.networking.hostName;
                  nn = if (hn != "") then hn else "unnamed";
-          in "nixos-system-${nn}-${config.system.nixosLabel}";
+          in "nixos-system-${nn}-${config.system.nixos.label}";
       preferLocalBuild = true;
       allowSubstitutes = false;
       buildCommand = systemBuilder;
@@ -120,7 +122,7 @@ let
         config.system.build.installBootLoader
         or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
       activationScript = config.system.activationScripts.script;
-      nixosLabel = config.system.nixosLabel;
+      nixosLabel = config.system.nixos.label;
 
       configurationName = config.boot.loader.grub.configurationName;
 
@@ -179,6 +181,15 @@ in
       '';
     };
 
+    system.boot.loader.initrdFile = mkOption {
+      internal = true;
+      default = "initrd";
+      type = types.str;
+      description = ''
+        Name of the initrd file to be passed to the bootloader.
+      '';
+    };
+
     system.copySystemConfiguration = mkOption {
       type = types.bool;
       default = false;
diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix
new file mode 100644
index 000000000000..15e84dc021e2
--- /dev/null
+++ b/nixos/modules/system/boot/binfmt.nix
@@ -0,0 +1,139 @@
+{ config, lib, ... }:
+let
+  inherit (lib) mkOption types optionalString;
+
+  cfg = config.boot.binfmtMiscRegistrations;
+
+  makeBinfmtLine = name: { recognitionType, offset, magicOrExtension
+                         , mask, preserveArgvZero, openBinary
+                         , matchCredentials, fixBinary, ...
+                         }: let
+    type = if recognitionType == "magic" then "M" else "E";
+    offset' = toString offset;
+    mask' = toString mask;
+    interpreter = "/run/binfmt/${name}";
+    flags = if !(matchCredentials -> openBinary)
+              then throw "boot.binfmtMiscRegistrations.${name}: you can't specify openBinary = false when matchCredentials = true."
+            else optionalString preserveArgvZero "P" +
+                 optionalString (openBinary && !matchCredentials) "O" +
+                 optionalString matchCredentials "C" +
+                 optionalString fixBinary "F";
+  in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
+
+  binfmtFile = builtins.toFile "binfmt_nixos.conf"
+    (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine cfg));
+
+  activationSnippet = name: { interpreter, ... }:
+    "ln -sf ${interpreter} /run/binfmt/${name}";
+  activationScript = ''
+    mkdir -p -m 0755 /run/binfmt
+    ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet cfg)}
+  '';
+in {
+  options = {
+    boot.binfmtMiscRegistrations = mkOption {
+      default = {};
+
+      description = ''
+        Extra binary formats to register with the kernel.
+        See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details.
+      '';
+
+      type = types.attrsOf (types.submodule ({ config, ... }: {
+        options = {
+          recognitionType = mkOption {
+            default = "magic";
+            description = "Whether to recognize executables by magic number or extension.";
+            type = types.enum [ "magic" "extension" ];
+          };
+
+          offset = mkOption {
+            default = null;
+            description = "The byte offset of the magic number used for recognition.";
+            type = types.nullOr types.int;
+          };
+
+          magicOrExtension = mkOption {
+            description = "The magic number or extension to match on.";
+            type = types.str;
+          };
+
+          mask = mkOption {
+            default = null;
+            description =
+              "A mask to be ANDed with the byte sequence of the file before matching";
+            type = types.nullOr types.str;
+          };
+
+          interpreter = mkOption {
+            description = ''
+              The interpreter to invoke to run the program.
+
+              Note that the actual registration will point to
+              /run/binfmt/''${name}, so the kernel interpreter length
+              limit doesn't apply.
+            '';
+            type = types.path;
+          };
+
+          preserveArgvZero = mkOption {
+            default = false;
+            description = ''
+              Whether to pass the original argv[0] to the interpreter.
+
+              See the description of the 'P' flag in the kernel docs
+              for more details;
+            '';
+            type = types.bool;
+          };
+
+          openBinary = mkOption {
+            default = config.matchCredentials;
+            description = ''
+              Whether to pass the binary to the interpreter as an open
+              file descriptor, instead of a path.
+            '';
+            type = types.bool;
+          };
+
+          matchCredentials = mkOption {
+            default = false;
+            description = ''
+              Whether to launch with the credentials and security
+              token of the binary, not the interpreter (e.g. setuid
+              bit).
+
+              See the description of the 'C' flag in the kernel docs
+              for more details.
+
+              Implies/requires openBinary = true.
+            '';
+            type = types.bool;
+          };
+
+          fixBinary = mkOption {
+            default = false;
+            description = ''
+              Whether to open the interpreter file as soon as the
+              registration is loaded, rather than waiting for a
+              relevant file to be invoked.
+
+              See the description of the 'F' flag in the kernel docs
+              for more details.
+            '';
+            type = types.bool;
+          };
+        };
+      }));
+    };
+  };
+
+  config = lib.mkIf (cfg != {}) {
+    environment.etc."binfmt.d/nixos.conf".source = binfmtFile;
+    system.activationScripts.binfmt = activationScript;
+    systemd.additionalUpstreamSystemUnits =
+      [ "proc-sys-fs-binfmt_misc.automount"
+        "proc-sys-fs-binfmt_misc.mount"
+      ];
+  };
+}
diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix
new file mode 100644
index 000000000000..1e6f9e442b67
--- /dev/null
+++ b/nixos/modules/system/boot/grow-partition.nix
@@ -0,0 +1,50 @@
+# This module automatically grows the root partition.
+# This allows an instance to be created with a bigger root filesystem
+# than provided by the machine image.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options = {
+    boot.growPartition = mkEnableOption "grow the root partition on boot";
+  };
+
+  config = mkIf config.boot.growPartition {
+
+    boot.initrd.extraUtilsCommands = ''
+      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
+      copy_bin_and_libs ${pkgs.gnused}/bin/sed
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
+      copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
+
+      substitute "${pkgs.cloud-utils}/bin/.growpart-wrapped" "$out/bin/growpart" \
+        --replace "${pkgs.bash}/bin/sh" "/bin/sh" \
+        --replace "awk" "gawk" \
+        --replace "sed" "gnused"
+
+      ln -s sed $out/bin/gnused
+    '';
+
+    boot.initrd.postDeviceCommands = ''
+      rootDevice="${config.fileSystems."/".device}"
+      if [ -e "$rootDevice" ]; then
+        rootDevice="$(readlink -f "$rootDevice")"
+        parentDevice="$rootDevice"
+        while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do
+          parentDevice="''${parentDevice%[0-9]}";
+        done
+        partNum="''${rootDevice#''${parentDevice}}"
+        if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then
+          parentDevice="''${parentDevice%p}"
+        fi
+        TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum"
+        udevadm settle
+      fi
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix
index 6e226c190609..33862b0965cc 100644
--- a/nixos/modules/system/boot/initrd-network.nix
+++ b/nixos/modules/system/boot/initrd-network.nix
@@ -23,6 +23,8 @@ let
       fi
     '';
 
+  udhcpcArgs = toString cfg.udhcpc.extraArgs;
+
 in
 
 {
@@ -40,6 +42,20 @@ in
         kernel documentation</link>.  Otherwise, if
         <option>networking.useDHCP</option> is enabled, an IP address
         is acquired using DHCP.
+
+        You should add the module(s) required for your network card to
+        boot.initrd.availableKernelModules. lspci -v -s &lt;ethernet controller&gt;
+        will tell you which.
+      '';
+    };
+
+    boot.initrd.network.udhcpc.extraArgs = mkOption {
+      default = [];
+      type = types.listOf types.str;
+      description = ''
+        Additional command-line arguments passed verbatim to udhcpc if
+        <option>boot.initrd.network.enable</option> and <option>networking.useDHCP</option>
+        are enabled.
       '';
     };
 
@@ -87,7 +103,7 @@ in
 
           # Acquire a DHCP lease.
           echo "acquiring IP address via DHCP..."
-          udhcpc --quit --now --script ${udhcpcScript} && hasNetwork=1
+          udhcpc --quit --now --script ${udhcpcScript} ${udhcpcArgs} && hasNetwork=1
         fi
       ''
 
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index d78775c27582..8b3dc2d90eb3 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -89,9 +89,6 @@ in
 
   config = mkIf (config.boot.initrd.network.enable && cfg.enable) {
     assertions = [
-      { assertion = cfg.hostRSAKey != null || cfg.hostDSSKey != null || cfg.hostECDSAKey != null;
-        message = "You should specify at least one host key for initrd SSH";
-      }
       { assertion = cfg.authorizedKeys != [];
         message = "You should specify at least one authorized key for initrd SSH";
       }
@@ -121,7 +118,7 @@ in
         echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
       '') cfg.authorizedKeys)}
 
-      dropbear -s -j -k -E -m -p ${toString cfg.port}
+      dropbear -s -j -k -E -p ${toString cfg.port} ${optionalString (cfg.hostRSAKey == null && cfg.hostDSSKey == null && cfg.hostECDSAKey == null) "-R"}
     '';
 
     boot.initrd.secrets =
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 4db9631743e3..8ea05ed14687 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -5,7 +5,7 @@ with lib;
 let
 
   inherit (config.boot) kernelPatches;
-
+  inherit (config.boot.kernel) features;
   inherit (config.boot.kernelPackages) kernel;
 
   kernelModulesConf = pkgs.writeText "nixos.conf"
@@ -21,11 +21,25 @@ in
 
   options = {
 
+    boot.kernel.features = mkOption {
+      default = {};
+      example = literalExample "{ debug = true; }";
+      internal = true;
+      description = ''
+        This option allows to enable or disable certain kernel features.
+        It's not API, because it's about kernel feature sets, that
+        make sense for specific use cases. Mostly along with programs,
+        which would have separate nixos options.
+        `grep features pkgs/os-specific/linux/kernel/common-config.nix`
+      '';
+    };
+
     boot.kernelPackages = mkOption {
       default = pkgs.linuxPackages;
       apply = kernelPackages: kernelPackages.extend (self: super: {
         kernel = super.kernel.override {
           kernelPatches = super.kernel.kernelPatches ++ kernelPatches;
+          features = lib.recursiveUpdate super.kernel.features features;
         };
       });
       # We don't want to evaluate all of linuxPackages for the manual
@@ -63,8 +77,8 @@ in
       type = types.int;
       default = 4;
       description = ''
-        The kernel console log level.  Log messages with a priority
-        numerically less than this will not appear on the console.
+        The kernel console <literal>loglevel</literal>. All Kernel Messages with a log level smaller
+        than this setting will be printed to the console.
       '';
     };
 
@@ -170,7 +184,7 @@ in
       [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
       optionals config.boot.vesa [ "vga=0x317" ];
 
-    boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
+    boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
 
     boot.kernelModules = [ "loop" "atkbd" ];
 
@@ -197,7 +211,7 @@ in
         "mmc_block"
 
         # Support USB keyboards, in case the boot fails and we only have
-        # a USB keyboard.
+        # a USB keyboard, or for LUKS passphrase prompt.
         "uhci_hcd"
         "ehci_hcd"
         "ehci_pci"
@@ -207,11 +221,13 @@ in
         "xhci_pci"
         "usbhid"
         "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
+        "hid_logitech_hidpp" "hid_logitech_dj"
 
-        # Misc. keyboard stuff.
+      ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+        # Misc. x86 keyboard stuff.
         "pcips2" "atkbd" "i8042"
 
-        # Needed by the stage 2 init script.
+        # x86 RTC needed by the stage 2 init script.
         "rtc_cmos"
       ];
 
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
index b7821f9509f1..3fc1af28f628 100644
--- a/nixos/modules/system/boot/kexec.nix
+++ b/nixos/modules/system/boot/kexec.nix
@@ -1,21 +1,22 @@
-{ config, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 {
-  environment.systemPackages = [ pkgs.kexectools ];
+  config = lib.mkIf (pkgs.kexectools.meta.available) {
+    environment.systemPackages = [ pkgs.kexectools ];
 
-  systemd.services."prepare-kexec" =
-    { description = "Preparation for kexec";
-      wantedBy = [ "kexec.target" ];
-      before = [ "systemd-kexec.service" ];
-      unitConfig.DefaultDependencies = false;
-      serviceConfig.Type = "oneshot";
-      path = [ pkgs.kexectools ];
-      script =
-        ''
-          p=$(readlink -f /nix/var/nix/profiles/system)
-          if ! [ -d $p ]; then exit 1; fi
-          exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
-        '';
-    };
-
-}
\ No newline at end of file
+    systemd.services."prepare-kexec" =
+      { description = "Preparation for kexec";
+        wantedBy = [ "kexec.target" ];
+        before = [ "systemd-kexec.service" ];
+        unitConfig.DefaultDependencies = false;
+        serviceConfig.Type = "oneshot";
+        path = [ pkgs.kexectools ];
+        script =
+          ''
+            p=$(readlink -f /nix/var/nix/profiles/system)
+            if ! [ -d $p ]; then exit 1; fi
+            exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+          '';
+      };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 9056121fa7d1..e2cff1c1bd94 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -40,7 +40,7 @@ let
     { splashImage = f cfg.splashImage;
       grub = f grub;
       grubTarget = f (grub.grubTarget or "");
-      shell = "${pkgs.stdenv.shell}";
+      shell = "${pkgs.runtimeShell}";
       fullName = (builtins.parseDrvName realGrub.name).name;
       fullVersion = (builtins.parseDrvName realGrub.name).version;
       grubEfi = f grubEfi;
@@ -110,7 +110,7 @@ in
 
       device = mkOption {
         default = "";
-        example = "/dev/hda";
+        example = "/dev/disk/by-id/wwn-0x500001234567890a";
         type = types.str;
         description = ''
           The device on which the GRUB boot loader will be installed.
@@ -123,7 +123,7 @@ in
 
       devices = mkOption {
         default = [];
-        example = [ "/dev/hda" ];
+        example = [ "/dev/disk/by-id/wwn-0x500001234567890a" ];
         type = types.listOf types.str;
         description = ''
           The devices on which the boot loader, GRUB, will be
@@ -135,8 +135,8 @@ in
       mirroredBoots = mkOption {
         default = [ ];
         example = [
-          { path = "/boot1"; devices = [ "/dev/sda" ]; }
-          { path = "/boot2"; devices = [ "/dev/sdb" ]; }
+          { path = "/boot1"; devices = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; }
+          { path = "/boot2"; devices = [ "/dev/disk/by-id/wwn-0x500009876543210a" ]; }
         ];
         description = ''
           Mirror the boot configuration to multiple partitions and install grub
@@ -178,7 +178,7 @@ in
 
             devices = mkOption {
               default = [ ];
-              example = [ "/dev/sda" "/dev/sdb" ];
+              example = [ "/dev/disk/by-id/wwn-0x500001234567890a" "/dev/disk/by-id/wwn-0x500009876543210a" ];
               type = types.listOf types.str;
               description = ''
                 The path to the devices which will have the GRUB MBR written.
@@ -536,9 +536,9 @@ in
             btrfsprogs = pkgs.btrfs-progs;
           };
         in pkgs.writeScript "install-grub.sh" (''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         set -e
-        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])}
+        export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX XMLSAXBase ListCompare ])}
         ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
       '' + flip concatMapStrings cfg.mirroredBoots (args: ''
         ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index cc03e54ead63..8bd203106f55 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -182,7 +182,7 @@ sub GrubFs {
                 # Based on the type pull in the identifier from the system
                 my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
                 if ($status != 0) {
-                    die "Failed to get blkid info for @{[$fs->mount]} on @{[$fs->device]}";
+                    die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
                 }
                 my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
                 if ($#matches != 0) {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 06f004fb06ec..54dfb53fd30f 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -5,7 +5,7 @@ with lib;
 let
   luks = config.boot.initrd.luks;
 
-  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, ... }: assert name' == name; ''
+  openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, fallbackToPassword, ... }: assert name' == name; ''
 
     # Wait for a target (e.g. device, keyFile, header, ...) to appear.
     wait_target() {
@@ -43,8 +43,17 @@ let
     open_normally() {
         echo luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
           ${optionalString (header != null) "--header=${header}"} \
-          ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"} \
           > /.luksopen_args
+        ${optionalString (keyFile != null) ''
+        ${optionalString fallbackToPassword "if [ -e ${keyFile} ]; then"}
+            echo " --key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}" \
+              >> /.luksopen_args
+        ${optionalString fallbackToPassword ''
+        else
+            echo "keyfile ${keyFile} not found -- fallback to interactive unlocking"
+        fi
+        ''}
+        ''}
         cryptsetup-askpass
         rm /.luksopen_args
     }
@@ -227,6 +236,7 @@ in
       default =
         [ "aes" "aes_generic" "blowfish" "twofish"
           "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
+
           (if pkgs.stdenv.system == "x86_64-linux" then "aes_x86_64" else "aes_i586")
         ];
       description = ''
@@ -323,6 +333,16 @@ in
             '';
           };
 
+          fallbackToPassword = mkOption {
+            default = false;
+            type = types.bool;
+            description = ''
+              Whether to fallback to interactive passphrase prompt if the keyfile
+              cannot be found. This will prevent unattended boot should the keyfile
+              go missing.
+            '';
+          };
+
           yubikey = mkOption {
             default = null;
             description = ''
@@ -434,7 +454,12 @@ in
       ["firewire_ohci" "firewire_core" "firewire_sbp2"];
 
     # Some modules that may be needed for mounting anything ciphered
-    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" ] ++ luks.cryptoModules;
+    # Also load input_leds to get caps lock light working (#12456)
+    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ]
+      ++ luks.cryptoModules
+      # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
+      # remove once 'modprobe --show-depends xts' shows ecb as a dependency
+      ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
 
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = ''
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index b915a98d5375..dee0ab470c99 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -54,7 +54,7 @@ with lib;
 
     environment.systemPackages = [ pkgs.kmod ];
 
-    system.activationScripts.modprobe =
+    system.activationScripts.modprobe = stringAfter ["specialfs"]
       ''
         # Allow the kernel to find our wrapped modprobe (which searches
         # in the right location in the Nix store for kernel modules).
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 9d2cea3ad165..eea10613ea58 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -94,7 +94,7 @@ let
   checkNetwork = checkUnitConfig "Network" [
     (assertOnlyFields [
       "Description" "DHCP" "DHCPServer" "IPForward" "IPMasquerade" "IPv4LL" "IPv4LLRoute"
-      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond"
+      "LLMNR" "MulticastDNS" "Domains" "Bridge" "Bond" "IPv6PrivacyExtensions"
     ])
     (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"])
     (assertValueOneOf "DHCPServer" boolValues)
@@ -104,6 +104,7 @@ let
     (assertValueOneOf "IPv4LLRoute" boolValues)
     (assertValueOneOf "LLMNR" boolValues)
     (assertValueOneOf "MulticastDNS" boolValues)
+    (assertValueOneOf "IPv6PrivacyExtensions" ["yes" "no" "prefer-public" "kernel"])
   ];
 
   checkAddress = checkUnitConfig "Address" [
@@ -700,7 +701,6 @@ in
 
     systemd.additionalUpstreamSystemUnits = [
       "systemd-networkd.service" "systemd-networkd-wait-online.service"
-      "org.freedesktop.network1.busname"
     ];
 
     systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index 4b0c498424b5..f8fb8a64cb9b 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -8,9 +8,14 @@ let
 
   cfg = config.boot.plymouth;
 
+  breezePlymouth = pkgs.breeze-plymouth.override {
+    nixosBranding = true;
+    nixosVersion = config.system.nixos.release;
+  };
+
   themesEnv = pkgs.buildEnv {
     name = "plymouth-themes";
-    paths = [ plymouth ] ++ cfg.themePackages;
+    paths = [ plymouth breezePlymouth ] ++ cfg.themePackages;
   };
 
   configFile = pkgs.writeText "plymouthd.conf" ''
@@ -38,7 +43,7 @@ in
       };
 
       theme = mkOption {
-        default = "fade-in";
+        default = "breeze";
         type = types.str;
         description = ''
           Splash screen theme.
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 2147d43c4f19..4d9de020c84e 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -126,7 +126,7 @@ in
   config = mkIf cfg.enable {
 
     systemd.additionalUpstreamSystemUnits = [
-      "systemd-resolved.service" "org.freedesktop.resolve1.busname"
+      "systemd-resolved.service"
     ];
 
     systemd.services.systemd-resolved = {
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index b442386914ad..964ec68cfe2f 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -167,6 +167,7 @@ done
 # Load the required kernel modules.
 mkdir -p /lib
 ln -s @modulesClosure@/lib/modules /lib/modules
+ln -s @modulesClosure@/lib/firmware /lib/firmware
 echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
 for i in @kernelModules@; do
     echo "loading module $(basename $i)..."
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index d6e3e3a87d01..55bb6d3449c5 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -13,12 +13,14 @@ let
 
   kernelPackages = config.boot.kernelPackages;
   modulesTree = config.system.modulesTree;
+  firmware = config.hardware.firmware;
 
 
   # Determine the set of modules that we need to mount the root FS.
   modulesClosure = pkgs.makeModulesClosure {
     rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
     kernel = modulesTree;
+    firmware = firmware;
     allowMissing = true;
   };
 
@@ -28,6 +30,50 @@ let
   # mounting `/`, like `/` on a loopback).
   fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
 
+  # A utility for enumerating the shared-library dependencies of a program
+  findLibs = pkgs.writeShellScriptBin "find-libs" ''
+    set -euo pipefail
+
+    declare -A seen
+    declare -a left
+
+    patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
+
+    function add_needed {
+      rpath="$($patchelf --print-rpath $1)"
+      dir="$(dirname $1)"
+      for lib in $($patchelf --print-needed $1); do
+        left+=("$lib" "$rpath" "$dir")
+      done
+    }
+
+    add_needed $1
+
+    while [ ''${#left[@]} -ne 0 ]; do
+      next=''${left[0]}
+      rpath=''${left[1]}
+      ORIGIN=''${left[2]}
+      left=("''${left[@]:3}")
+      if [ -z ''${seen[$next]+x} ]; then
+        seen[$next]=1
+        IFS=: read -ra paths <<< $rpath
+        res=
+        for path in "''${paths[@]}"; do
+          path=$(eval "echo $path")
+          if [ -f "$path/$next" ]; then
+              res="$path/$next"
+              echo "$res"
+              add_needed "$res"
+              break
+          fi
+        done
+        if [ -z "$res" ]; then
+          echo "Couldn't satisfy dependency $next" >&2
+          exit 1
+        fi
+      fi
+    done
+  '';
 
   # Some additional utilities needed in stage 1, like mount, lvm, fsck
   # etc.  We don't want to bring in all of those packages, so we just
@@ -35,7 +81,7 @@ let
   # we just copy what we need from Glibc and use patchelf to make it
   # work.
   extraUtils = pkgs.runCommandCC "extra-utils"
-    { buildInputs = [pkgs.nukeReferences];
+    { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
     }
     ''
@@ -101,9 +147,7 @@ let
       # Copy all of the needed libraries
       find $out/bin $out/lib -type f | while read BIN; do
         echo "Copying libs for executable $BIN"
-        LDD="$(ldd $BIN)" || continue
-        LIBS="$(echo "$LDD" | awk '{print $3}' | sed '/^$/d')"
-        for LIB in $LIBS; do
+        for LIB in $(${findLibs}/bin/find-libs $BIN); do
           TGT="$out/lib/$(basename $LIB)"
           if [ ! -f "$TGT" ]; then
             SRC="$(readlink -e $LIB)"
@@ -130,6 +174,7 @@ let
         fi
       done
 
+      if [ -z "${toString pkgs.stdenv.isCross}" ]; then
       # Make sure that the patchelf'ed binaries still work.
       echo "testing patched programs..."
       $out/bin/ash -c 'echo hello world' | grep "hello world"
@@ -142,6 +187,7 @@ let
       $out/bin/mdadm --version
 
       ${config.boot.initrd.extraUtilsCommandsTest}
+      fi
     ''; # */
 
 
@@ -243,7 +289,7 @@ let
             { src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; }
             ''
               target=$out
-              ${pkgs.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
+              ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
             '';
           symlink = "/etc/modprobe.d/ubuntu.conf";
         }
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 46aed44bf10f..b83012dfda7e 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -43,7 +43,7 @@ if [ ! -e /proc/1 ]; then
         local options="$3"
         local fsType="$4"
 
-        mkdir -m 0755 -p "$mountPoint"
+        install -m 0755 -d "$mountPoint"
         mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
     }
     source @earlyMountScript@
@@ -71,7 +71,7 @@ fi
 
 
 # Provide a /etc/mtab.
-mkdir -m 0755 -p /etc
+install -m 0755 -d /etc
 test -e /etc/fstab || touch /etc/fstab # to shut up mount
 rm -f /etc/mtab* # not that we care about stale locks
 ln -s /proc/mounts /etc/mtab
@@ -79,10 +79,9 @@ ln -s /proc/mounts /etc/mtab
 
 # More special file systems, initialise required directories.
 [ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default
-mkdir -m 01777 -p /tmp
-mkdir -m 0755 -p /var/{log,lib,db} /nix/var /etc/nixos/ \
+install -m 01777 -d /tmp
+install -m 0755 -d /var/{log,lib,db} /nix/var /etc/nixos/ \
     /run/lock /home /bin # for the /bin/sh symlink
-install -m 0700 -d /root
 
 
 # Miscellaneous boot time cleanup.
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index 8db6d2d2f734..78afbd8dbc12 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -10,6 +10,7 @@ let
   bootStage2 = pkgs.substituteAll {
     src = ./stage-2-init.sh;
     shellDebug = "${pkgs.bashInteractive}/bin/bash";
+    shell = "${pkgs.bash}/bin/bash";
     isExecutable = true;
     inherit (config.nix) readOnlyStore;
     inherit (config.networking) useHostResolvConf;
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 43a9c28bb694..5255f1a1b97a 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -217,7 +217,7 @@ in rec {
 
     environment = mkOption {
       default = {};
-      type = types.attrs; # FIXME
+      type = with types; attrsOf (nullOr (either str package));
       example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
       description = "Environment variables passed to the service's processes.";
     };
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index dd9ba7104485..d2fe33488a7a 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -14,7 +14,6 @@ let
   upstreamSystemUnits =
     [ # Targets.
       "basic.target"
-      "busnames.target"
       "sysinit.target"
       "sockets.target"
       "exit.target"
@@ -47,6 +46,7 @@ let
 
       # Consoles.
       "getty.target"
+      "getty-pre.target"
       "getty@.service"
       "serial-getty@.service"
       "console-getty.service"
@@ -63,10 +63,7 @@ let
       "systemd-logind.service"
       "autovt@.service"
       "systemd-user-sessions.service"
-      "dbus-org.freedesktop.login1.service"
       "dbus-org.freedesktop.machine1.service"
-      "org.freedesktop.login1.busname"
-      "org.freedesktop.machine1.busname"
       "user@.service"
 
       # Journal.
@@ -99,7 +96,6 @@ let
       "swap.target"
       "dev-hugepages.mount"
       "dev-mqueue.mount"
-      "proc-sys-fs-binfmt_misc.mount"
       "sys-fs-fuse-connections.mount"
       "sys-kernel-config.mount"
       "sys-kernel-debug.mount"
@@ -141,7 +137,6 @@ let
 
       # Slices / containers.
       "slices.target"
-      "system.slice"
       "user.slice"
       "machine.slice"
       "machines.target"
@@ -155,19 +150,16 @@ let
       "systemd-tmpfiles-setup-dev.service"
 
       # Misc.
-      "org.freedesktop.systemd1.busname"
       "systemd-sysctl.service"
       "dbus-org.freedesktop.timedate1.service"
       "dbus-org.freedesktop.locale1.service"
       "dbus-org.freedesktop.hostname1.service"
-      "org.freedesktop.timedate1.busname"
-      "org.freedesktop.locale1.busname"
-      "org.freedesktop.hostname1.busname"
       "systemd-timedated.service"
       "systemd-localed.service"
       "systemd-hostnamed.service"
       "systemd-binfmt.service"
       "systemd-exit.service"
+      "systemd-update-done.service"
     ]
     ++ cfg.additionalUpstreamSystemUnits;
 
@@ -182,7 +174,6 @@ let
   upstreamUserUnits =
     [ "basic.target"
       "bluetooth.target"
-      "busnames.target"
       "default.target"
       "exit.target"
       "graphical-session-pre.target"
@@ -249,37 +240,37 @@ let
         }
         (mkIf (config.preStart != "")
           { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStart}
             '';
           })
         (mkIf (config.script != "")
           { serviceConfig.ExecStart = makeJobScript "${name}-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.script}
             '' + " " + config.scriptArgs;
           })
         (mkIf (config.postStart != "")
           { serviceConfig.ExecStartPost = makeJobScript "${name}-post-start" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStart}
             '';
           })
         (mkIf (config.reload != "")
           { serviceConfig.ExecReload = makeJobScript "${name}-reload" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.reload}
             '';
           })
         (mkIf (config.preStop != "")
           { serviceConfig.ExecStop = makeJobScript "${name}-pre-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.preStop}
             '';
           })
         (mkIf (config.postStop != "")
           { serviceConfig.ExecStopPost = makeJobScript "${name}-post-stop" ''
-              #! ${pkgs.stdenv.shell} -e
+              #! ${pkgs.runtimeShell} -e
               ${config.postStop}
             '';
           })
@@ -524,7 +515,7 @@ in
     };
 
     systemd.globalEnvironment = mkOption {
-      type = types.attrs;
+      type = with types; attrsOf (nullOr (either str package));
       default = {};
       example = { TZ = "CET"; };
       description = ''
@@ -532,6 +523,14 @@ in
       '';
     };
 
+    systemd.enableCgroupAccounting = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable cgroup accounting.
+      '';
+    };
+
     systemd.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -733,6 +732,13 @@ in
 
       "systemd/system.conf".text = ''
         [Manager]
+        ${optionalString config.systemd.enableCgroupAccounting ''
+          DefaultCPUAccounting=yes
+          DefaultIOAccounting=yes
+          DefaultBlockIOAccounting=yes
+          DefaultMemoryAccounting=yes
+          DefaultTasksAccounting=yes
+        ''}
         ${config.systemd.extraConfig}
       '';
 
@@ -789,8 +795,7 @@ in
 
         # Keep a persistent journal. Note that systemd-tmpfiles will
         # set proper ownership/permissions.
-        # FIXME: revert to 0700 with systemd v233.
-        mkdir -m 0750 -p /var/log/journal
+        mkdir -m 0700 -p /var/log/journal
       '';
 
     users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
@@ -830,7 +835,8 @@ in
 
     system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
       [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
-        "SYSFS" "PROC_FS" "FHANDLE" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
+        "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
+        "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL"
         "TMPFS_XATTR" "SECCOMP"
       ];
 
@@ -887,7 +893,7 @@ in
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
-    systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ];
+    systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.mount" ];
 
     # Don't bother with certain units in containers.
     systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
diff --git a/nixos/modules/tasks/filesystems/btrfs.nix b/nixos/modules/tasks/filesystems/btrfs.nix
index 8cfa1b6921d3..1384873b6631 100644
--- a/nixos/modules/tasks/filesystems/btrfs.nix
+++ b/nixos/modules/tasks/filesystems/btrfs.nix
@@ -1,35 +1,132 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 with lib;
 
 let
 
   inInitrd = any (fs: fs == "btrfs") config.boot.initrd.supportedFilesystems;
+  inSystem = any (fs: fs == "btrfs") config.boot.supportedFilesystems;
+
+  cfgScrub = config.services.btrfs.autoScrub;
+
+  enableAutoScrub = cfgScrub.enable;
+  enableBtrfs = inInitrd || inSystem || enableAutoScrub;
 
 in
 
 {
-  config = mkIf (any (fs: fs == "btrfs") config.boot.supportedFilesystems) {
+  options = {
+    # One could also do regular btrfs balances, but that shouldn't be necessary
+    # during normal usage and as long as the filesystems aren't filled near capacity
+    services.btrfs.autoScrub = {
+      enable = mkEnableOption "Enable regular btrfs scrub";
 
-    system.fsPackages = [ pkgs.btrfs-progs ];
+      fileSystems = mkOption {
+        type = types.listOf types.path;
+        example = [ "/" ];
+        description = ''
+          List of paths to btrfs filesystems to regularily call <command>btrfs scrub</command> on.
+          Defaults to all mount points with btrfs filesystems.
+          If you mount a filesystem multiple times or additionally mount subvolumes,
+          you need to manually specify this list to avoid scrubbing multiple times.
+        '';
+      };
 
-    boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+      interval = mkOption {
+        default = "monthly";
+        type = types.str;
+        example = "weekly";
+        description = ''
+          Systemd calendar expression for when to scrub btrfs filesystems.
+          The recommended period is a month but could be less
+          (<citerefentry><refentrytitle>btrfs-scrub</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry>).
+          See
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>
+          for more information on the syntax.
+        '';
+      };
+
+    };
+  };
 
-    boot.initrd.extraUtilsCommands = mkIf inInitrd
+  config = mkMerge [
+    (mkIf enableBtrfs {
+      system.fsPackages = [ pkgs.btrfs-progs ];
+
+      boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+
+      boot.initrd.extraUtilsCommands = mkIf inInitrd
       ''
         copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs
         ln -sv btrfs $out/bin/btrfsck
         ln -sv btrfsck $out/bin/fsck.btrfs
       '';
 
-    boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
+      boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
       ''
         $out/bin/btrfs --version
       '';
 
-    boot.initrd.postDeviceCommands = mkIf inInitrd
+      boot.initrd.postDeviceCommands = mkIf inInitrd
       ''
         btrfs device scan
       '';
-  };
+    })
+
+    (mkIf enableAutoScrub {
+      assertions = [
+        {
+          assertion = cfgScrub.enable -> (cfgScrub.fileSystems != []);
+          message = ''
+            If 'services.btrfs.autoScrub' is enabled, you need to have at least one
+            btrfs file system mounted via 'fileSystems' or specify a list manually
+            in 'services.btrfs.autoScrub.fileSystems'.
+          '';
+        }
+      ];
+
+      # This will yield duplicated units if the user mounts a filesystem multiple times
+      # or additionally mounts subvolumes, but going the other way around via devices would
+      # yield duplicated units when a filesystem spans multiple devices.
+      # This way around seems like the more sensible default.
+      services.btrfs.autoScrub.fileSystems = mkDefault (mapAttrsToList (name: fs: fs.mountPoint)
+      (filterAttrs (name: fs: fs.fsType == "btrfs") config.fileSystems));
+
+      # TODO: Did not manage to do it via the usual btrfs-scrub@.timer/.service
+      # template units due to problems enabling the parameterized units,
+      # so settled with many units and templating via nix for now.
+      # https://github.com/NixOS/nixpkgs/pull/32496#discussion_r156527544
+      systemd.timers = let
+        scrubTimer = fs: let
+          fs' = utils.escapeSystemdPath fs;
+        in nameValuePair "btrfs-scrub-${fs'}" {
+          description = "regular btrfs scrub timer on ${fs}";
+
+          wantedBy = [ "timers.target" ];
+          timerConfig = {
+            OnCalendar = cfgScrub.interval;
+            AccuracySec = "1d";
+            Persistent = true;
+          };
+        };
+      in listToAttrs (map scrubTimer cfgScrub.fileSystems);
+
+      systemd.services = let
+        scrubService = fs: let
+          fs' = utils.escapeSystemdPath fs;
+        in nameValuePair "btrfs-scrub-${fs'}" {
+          description = "btrfs scrub on ${fs}";
+
+          serviceConfig = {
+            Type = "oneshot";
+            Nice = 19;
+            IOSchedulingClass = "idle";
+            ExecStart = "${pkgs.btrfs-progs}/bin/btrfs scrub start -B ${fs}";
+          };
+        };
+      in listToAttrs (map scrubService cfgScrub.fileSystems);
+    })
+  ];
 }
diff --git a/nixos/modules/tasks/filesystems/exfat.nix b/nixos/modules/tasks/filesystems/exfat.nix
index 963bc940b4fa..1527f993fdd4 100644
--- a/nixos/modules/tasks/filesystems/exfat.nix
+++ b/nixos/modules/tasks/filesystems/exfat.nix
@@ -5,7 +5,7 @@ with lib;
 {
   config = mkIf (any (fs: fs == "exfat") config.boot.supportedFilesystems) {
 
-    system.fsPackages = [ pkgs.exfat-utils pkgs.fuse_exfat ];
+    system.fsPackages = [ pkgs.exfat ];
 
   };
 }
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 7fee99115329..c3bf897d51fd 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -24,7 +24,11 @@ let
 
   kernel = config.boot.kernelPackages;
 
-  packages = if config.boot.zfs.enableUnstable then {
+  packages = if config.boot.zfs.enableLegacyCrypto then {
+    spl = kernel.splLegacyCrypto;
+    zfs = kernel.zfsLegacyCrypto;
+    zfsUser = pkgs.zfsLegacyCrypto;
+  } else if config.boot.zfs.enableUnstable then {
     spl = kernel.splUnstable;
     zfs = kernel.zfsUnstable;
     zfsUser = pkgs.zfsUnstable;
@@ -75,6 +79,27 @@ in
           '';
       };
 
+      enableLegacyCrypto = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enabling this option will allow you to continue to use the old format for
+          encrypted datasets. With the inclusion of stability patches the format of
+          encrypted datasets has changed. They can still be accessed and mounted but
+          in read-only mode mounted. It is highly recommended to convert them to
+          the new format.
+
+          This option is only for convenience to people that cannot convert their
+          datasets to the new format yet and it will be removed in due time.
+
+          For migration strategies from old format to this new one, check the Wiki:
+          https://nixos.wiki/wiki/NixOS_on_ZFS#Encrypted_Dataset_Format_Change
+
+          See https://github.com/zfsonlinux/zfs/pull/6864 for more details about
+          the stability patches.
+          '';
+      };
+
       extraPools = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -268,7 +293,7 @@ in
       assertions = [
         {
           assertion = config.networking.hostId != null;
-          message = "ZFS requires config.networking.hostId to be set";
+          message = "ZFS requires networking.hostId to be set";
         }
         {
           assertion = !cfgZfs.forceImportAll || cfgZfs.forceImportRoot;
@@ -280,6 +305,8 @@ in
         }
       ];
 
+      virtualisation.lxd.zfsSupport = true;
+
       boot = {
         kernelModules = [ "spl" "zfs" ] ;
         extraModulePackages = with packages; [ spl zfs ];
@@ -427,7 +454,7 @@ in
                               }) snapshotNames);
 
       systemd.timers = let
-                         timer = name: if name == "frequent" then "*:15,30,45" else name;
+                         timer = name: if name == "frequent" then "*:0,15,30,45" else name;
                        in builtins.listToAttrs (map (snapName:
                             {
                               name = "zfs-snapshot-${snapName}";
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index 7fb3cbc5c1bc..fbe42b8e8f04 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -13,7 +13,7 @@ let
   isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
 
   optimizedKeymap = pkgs.runCommand "keymap" {
-    nativeBuildInputs = [ pkgs.kbd ];
+    nativeBuildInputs = [ pkgs.buildPackages.kbd ];
     LOADKEYS_KEYMAP_PATH = "${kbdEnv}/share/keymaps/**";
   } ''
     loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 1f424f84c6e0..e754a1e8718d 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -20,14 +20,8 @@ let
     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
 
   interfaceIps = i:
-    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
-    ++ optional (i.ipAddress != null) {
-      address = i.ipAddress;
-      prefixLength = i.prefixLength;
-    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
-      address = i.ipv6Address;
-      prefixLength = i.ipv6PrefixLength;
-    };
+    i.ipv4.addresses
+    ++ optionals cfg.enableIPv6 i.ipv6.addresses;
 
   destroyBond = i: ''
     while true; do
@@ -74,13 +68,12 @@ let
              (hasAttr dev cfg.macvlans) ||
              (hasAttr dev cfg.sits) ||
              (hasAttr dev cfg.vlans) ||
-             (hasAttr dev cfg.vswitches) ||
-             (hasAttr dev cfg.wlanInterfaces)
+             (hasAttr dev cfg.vswitches)
           then [ "${dev}-netdev.service" ]
           else optional (dev != null && dev != "lo" && !config.boot.isContainer) (subsystemDevice dev);
 
         hasDefaultGatewaySet = (cfg.defaultGateway != null && cfg.defaultGateway.address != "")
-                            || (cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
+                            || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
 
         networkLocalCommands = {
           after = [ "network-setup.service" ];
@@ -185,33 +178,58 @@ let
             path = [ pkgs.iproute ];
             script =
               ''
-                # FIXME: shouldn't this be done in network-link?
-                echo "bringing up interface..."
-                ip link set "${i.name}" up
-
                 state="/run/nixos/network/addresses/${i.name}"
+                mkdir -p $(dirname "$state")
 
+                ${flip concatMapStrings ips (ip:
+                  let
+                    cidr = "${ip.address}/${toString ip.prefixLength}";
+                  in
+                  ''
+                    echo "${cidr}" >> $state
+                    echo -n "adding address ${cidr}... "
+                    if out=$(ip addr add "${cidr}" dev "${i.name}" 2>&1); then
+                      echo "done"
+                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                      echo "'ip addr add "${cidr}" dev "${i.name}"' failed: $out"
+                      exit 1
+                    fi
+                  ''
+                )}
+
+                state="/run/nixos/network/routes/${i.name}"
                 mkdir -p $(dirname "$state")
 
-              '' + flip concatMapStrings (ips) (ip:
-                let
-                  address = "${ip.address}/${toString ip.prefixLength}";
-                in
-                ''
-                  echo "${address}" >> $state
-                  if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
-                    echo "added ip ${address}"
-                  elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
-                    echo "failed to add ${address}"
-                    exit 1
-                  fi
-                '');
+                ${flip concatMapStrings (i.ipv4.routes ++ i.ipv6.routes) (route:
+                  let
+                    cidr = "${route.address}/${toString route.prefixLength}";
+                    via = optionalString (route.via != null) ''via "${route.via}"'';
+                    options = concatStrings (mapAttrsToList (name: val: "${name} ${val} ") route.options);
+                  in
+                  ''
+                     echo "${cidr}" >> $state
+                     echo -n "adding route ${cidr}... "
+                     if out=$(ip route add "${cidr}" ${options} ${via} dev "${i.name}" 2>&1); then
+                       echo "done"
+                     elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                       echo "'ip route add "${cidr}" ${options} ${via} dev "${i.name}"' failed: $out"
+                       exit 1
+                     fi
+                  ''
+                )}
+              '';
             preStop = ''
+              state="/run/nixos/network/routes/${i.name}"
+              while read cidr; do
+                echo -n "deleting route $cidr... "
+                ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed"
+              done < "$state"
+              rm -f "$state"
+
               state="/run/nixos/network/addresses/${i.name}"
-              while read address; do
-                echo -n "deleting $address..."
-                ip addr del "$address" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
-                echo ""
+              while read cidr; do
+                echo -n "deleting address $cidr... "
+                ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed"
               done < "$state"
               rm -f "$state"
             '';
@@ -230,9 +248,7 @@ let
               RemainAfterExit = true;
             };
             script = ''
-              ip tuntap add dev "${i.name}" \
-              ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \
-              user "${i.virtualOwner}"
+              ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}"
             '';
             postStop = ''
               ip link del ${i.name} || true
@@ -271,6 +287,17 @@ let
                 ${i}
               '')}" > /run/${n}.interfaces
 
+              ${optionalString config.virtualisation.libvirtd.enable ''
+                  # Enslave dynamically added interfaces which may be lost on nixos-rebuild
+                  for uri in qemu:///system lxc:///; do
+                    for dom in $(${pkgs.libvirt}/bin/virsh -c $uri list --name); do
+                      ${pkgs.libvirt}/bin/virsh -c $uri dumpxml "$dom" | \
+                      ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//domain/devices/interface[@type='bridge'][source/@bridge='${n}'][target/@dev]" -v "concat('ip link set ',target/@dev,' master ',source/@bridge,';')" | \
+                      ${pkgs.bash}/bin/bash
+                    done
+                  done
+                ''}
+
               # Enable stp on the interface
               ${optionalString v.rstp ''
                 echo 2 >/sys/class/net/${n}/bridge/stp_state
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index a365a01bfb1e..c640e886fca8 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -9,14 +9,8 @@ let
   interfaces = attrValues cfg.interfaces;
 
   interfaceIps = i:
-    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
-    ++ optional (i.ipAddress != null) {
-      address = i.ipAddress;
-      prefixLength = i.prefixLength;
-    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
-      address = i.ipv6Address;
-      prefixLength = i.ipv6PrefixLength;
-    };
+    i.ipv4.addresses
+    ++ optionals cfg.enableIPv6 i.ipv6.addresses;
 
   dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "both" else "none";
 
@@ -74,27 +68,24 @@ in
         networks."99-main" = genericNetwork mkDefault;
       }
       (mkMerge (flip map interfaces (i: {
-        netdevs = mkIf i.virtual (
-          let
-            devType = if i.virtualType != null then i.virtualType
-              else (if hasPrefix "tun" i.name then "tun" else "tap");
-          in {
-            "40-${i.name}" = {
-              netdevConfig = {
-                Name = i.name;
-                Kind = devType;
-              };
-              "${devType}Config" = optionalAttrs (i.virtualOwner != null) {
-                User = i.virtualOwner;
-              };
+        netdevs = mkIf i.virtual ({
+          "40-${i.name}" = {
+            netdevConfig = {
+              Name = i.name;
+              Kind = i.virtualType;
+            };
+            "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) {
+              User = i.virtualOwner;
             };
-          });
+          };
+        });
         networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
           name = mkDefault i.name;
           DHCP = mkForce (dhcpStr
             (if i.useDHCP != null then i.useDHCP else cfg.useDHCP && interfaceIps i == [ ]));
           address = flip map (interfaceIps i)
             (ip: "${ip.address}/${toString ip.prefixLength}");
+          networkConfig.IPv6PrivacyExtensions = "kernel";
         } ];
       })))
       (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index b7e85e402aa9..14f9b9567515 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, utils, stdenv, ... }:
+{ config, options, lib, pkgs, utils, stdenv, ... }:
 
 with lib;
 with utils;
@@ -26,7 +26,7 @@ let
     executable = true;
     destination = "/bin/bridge-stp";
     text = ''
-      #!${pkgs.stdenv.shell} -e
+      #!${pkgs.runtimeShell} -e
       export PATH="${pkgs.mstpd}/bin"
 
       BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)})
@@ -62,35 +62,6 @@ let
     then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces)
     else mapAttrsToList (n: v: v // {_iName = n;}) interfaces;
 
-  # udev script that configures a physical wlan device and adds virtual interfaces
-  wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" ''
-    #!${pkgs.stdenv.shell}
-
-    # Change the wireless phy device to a predictable name.
-    if [ -e "/sys/class/net/${device}/phy80211/name" ]; then
-      ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/${device}/phy80211/name` set name ${device} || true
-    fi
-
-    # Crate new, virtual interfaces and configure them at the same time
-    ${flip concatMapStrings (drop 1 interfaceList) (i: ''
-    ${pkgs.iw}/bin/iw dev ${device} interface add ${i._iName} type ${i.type} \
-      ${optionalString (i.type == "mesh" && i.meshID != null) "mesh_id ${i.meshID}"} \
-      ${optionalString (i.type == "monitor" && i.flags != null) "flags ${i.flags}"} \
-      ${optionalString (i.type == "managed" && i.fourAddr != null) "4addr ${if i.fourAddr then "on" else "off"}"} \
-      ${optionalString (i.mac != null) "addr ${i.mac}"}
-    '')}
-
-    # Reconfigure and rename the default interface that already exists
-    ${flip concatMapStrings (take 1 interfaceList) (i: ''
-      ${pkgs.iw}/bin/iw dev ${device} set type ${i.type}
-      ${optionalString (i.type == "mesh" && i.meshID != null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${i.meshID}"}
-      ${optionalString (i.type == "monitor" && i.flags != null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${i.flags}"}
-      ${optionalString (i.type == "managed" && i.fourAddr != null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if i.fourAddr then "on" else "off"}"}
-      ${optionalString (i.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${i.mac}"}
-      ${optionalString (device != i._iName) "${pkgs.iproute}/bin/ip link set dev ${device} name ${i._iName}"}
-    '')}
-  '';
-
   # We must escape interfaces due to the systemd interpretation
   subsystemDevice = interface:
     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
@@ -101,7 +72,7 @@ let
         address = mkOption {
           type = types.str;
           description = ''
-            IPv${toString v} address of the interface.  Leave empty to configure the
+            IPv${toString v} address of the interface. Leave empty to configure the
             interface using DHCP.
           '';
         };
@@ -116,6 +87,40 @@ let
       };
     };
 
+  routeOpts = v:
+  { options = {
+      address = mkOption {
+        type = types.str;
+        description = "IPv${toString v} address of the network.";
+      };
+
+      prefixLength = mkOption {
+        type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
+        description = ''
+          Subnet mask of the network, specified as the number of
+          bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+        '';
+      };
+
+      via = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "IPv${toString v} address of the next hop.";
+      };
+
+      options = mkOption {
+        type = types.attrsOf types.str;
+        default = { };
+        example = { mtu = "1492"; window = "524288"; };
+        description = ''
+          Other route options. See the symbol <literal>OPTIONS</literal>
+          in the <literal>ip-route(8)</literal> manual page for the details.
+        '';
+      };
+
+    };
+  };
+
   gatewayCoerce = address: { inherit address; };
 
   gatewayOpts = { ... }: {
@@ -148,13 +153,22 @@ let
   interfaceOpts = { name, ... }: {
 
     options = {
-
       name = mkOption {
         example = "eth0";
         type = types.str;
         description = "Name of the interface.";
       };
 
+      preferTempAddress = mkOption {
+        type = types.bool;
+        default = cfg.enableIPv6;
+        defaultText = literalExample "config.networking.enableIPv6";
+        description = ''
+          When using SLAAC prefer a temporary (IPv6) address over the EUI-64
+          address for originating connections. This is used to reduce tracking.
+        '';
+      };
+
       useDHCP = mkOption {
         type = types.nullOr types.bool;
         default = null;
@@ -165,7 +179,7 @@ let
         '';
       };
 
-      ip4 = mkOption {
+      ipv4.addresses = mkOption {
         default = [ ];
         example = [
           { address = "10.0.0.1"; prefixLength = 16; }
@@ -177,7 +191,7 @@ let
         '';
       };
 
-      ip6 = mkOption {
+      ipv6.addresses = mkOption {
         default = [ ];
         example = [
           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
@@ -189,50 +203,27 @@ let
         '';
       };
 
-      ipAddress = mkOption {
-        default = null;
-        example = "10.0.0.1";
-        type = types.nullOr types.str;
-        description = ''
-          IP address of the interface.  Leave empty to configure the
-          interface using DHCP.
-        '';
-      };
-
-      prefixLength = mkOption {
-        default = null;
-        example = 24;
-        type = types.nullOr types.int;
-        description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>24</literal>).
-        '';
-      };
-
-      subnetMask = mkOption {
-        default = null;
-        description = ''
-          Defunct, supply the prefix length instead.
-        '';
-      };
-
-      ipv6Address = mkOption {
-        default = null;
-        example = "2001:1470:fffd:2098::e006";
-        type = types.nullOr types.str;
+      ipv4.routes = mkOption {
+        default = [];
+        example = [
+          { address = "10.0.0.0"; prefixLength = 16; }
+          { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
+        ];
+        type = with types; listOf (submodule (routeOpts 4));
         description = ''
-          IPv6 address of the interface.  Leave empty to configure the
-          interface using NDP.
+          List of extra IPv4 static routes that will be assigned to the interface.
         '';
       };
 
-      ipv6PrefixLength = mkOption {
-        default = 64;
-        example = 64;
-        type = types.int;
+      ipv6.routes = mkOption {
+        default = [];
+        example = [
+          { address = "fdfd:b3f0::"; prefixLength = 48; }
+          { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
+        ];
+        type = with types; listOf (submodule (routeOpts 6));
         description = ''
-          Subnet mask of the interface, specified as the number of
-          bits in the prefix (<literal>64</literal>).
+          List of extra IPv6 static routes that will be assigned to the interface.
         '';
       };
 
@@ -273,11 +264,13 @@ let
       };
 
       virtualType = mkOption {
-        default = null;
-        type = with types; nullOr (enum [ "tun" "tap" ]);
+        default = if hasPrefix "tun" name then "tun" else "tap";
+        defaultText = literalExample ''if hasPrefix "tun" name then "tun" else "tap"'';
+        type = with types; enum [ "tun" "tap" ];
         description = ''
-          The explicit type of interface to create. Accepts tun or tap strings.
-          Also accepts null to implicitly detect the type of device.
+          The type of interface to create.
+          The default is TUN for an interface name starting
+          with "tun", otherwise TAP.
         '';
       };
 
@@ -305,6 +298,32 @@ let
       name = mkDefault name;
     };
 
+    # Renamed or removed options
+    imports =
+      let
+        defined = x: x != "_mkMergedOptionModule";
+      in [
+        (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"])
+        (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"])
+        (mkRemovedOptionModule [ "subnetMask" ] ''
+          Supply a prefix length instead; use option
+          networking.interfaces.<name>.ipv{4,6}.addresses'')
+        (mkMergedOptionModule
+          [ [ "ipAddress" ] [ "prefixLength" ] ]
+          [ "ipv4" "addresses" ]
+          (cfg: with cfg;
+            optional (defined ipAddress && defined prefixLength)
+            { address = ipAddress; prefixLength = prefixLength; }))
+        (mkMergedOptionModule
+          [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ]
+          [ "ipv6" "addresses" ]
+          (cfg: with cfg;
+            optional (defined ipv6Address && defined ipv6PrefixLength)
+            { address = ipv6Address; prefixLength = ipv6PrefixLength; }))
+
+        ({ options.warnings = options.warnings; })
+      ];
+
   };
 
   hexChars = stringToCharacters "0123456789abcdef";
@@ -441,7 +460,7 @@ in
     networking.interfaces = mkOption {
       default = {};
       example =
-        { eth0.ip4 = [ {
+        { eth0.ipv4.addresses = [ {
             address = "131.211.84.78";
             prefixLength = 25;
           } ];
@@ -920,13 +939,10 @@ in
 
   config = {
 
+    warnings = concatMap (i: i.warnings) interfaces;
+
     assertions =
       (flip map interfaces (i: {
-        assertion = i.subnetMask == null;
-        message = ''
-          The networking.interfaces."${i.name}".subnetMask option is defunct. Use prefixLength instead.
-        '';
-      })) ++ (flip map interfaces (i: {
         # With the linux kernel, interface name length is limited by IFNAMSIZ
         # to 16 bytes, including the trailing null byte.
         # See include/linux/if.h in the kernel sources
@@ -935,10 +951,15 @@ in
           The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters.
         '';
       })) ++ (flip map slaveIfs (i: {
-        assertion = i.ip4 == [ ] && i.ipAddress == null && i.ip6 == [ ] && i.ipv6Address == null;
+        assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ];
         message = ''
           The networking.interfaces."${i.name}" must not have any defined ips when it is a slave.
         '';
+      })) ++ (flip map interfaces (i: {
+        assertion = i.preferTempAddress -> cfg.enableIPv6;
+        message = ''
+          Temporary addresses are only needed when IPv6 is enabled.
+        '';
       })) ++ [
         {
           assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId);
@@ -961,9 +982,10 @@ in
       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
-    } // listToAttrs (concatLists (flip map (filter (i: i.proxyARP) interfaces)
-        (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true))
-      ));
+    } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
+        (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)))
+      // listToAttrs (flip map (filter (i: i.preferTempAddress) interfaces)
+        (i: nameValuePair "net.ipv6.conf.${i.name}.use_tempaddr" 2));
 
     # Capabilities won't work unless we have at-least a 4.3 Linux
     # kernel because we need the ambient capability
@@ -1071,6 +1093,9 @@ in
           '' + optionalString (i.mtu != null) ''
             echo "setting MTU to ${toString i.mtu}..."
             ip link set "${i.name}" mtu "${toString i.mtu}"
+          '' + ''
+            echo -n "bringing up interface... "
+            ip link set "${i.name}" up && echo "done" || (echo "failed"; exit 1)
           '';
       })));
 
@@ -1104,7 +1129,7 @@ in
             # The script creates the required, new WLAN interfaces interfaces and configures the
             # existing, default interface.
             curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Change the wireless phy device to a predictable name.
               ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device}
 
@@ -1123,7 +1148,7 @@ in
 
             # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface.
             newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
-              #!${pkgs.stdenv.shell}
+              #!${pkgs.runtimeShell}
               # Configure the new interface
               ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type}
               ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"}
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 7f5b55d5cca0..41dec2af9ed4 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -4,8 +4,11 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
-let kernel = config.boot.kernelPackages.kernel; in
+let
+  kernel = config.boot.kernelPackages.kernel;
+in
 
 {
 
@@ -22,8 +25,8 @@ let kernel = config.boot.kernelPackages.kernel; in
 
     systemd.services.backdoor =
       { wantedBy = [ "multi-user.target" ];
-        requires = [ "dev-hvc0.device" "dev-ttyS0.device" ];
-        after = [ "dev-hvc0.device" "dev-ttyS0.device" ];
+        requires = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
+        after = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ];
         script =
           ''
             export USER=root
@@ -40,7 +43,7 @@ let kernel = config.boot.kernelPackages.kernel; in
 
             cd /tmp
             exec < /dev/hvc0 > /dev/hvc0
-            while ! exec 2> /dev/ttyS0; do sleep 0.1; done
+            while ! exec 2> /dev/${qemuSerialDevice}; do sleep 0.1; done
             echo "connecting to host..." >&2
             stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
             echo
@@ -49,10 +52,10 @@ let kernel = config.boot.kernelPackages.kernel; in
         serviceConfig.KillSignal = "SIGHUP";
       };
 
-    # Prevent agetty from being instantiated on ttyS0, since it
-    # interferes with the backdoor (writes to ttyS0 will randomly fail
+    # Prevent agetty from being instantiated on the serial device, since it
+    # interferes with the backdoor (writes to it will randomly fail
     # with EIO).  Likewise for hvc0.
-    systemd.services."serial-getty@ttyS0".enable = false;
+    systemd.services."serial-getty@${qemuSerialDevice}".enable = false;
     systemd.services."serial-getty@hvc0".enable = false;
 
     boot.initrd.preDeviceCommands =
@@ -88,7 +91,7 @@ let kernel = config.boot.kernelPackages.kernel; in
     # Panic if an error occurs in stage 1 (rather than waiting for
     # user intervention).
     boot.kernelParams =
-      [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
+      [ "console=${qemuSerialDevice}" "panic=1" "boot.panic_on_fail" ];
 
     # `xwininfo' is used by the test driver to query open windows.
     environment.systemPackages = [ pkgs.xorg.xwininfo ];
@@ -122,7 +125,7 @@ let kernel = config.boot.kernelPackages.kernel; in
     # Make it easy to log in as root when running the test interactively.
     users.extraUsers.root.initialHashedPassword = mkOverride 150 "";
 
-    services.xserver.displayManager.logToJournal = true;
+    services.xserver.displayManager.job.logToJournal = true;
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index 1eb3ca707afd..f74c42a777f5 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -11,7 +11,7 @@ with lib;
 let cfg = config.ec2; in
 
 {
-  imports = [ ../profiles/headless.nix ./ec2-data.nix ./grow-partition.nix ./amazon-init.nix ];
+  imports = [ ../profiles/headless.nix ./ec2-data.nix ./amazon-init.nix ];
 
   config = {
 
@@ -21,7 +21,7 @@ let cfg = config.ec2; in
       }
     ];
 
-    virtualisation.growPartition = cfg.hvm;
+    boot.growPartition = cfg.hvm;
 
     fileSystems."/" = {
       device = "/dev/disk/by-label/nixos";
@@ -152,5 +152,8 @@ let cfg = config.ec2; in
     environment.systemPackages = [ pkgs.cryptsetup ];
 
     boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
+    
+    # EC2 has its own NTP server provided by the hypervisor
+    networking.timeServers = [ "169.254.169.123" ];
   };
 }
diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix
index a7362423eb46..8032b2c6d7ca 100644
--- a/nixos/modules/virtualisation/amazon-init.nix
+++ b/nixos/modules/virtualisation/amazon-init.nix
@@ -2,7 +2,7 @@
 
 let
   script = ''
-    #!${pkgs.stdenv.shell} -eu
+    #!${pkgs.runtimeShell} -eu
 
     echo "attempting to fetch configuration from EC2 user data..."
 
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix
index 6817eb837a01..b7ab54aab7ec 100644
--- a/nixos/modules/virtualisation/azure-agent.nix
+++ b/nixos/modules/virtualisation/azure-agent.nix
@@ -47,7 +47,7 @@ let
   };
 
   provisionedHook = pkgs.writeScript "provisioned-hook" ''
-    #!${pkgs.stdenv.shell}
+    #!${pkgs.runtimeShell}
     ${config.systemd.package}/bin/systemctl start provisioned.target
   '';
 
@@ -66,6 +66,10 @@ in
       default = false;
       description = "Whether to enable verbose logging.";
     };
+    mountResourceDisk = mkOption {
+      default = true;
+      description = "Whether the agent should format (ext4) and mount the resource disk to /mnt/resource.";
+    };
   };
 
   ###### implementation
@@ -112,7 +116,7 @@ in
         Provisioning.ExecuteCustomData=n
 
         # Format if unformatted. If 'n', resource disk will not be mounted.
-        ResourceDisk.Format=y
+        ResourceDisk.Format=${if cfg.mountResourceDisk then "y" else "n"}
 
         # File system on the resource disk
         # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here.
@@ -181,7 +185,7 @@ in
       after = [ "network-online.target" "sshd.service" ];
       wants = [ "network-online.target" ];
 
-      path = [ pkgs.e2fsprogs ];
+      path = [ pkgs.e2fsprogs pkgs.bash ];
       description = "Windows Azure Agent Service";
       unitConfig.ConditionPathExists = "/etc/waagent.conf";
       serviceConfig = {
diff --git a/nixos/modules/virtualisation/brightbox-image.nix b/nixos/modules/virtualisation/brightbox-image.nix
index 08bbcfd9d7c2..39a655b4c104 100644
--- a/nixos/modules/virtualisation/brightbox-image.nix
+++ b/nixos/modules/virtualisation/brightbox-image.nix
@@ -26,7 +26,7 @@ in
               rm $diskImageBase
               popd
             '';
-          diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw";
+          diskImageBase = "nixos-image-${config.system.nixos.label}-${pkgs.stdenv.system}.raw";
           buildInputs = [ pkgs.utillinux pkgs.perl ];
           exportReferencesGraph =
             [ "closure" config.system.build.toplevel ];
diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix
index b4f9d8b6fc17..5e368acd6d8b 100644
--- a/nixos/modules/virtualisation/container-config.nix
+++ b/nixos/modules/virtualisation/container-config.nix
@@ -11,7 +11,7 @@ with lib;
     services.udisks2.enable = mkDefault false;
     powerManagement.enable = mkDefault false;
 
-    networking.useHostResolvConf = true;
+    networking.useHostResolvConf = mkDefault true;
 
     # Containers should be light-weight, so start sshd on demand.
     services.openssh.startWhenNeeded = mkDefault true;
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index e68bfd860601..248c2fc1fb23 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -33,7 +33,7 @@ let
     in
       pkgs.writeScript "container-init"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
 
         # Initialise the container side of the veth pair.
         if [ "$PRIVATE_NETWORK" = 1 ]; then
@@ -112,7 +112,7 @@ let
 
       # If the host is 64-bit and the container is 32-bit, add a
       # --personality flag.
-      ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+      ${optionalString (config.nixpkgs.localSystem.system == "x86_64-linux") ''
         if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
           extraFlags+=" --personality=x86"
         fi
@@ -223,7 +223,7 @@ let
   serviceDirectives = cfg: {
     ExecReload = pkgs.writeScript "reload-container"
       ''
-        #! ${pkgs.stdenv.shell} -e
+        #! ${pkgs.runtimeShell} -e
         ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \
           bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
       '';
@@ -255,7 +255,7 @@ let
   };
 
 
-  system = config.nixpkgs.system;
+  system = config.nixpkgs.localSystem.system;
 
   bindMountOpts = { name, config, ... }: {
 
@@ -575,6 +575,16 @@ in
               '';
             };
 
+            extraFlags = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "--drop-capability=CAP_SYS_CHROOT" ];
+              description = ''
+                Extra flags passed to the systemd-nspawn command.
+                See systemd-nspawn(1) for details.
+              '';
+            };
+
           } // networkOptions;
 
           config = mkMerge
@@ -714,7 +724,9 @@ in
             ${optionalString cfg.autoStart ''
               AUTO_START=1
             ''}
-            EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}"
+            EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts +
+              optionalString (cfg.extraFlags != [])
+                (" " + concatStringsSep " " cfg.extraFlags)}"
           '';
       }) config.containers;
 
@@ -726,6 +738,11 @@ in
 
     networking.dhcpcd.denyInterfaces = [ "ve-*" "vb-*" ];
 
+    services.udev.extraRules = optionalString config.networking.networkmanager.enable ''
+      # Don't manage interfaces created by nixos-container.
+      ENV{INTERFACE}=="v[eb]-*", ENV{NM_UNMANAGED}="1"
+    '';
+
     environment.systemPackages = [ pkgs.nixos-container ];
   });
 }
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 14826b6272f7..baffad79b001 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -223,21 +223,39 @@ let self = {
   "17.03".us-west-2.hvm-ebs = "ami-a93daac9";
   "17.03".us-west-2.hvm-s3 = "ami-5139ae31";
 
-  # 17.09.1483.d0f0657ca0
-  "17.09".eu-west-1.hvm-ebs = "ami-cf33e7b6";
-  "17.09".eu-west-2.hvm-ebs = "ami-7d061419";
-  "17.09".eu-central-1.hvm-ebs = "ami-7548fa1a";
-  "17.09".us-east-1.hvm-ebs = "ami-6f669d15";
-  "17.09".us-east-2.hvm-ebs = "ami-cbe1ccae";
-  "17.09".us-west-1.hvm-ebs = "ami-9d95a5fd";
-  "17.09".us-west-2.hvm-ebs = "ami-d3956fab";
-  "17.09".ca-central-1.hvm-ebs = "ami-ee4ef78a";
-  "17.09".ap-southeast-1.hvm-ebs = "ami-1dfc807e";
-  "17.09".ap-southeast-2.hvm-ebs = "ami-dcb350be";
-  "17.09".ap-northeast-1.hvm-ebs = "ami-00ec3d66";
-  "17.09".ap-northeast-2.hvm-ebs = "ami-1107dd7f";
-  "17.09".sa-east-1.hvm-ebs = "ami-0377086f";
-  "17.09".ap-south-1.hvm-ebs = "ami-4a064625";
+  # 17.09.2681.59661f21be6
+  "17.09".eu-west-1.hvm-ebs = "ami-a30192da";
+  "17.09".eu-west-2.hvm-ebs = "ami-295a414d";
+  "17.09".eu-west-3.hvm-ebs = "ami-8c0eb9f1";
+  "17.09".eu-central-1.hvm-ebs = "ami-266cfe49";
+  "17.09".us-east-1.hvm-ebs = "ami-40bee63a";
+  "17.09".us-east-2.hvm-ebs = "ami-9d84aff8";
+  "17.09".us-west-1.hvm-ebs = "ami-d14142b1";
+  "17.09".us-west-2.hvm-ebs = "ami-3eb40346";
+  "17.09".ca-central-1.hvm-ebs = "ami-ca8207ae";
+  "17.09".ap-southeast-1.hvm-ebs = "ami-84bccff8";
+  "17.09".ap-southeast-2.hvm-ebs = "ami-0dc5386f";
+  "17.09".ap-northeast-1.hvm-ebs = "ami-89b921ef";
+  "17.09".ap-northeast-2.hvm-ebs = "ami-179b3b79";
+  "17.09".sa-east-1.hvm-ebs = "ami-4762202b";
+  "17.09".ap-south-1.hvm-ebs = "ami-4e376021";
 
-  latest = self."17.09";
+  # 18.03.131792.becbe4dbe16
+  "18.03".eu-west-1.hvm-ebs = "ami-cda4fab4";
+  "18.03".eu-west-2.hvm-ebs = "ami-d96786be";
+  "18.03".eu-west-3.hvm-ebs = "ami-6b0cba16";
+  "18.03".eu-central-1.hvm-ebs = "ami-5e2b75b5";
+  "18.03".us-east-1.hvm-ebs = "ami-d464cba9";
+  "18.03".us-east-2.hvm-ebs = "ami-fd221298";
+  "18.03".us-west-1.hvm-ebs = "ami-ff0d1d9f";
+  "18.03".us-west-2.hvm-ebs = "ami-c05c3bb8";
+  "18.03".ca-central-1.hvm-ebs = "ami-cc72f4a8";
+  "18.03".ap-southeast-1.hvm-ebs = "ami-b61633ca";
+  "18.03".ap-southeast-2.hvm-ebs = "ami-530fc131";
+  "18.03".ap-northeast-1.hvm-ebs = "ami-90d6c0ec";
+  "18.03".ap-northeast-2.hvm-ebs = "ami-a1248bcf";
+  "18.03".sa-east-1.hvm-ebs = "ami-b090c6dc";
+  "18.03".ap-south-1.hvm-ebs = "ami-32c9ec5d";
+
+  latest = self."18.03";
 }; in self
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index e3b3e6a5f4ab..0b6bec786da4 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -2,11 +2,11 @@
 
 with lib;
 let
-  diskSize = 1024; # MB
+  diskSize = 1536; # MB
   gce = pkgs.google-compute-engine;
 in
 {
-  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ./grow-partition.nix ];
+  imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ];
 
   system.build.googleComputeImage = import ../../lib/make-disk-image.nix {
     name = "google-compute-image";
@@ -14,7 +14,7 @@ in
       PATH=$PATH:${pkgs.stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]}
       pushd $out
       mv $diskImage disk.raw
-      tar -Szcf nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw.tar.gz disk.raw
+      tar -Szcf nixos-image-${config.system.nixos.label}-${pkgs.stdenv.system}.raw.tar.gz disk.raw
       rm $out/disk.raw
       popd
     '';
@@ -29,6 +29,7 @@ in
     autoResize = true;
   };
 
+  boot.growPartition = true;
   boot.kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
   boot.initrd.kernelModules = [ "virtio_scsi" ];
   boot.kernelModules = [ "virtio_pci" "virtio_net" ];
@@ -56,6 +57,12 @@ in
   # Always include cryptsetup so that NixOps can use it.
   environment.systemPackages = [ pkgs.cryptsetup ];
 
+  # Make sure GCE image does not replace host key that NixOps sets
+  environment.etc."default/instance_configs.cfg".text = lib.mkDefault ''
+    [InstanceSetup]
+    set_host_keys = false
+  '';
+
   # Rely on GCP's firewall instead
   networking.firewall.enable = mkDefault false;
 
@@ -68,6 +75,9 @@ in
 
   networking.usePredictableInterfaceNames = false;
 
+  # GC has 1460 MTU
+  networking.interfaces.eth0.mtu = 1460;
+
   # allow the google-accounts-daemon to manage users
   users.mutableUsers = true;
   # and allow users to sudo without password
@@ -211,7 +221,7 @@ in
           echo "Obtaining SSH keys..."
           mkdir -m 0700 -p /root/.ssh
           AUTH_KEYS=$(${mktemp})
-          ${wget} -O $AUTH_KEYS http://metadata.google.internal/computeMetadata/v1/project/attributes/sshKeys
+          ${wget} -O $AUTH_KEYS --header="Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/sshKeys
           if [ -s $AUTH_KEYS ]; then
 
             # Read in key one by one, split in case Google decided
diff --git a/nixos/modules/virtualisation/grow-partition.nix b/nixos/modules/virtualisation/grow-partition.nix
index 2cb932d208f0..444c0bc1630e 100644
--- a/nixos/modules/virtualisation/grow-partition.nix
+++ b/nixos/modules/virtualisation/grow-partition.nix
@@ -1,48 +1,3 @@
-# This module automatically grows the root partition on virtual machines.
-# This allows an instance to be created with a bigger root filesystem
-# than provided by the machine image.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-{
-
-  options = {
-
-    virtualisation.growPartition = mkOption {
-      type = types.bool;
-      default = true;
-    };
-
-  };
-
-  config = mkIf config.virtualisation.growPartition {
-
-    boot.initrd.extraUtilsCommands = ''
-      copy_bin_and_libs ${pkgs.gawk}/bin/gawk
-      copy_bin_and_libs ${pkgs.gnused}/bin/sed
-      copy_bin_and_libs ${pkgs.utillinux}/sbin/sfdisk
-      copy_bin_and_libs ${pkgs.utillinux}/sbin/lsblk
-
-      substitute "${pkgs.cloud-utils}/bin/.growpart-wrapped" "$out/bin/growpart" \
-        --replace "${pkgs.bash}/bin/sh" "/bin/sh" \
-        --replace "awk" "gawk" \
-        --replace "sed" "gnused"
-
-      ln -s sed $out/bin/gnused
-    '';
-
-    boot.initrd.postDeviceCommands = ''
-      rootDevice="${config.fileSystems."/".device}"
-      if [ -e "$rootDevice" ]; then
-        rootDevice="$(readlink -f "$rootDevice")"
-        parentDevice="$(lsblk -npo PKNAME "$rootDevice")"
-        TMPDIR=/run sh $(type -P growpart) "$parentDevice" "''${rootDevice#$parentDevice}"
-        udevadm settle
-      fi
-    '';
-
-  };
-
-}
+# This profile is deprecated, use boot.growPartition directly.
+builtins.trace "the profile <nixos/modules/virtualisation/grow-partition.nix> is deprecated, use boot.growPartition instead"
+{ }
diff --git a/nixos/modules/virtualisation/hyperv-guest.nix b/nixos/modules/virtualisation/hyperv-guest.nix
new file mode 100644
index 000000000000..ecd2a8117710
--- /dev/null
+++ b/nixos/modules/virtualisation/hyperv-guest.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.virtualisation.hypervGuest;
+
+in {
+  options = {
+    virtualisation.hypervGuest = {
+      enable = mkEnableOption "Hyper-V Guest Support";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ config.boot.kernelPackages.hyperv-daemons.bin ];
+
+    security.rngd.enable = false;
+
+    # enable hotadding memory
+    services.udev.packages = lib.singleton (pkgs.writeTextFile {
+      name = "hyperv-memory-hotadd-udev-rules";
+      destination = "/etc/udev/rules.d/99-hyperv-memory-hotadd.rules";
+      text = ''
+        ACTION="add", SUBSYSTEM=="memory", ATTR{state}="online"
+      '';
+    });
+
+    systemd = {
+      packages = [ config.boot.kernelPackages.hyperv-daemons.lib ];
+
+      targets.hyperv-daemons = {
+        wantedBy = [ "multi-user.target" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 8aa7ad8e3911..024db7f87c2e 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -37,11 +37,13 @@ in {
       '';
     };
 
-    virtualisation.libvirtd.enableKVM = mkOption {
-      type = types.bool;
-      default = true;
+    virtualisation.libvirtd.qemuPackage = mkOption {
+      type = types.package;
+      default = pkgs.qemu;
       description = ''
-        This option enables support for QEMU/KVM in libvirtd.
+        Qemu package to use with libvirt.
+        `pkgs.qemu` can emulate alien architectures (e.g. aarch64 on x86)
+        `pkgs.qemu_kvm` saves disk space allowing to emulate only host architectures.
       '';
     };
 
@@ -102,7 +104,7 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd qemu_kvm ];
+    environment.systemPackages = with pkgs; [ libvirt netcat-openbsd cfg.qemuPackage ];
 
     boot.kernelModules = [ "tun" ];
 
@@ -117,17 +119,10 @@ in {
       after = [ "systemd-udev-settle.service" ]
               ++ optional vswitch.enable "vswitchd.service";
 
-      environment = {
-        LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
-      };
+      environment.LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
 
-      path = with pkgs; [
-          bridge-utils
-          dmidecode
-          dnsmasq
-          ebtables
-        ]
-        ++ optional vswitch.enable vswitch.package;
+      path = [ cfg.qemuPackage ] # libvirtd requires qemu-img to manage disk images
+             ++ optional vswitch.enable vswitch.package;
 
       preStart = ''
         mkdir -p /var/log/libvirt/qemu -m 755
@@ -154,9 +149,9 @@ in {
 
         # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
         mkdir -p /run/libvirt/nix-emulators
-        ln -s --force ${pkgs.libvirt}/libexec/libvirt_lxc /run/libvirt/nix-emulators/
-        ${optionalString pkgs.stdenv.isAarch64 "ln -s --force ${pkgs.qemu}/bin/qemu-system-aarch64 /run/libvirt/nix-emulators/"}
-        ${optionalString cfg.enableKVM         "ln -s --force ${pkgs.qemu_kvm}/bin/qemu-kvm        /run/libvirt/nix-emulators/"}
+        for emulator in ${pkgs.libvirt}/libexec/libvirt_lxc ${cfg.qemuPackage}/bin/qemu-kvm ${cfg.qemuPackage}/bin/qemu-system-*; do
+          ln -s --force "$emulator" /run/libvirt/nix-emulators/
+        done
 
         ${optionalString cfg.qemuOvmf ''
             mkdir -p /run/libvirt/nix-ovmf
diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
index 2310fe984325..9b5adaf08249 100644
--- a/nixos/modules/virtualisation/lxc.nix
+++ b/nixos/modules/virtualisation/lxc.nix
@@ -74,6 +74,9 @@ in
     systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
 
     security.apparmor.packages = [ pkgs.lxc ];
-    security.apparmor.profiles = [ "${pkgs.lxc}/etc/apparmor.d/lxc-containers" ];
+    security.apparmor.profiles = [
+      "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
+      "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
+    ];
   };
 }
diff --git a/nixos/modules/virtualisation/lxcfs.nix b/nixos/modules/virtualisation/lxcfs.nix
index 48462dc66da8..b2457403463a 100644
--- a/nixos/modules/virtualisation/lxcfs.nix
+++ b/nixos/modules/virtualisation/lxcfs.nix
@@ -28,13 +28,9 @@ in {
 
   ###### implementation
   config = mkIf cfg.enable {
-    services.cgmanager.enable = true;
-
     systemd.services.lxcfs = {
       description = "FUSE filesystem for LXC";
       wantedBy = [ "multi-user.target" ];
-      requires = [ "cgmanager.service" ];
-      after = [ "cgmanager.service" ];
       before = [ "lxc.service" ];
       restartIfChanged = false;
       serviceConfig = {
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index b1ff0337994e..3e76cdacfc4b 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -15,50 +15,69 @@ in
 
   options = {
 
-    virtualisation.lxd.enable =
-      mkOption {
+    virtualisation.lxd = {
+      enable = mkOption {
         type = types.bool;
         default = false;
-        description =
-          ''
-            This option enables lxd, a daemon that manages
-            containers. Users in the "lxd" group can interact with
-            the daemon (e.g. to start or stop containers) using the
-            <command>lxc</command> command line tool, among others.
-          '';
+        description = ''
+          This option enables lxd, a daemon that manages
+          containers. Users in the "lxd" group can interact with
+          the daemon (e.g. to start or stop containers) using the
+          <command>lxc</command> command line tool, among others.
+        '';
       };
-
+      zfsSupport = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          enables lxd to use zfs as a storage for containers.
+          This option is enabled by default if a zfs pool is configured
+          with nixos.
+        '';
+      };
+    };
   };
 
-
   ###### implementation
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages =
-      [ pkgs.lxd ];
+    environment.systemPackages = [ pkgs.lxd ];
 
-    systemd.services.lxd =
-      { description = "LXD Container Management Daemon";
+    security.apparmor = {
+      enable = true;
+      profiles = [
+        "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
+        "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
+      ];
+      packages = [ pkgs.lxc ];
+    };
 
-        wantedBy = [ "multi-user.target" ];
-        after = [ "systemd-udev-settle.service" ];
+    systemd.services.lxd = {
+      description = "LXD Container Management Daemon";
 
-        # TODO(wkennington): Add lvm2 and thin-provisioning-tools
-        path = with pkgs; [ acl rsync gnutar xz btrfs-progs gzip dnsmasq squashfsTools iproute iptables ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-udev-settle.service" ];
 
-        serviceConfig.ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --syslog --group lxd";
-        serviceConfig.Type = "simple";
-        serviceConfig.KillMode = "process"; # when stopping, leave the containers alone
+      path = lib.optional cfg.zfsSupport pkgs.zfs;
+
+      preStart = ''
+        mkdir -m 0755 -p /var/lib/lxc/rootfs
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --group lxd";
+        Type = "simple";
+        KillMode = "process"; # when stopping, leave the containers alone
       };
 
+    };
+
     users.extraGroups.lxd.gid = config.ids.gids.lxd;
 
     users.extraUsers.root = {
       subUidRanges = [ { startUid = 1000000; count = 65536; } ];
       subGidRanges = [ { startGid = 1000000; count = 65536; } ];
     };
-
   };
-
 }
diff --git a/nixos/modules/virtualisation/nova-config.nix b/nixos/modules/virtualisation/nova-config.nix
index c865cf451e40..c1d2a314daf2 100644
--- a/nixos/modules/virtualisation/nova-config.nix
+++ b/nixos/modules/virtualisation/nova-config.nix
@@ -6,7 +6,6 @@ with lib;
   imports = [
     ../profiles/qemu-guest.nix
     ../profiles/headless.nix
-    ./grow-partition.nix
   ];
 
   config = {
@@ -15,8 +14,7 @@ with lib;
       autoResize = true;
     };
 
-    virtualisation.growPartition = true;
-
+    boot.growPartition = true;
     boot.kernelParams = [ "console=ttyS0" ];
     boot.loader.grub.device = "/dev/vda";
     boot.loader.timeout = 0;
diff --git a/nixos/modules/virtualisation/nova.nix b/nixos/modules/virtualisation/nova.nix
deleted file mode 100644
index c2837d0e2e24..000000000000
--- a/nixos/modules/virtualisation/nova.nix
+++ /dev/null
@@ -1,174 +0,0 @@
-# Module for Nova, a.k.a. OpenStack Compute.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.virtualisation.nova;
-
-  nova = pkgs.nova;
-
-  novaConf = pkgs.writeText "nova.conf"
-    ''
-      --nodaemon
-      --verbose
-      ${cfg.extraConfig}
-    '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    virtualisation.nova.enableSingleNode =
-      mkOption {
-        default = false;
-        description =
-          ''
-            This option enables Nova, also known as OpenStack Compute,
-            a cloud computing system, as a single-machine
-            installation.  That is, all of Nova's components are
-            enabled on this machine, using SQLite as Nova's database.
-            This is useful for evaluating and experimenting with Nova.
-            However, for a real cloud computing environment, you'll
-            want to enable some of Nova's services on other machines,
-            and use a database such as MySQL.
-          '';
-      };
-
-    virtualisation.nova.extraConfig =
-      mkOption {
-        default = "";
-        description =
-          ''
-            Additional text appended to <filename>nova.conf</filename>,
-            the main Nova configuration file.
-          '';
-      };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf cfg.enableSingleNode {
-
-    environment.systemPackages = [ nova pkgs.euca2ools pkgs.novaclient ];
-
-    environment.etc =
-      [ { source = novaConf;
-          target = "nova/nova.conf";
-        }
-      ];
-
-    # Nova requires libvirtd and RabbitMQ.
-    virtualisation.libvirtd.enable = true;
-    services.rabbitmq.enable = true;
-
-    # `qemu-nbd' required the `nbd' kernel module.
-    boot.kernelModules = [ "nbd" ];
-
-    system.activationScripts.nova =
-      ''
-        mkdir -m 755 -p /var/lib/nova
-        mkdir -m 755 -p /var/lib/nova/networks
-        mkdir -m 700 -p /var/lib/nova/instances
-        mkdir -m 700 -p /var/lib/nova/keys
-
-        # Allow the CA certificate generation script (called by
-        # nova-api) to work.
-        mkdir -m 700 -p /var/lib/nova/CA /var/lib/nova/CA/private
-
-        # Initialise the SQLite database.
-        ${nova}/bin/nova-manage db sync
-      '';
-
-    # `nova-api' receives and executes external client requests from
-    # tools such as euca2ools.  It listens on port 8773 (XML) and 8774
-    # (JSON).
-    jobs.nova_api =
-      { name = "nova-api";
-
-        description = "Nova API service";
-
-        startOn = "ip-up";
-
-        # `openssl' is required to generate the CA.  `openssh' is
-        # required to generate key pairs.
-        path = [ pkgs.openssl config.programs.ssh.package pkgs.bash ];
-
-        respawn = false;
-
-        exec = "${nova}/bin/nova-api --flagfile=${novaConf} --api_paste_config=${nova}/etc/nova/api-paste.ini";
-      };
-
-    # `nova-objectstore' is a simple image server.  Useful if you're
-    # not running the OpenStack Imaging Service (Swift).  It serves
-    # images placed in /var/lib/nova/images/.
-    jobs.nova_objectstore =
-      { name = "nova-objectstore";
-
-        description = "Nova Simple Object Store Service";
-
-        startOn = "ip-up";
-
-        preStart =
-          ''
-            mkdir -m 700 -p /var/lib/nova/images
-          '';
-
-        exec = "${nova}/bin/nova-objectstore --flagfile=${novaConf}";
-      };
-
-    # `nova-scheduler' schedules VM execution requests.
-    jobs.nova_scheduler =
-      { name = "nova-scheduler";
-
-        description = "Nova Scheduler Service";
-
-        startOn = "ip-up";
-
-        exec = "${nova}/bin/nova-scheduler --flagfile=${novaConf}";
-      };
-
-    # `nova-compute' starts and manages virtual machines.
-    jobs.nova_compute =
-      { name = "nova-compute";
-
-        description = "Nova Compute Service";
-
-        startOn = "ip-up";
-
-        path =
-          [ pkgs.sudo pkgs.vlan pkgs.nettools pkgs.iptables pkgs.qemu_kvm
-            pkgs.e2fsprogs pkgs.utillinux pkgs.multipath-tools pkgs.iproute
-            pkgs.bridge-utils
-          ];
-
-        exec = "${nova}/bin/nova-compute --flagfile=${novaConf}";
-      };
-
-    # `nova-network' manages networks and allocates IP addresses.
-    jobs.nova_network =
-      { name = "nova-network";
-
-        description = "Nova Network Service";
-
-        startOn = "ip-up";
-
-        path =
-          [ pkgs.sudo pkgs.vlan pkgs.dnsmasq pkgs.nettools pkgs.iptables
-            pkgs.iproute pkgs.bridge-utils pkgs.radvd
-          ];
-
-        exec = "${nova}/bin/nova-network --flagfile=${novaConf}";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/virtualisation/openstack/common.nix b/nixos/modules/virtualisation/openstack/common.nix
deleted file mode 100644
index 2feb0a873951..000000000000
--- a/nixos/modules/virtualisation/openstack/common.nix
+++ /dev/null
@@ -1,84 +0,0 @@
-{ lib }:
-
-with lib;
-
-rec {
-  # A shell script string helper to get the value of a secret at
-  # runtime.
-  getSecret = secretOption:
-    if secretOption.storage == "fromFile"
-    then ''$(cat ${secretOption.value})''
-    else ''${secretOption.value}'';
-
-
-  # A shell script string help to replace at runtime in a file the
-  # pattern of a secret by its value.
-  replaceSecret = secretOption: filename: ''
-    sed -i "s/${secretOption.pattern}/${getSecret secretOption}/g" ${filename}
-    '';
-
-  # This generates an option that can be used to declare secrets which
-  # can be stored in the nix store, or not. A pattern is written in
-  # the nix store to represent the secret. The pattern can
-  # then be overwritten with the value of the secret at runtime.
-  mkSecretOption = {name, description ? ""}:
-    mkOption {
-      description = description;
-      type = types.submodule ({
-        options = {
-          pattern = mkOption {
-            type = types.str;
-            default = "##${name}##";
-            description = "The pattern that represent the secret.";
-            };
-          storage = mkOption {
-            type = types.enum [ "fromNixStore" "fromFile" ];
-            description = ''
-            Choose the way the password is provisionned. If
-            fromNixStore is used, the value is the password and it is
-            written in the nix store. If fromFile is used, the value
-            is a path from where the password will be read at
-            runtime. This is generally used with <link
-            xlink:href="https://nixos.org/nixops/manual/#opt-deployment.keys">
-            deployment keys</link> of Nixops.
-           '';};
-            value = mkOption {
-              type = types.str;
-	      description = ''
-	      If the storage is fromNixStore, the value is the password itself,
-	      otherwise it is a path to the file that contains the password.
-	      '';
-	      };
-            };});
-  };
-  
-  databaseOption = name: {
-    host = mkOption {
-      type = types.str;
-      default = "localhost";
-      description = ''
-        Host of the database.
-      '';
-    };
-
-    name = mkOption {
-      type = types.str;
-      default = name;
-      description = ''
-        Name of the existing database.
-      '';
-    };
-
-    user = mkOption {
-      type = types.str;
-      default = name;
-      description = ''
-        The database user. The user must exist and has access to
-        the specified database.
-      '';
-    };
-    password = mkSecretOption {
-      name = name + "MysqlPassword";
-      description = "The database user's password";};
-  };
-}
diff --git a/nixos/modules/virtualisation/openstack/glance.nix b/nixos/modules/virtualisation/openstack/glance.nix
deleted file mode 100644
index 7862409a65ec..000000000000
--- a/nixos/modules/virtualisation/openstack/glance.nix
+++ /dev/null
@@ -1,245 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib; with import ./common.nix {inherit lib;};
-
-let
-  cfg = config.virtualisation.openstack.glance;
-  commonConf = ''
-    [database]
-    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
-    notification_driver = noop
-
-    [keystone_authtoken]
-    auth_url = ${cfg.authUrl}
-    auth_plugin = password
-    project_name = service
-    project_domain_id = default
-    user_domain_id = default
-    username = ${cfg.serviceUsername}
-    password = ${cfg.servicePassword.pattern}
-
-    [glance_store]
-    default_store = file
-    filesystem_store_datadir = /var/lib/glance/images/
-  '';
-  glanceApiConfTpl = pkgs.writeText "glance-api.conf" ''
-    ${commonConf}
-
-    [paste_deploy]
-    flavor = keystone
-    config_file = ${cfg.package}/etc/glance-api-paste.ini
-  '';
-  glanceRegistryConfTpl = pkgs.writeText "glance-registry.conf" ''
-    ${commonConf}
-
-    [paste_deploy]
-    config_file = ${cfg.package}/etc/glance-registry-paste.ini
-  '';
-  glanceApiConf = "/var/lib/glance/glance-api.conf";
-  glanceRegistryConf = "/var/lib/glance/glance-registry.conf";
-
-in {
-  options.virtualisation.openstack.glance = {
-    package = mkOption {
-      type = types.package;
-      default = pkgs.glance;
-      defaultText = "pkgs.glance";
-      description = ''
-        Glance package to use.
-      '';
-    };
-
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        This option enables Glance as a single-machine
-        installation. That is, all of Glance's components are
-        enabled on this machine. This is useful for evaluating and
-        experimenting with Glance. Note we are currently not
-        providing any configurations for a multi-node setup.
-      '';
-    };
-
-    authUrl = mkOption {
-      type = types.str;
-      default = http://localhost:5000;
-      description = ''
-        Complete public Identity (Keystone) API endpoint. Note this is
-        unversionned.
-      '';
-    };
-
-    serviceUsername = mkOption {
-      type = types.str;
-      default = "glance";
-      description = ''
-        The Glance service username. This user is created if bootstrap
-        is enable, otherwise it has to be manually created before
-        starting this service.
-      '';
-    };
-
-    servicePassword = mkSecretOption {
-      name = "glanceAdminPassword";
-      description = ''
-        The Glance service user's password.
-      '';
-    };
-
-    database = databaseOption "glance";
-
-    bootstrap = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Bootstrap the Glance service by creating the service tenant,
-          an admin account and a public endpoint. This option provides
-          a ready-to-use glance service. This is only done at the
-          first Glance execution by the systemd post start section.
-          The keystone admin account is used to create required
-          Keystone resource for the Glance service.
-
-          <note><para> This option is a helper for setting up
-          development or testing environments.</para></note>
-        '';
-      };
-
-      endpointPublic = mkOption {
-        type = types.str;
-        default = "http://localhost:9292";
-        description = ''
-          The public image endpoint. The link <link
-          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
-          create endpoint</link> provides more informations
-          about that.
-        '';
-      };
-
-      keystoneAdminUsername = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          The keystone admin user name used to create the Glance account.
-        '';
-      };
-
-      keystoneAdminPassword = mkSecretOption {
-        name = "keystoneAdminPassword";
-        description = ''
-          The keystone admin user's password.
-        '';
-      };
-
-      keystoneAdminTenant = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          The keystone admin tenant used to create the Glance account.
-        '';
-      };
-      keystoneAuthUrl = mkOption {
-        type = types.str;
-        default = "http://localhost:5000/v2.0";
-        description = ''
-          The keystone auth url used to create the Glance account.
-        '';
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    users.extraUsers = [{
-      name = "glance";
-      group = "glance";
-      uid = config.ids.gids.glance;
-
-    }];
-    users.extraGroups = [{
-      name = "glance";
-      gid = config.ids.gids.glance;
-    }];
-
-    systemd.services.glance-registry = {
-      description = "OpenStack Glance Registry Daemon";
-      after = [ "network.target"];
-      path = [ pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        mkdir -m 775 -p /var/lib/glance/{images,scrubber,image_cache}
-        chown glance:glance /var/lib/glance/{images,scrubber,image_cache}
-
-        # Secret file managment
-        cp ${glanceRegistryConfTpl} ${glanceRegistryConf};
-        chown glance:glance ${glanceRegistryConf};
-        chmod 640 ${glanceRegistryConf}
-        ${replaceSecret cfg.database.password glanceRegistryConf}
-        ${replaceSecret cfg.servicePassword glanceRegistryConf}
-
-        cp ${glanceApiConfTpl} ${glanceApiConf};
-        chown glance:glance ${glanceApiConf};
-        chmod 640 ${glanceApiConf}
-        ${replaceSecret cfg.database.password glanceApiConf}
-        ${replaceSecret cfg.servicePassword glanceApiConf}
-
-        # Initialise the database
-        ${cfg.package}/bin/glance-manage --config-file=${glanceApiConf} --config-file=${glanceRegistryConf} db_sync
-      '';
-      postStart = ''
-        set -eu
-        export OS_AUTH_URL=${cfg.bootstrap.keystoneAuthUrl}
-        export OS_USERNAME=${cfg.bootstrap.keystoneAdminUsername}
-        export OS_PASSWORD=${getSecret cfg.bootstrap.keystoneAdminPassword}
-        export OS_TENANT_NAME=${cfg.bootstrap.keystoneAdminTenant}
-
-        # Wait until the keystone is available for use
-        count=0
-        while ! keystone user-get ${cfg.bootstrap.keystoneAdminUsername} > /dev/null
-        do
-            if [ $count -eq 30 ]
-            then
-                echo "Tried 30 times, giving up..."
-                exit 1
-            fi
-
-            echo "Keystone not yet started. Waiting for 1 second..."
-            count=$((count++))
-            sleep 1
-        done
-
-        # If the service glance doesn't exist, we consider glance is
-        # not initialized
-        if ! keystone service-get glance
-        then
-            keystone service-create --type image --name glance
-            ID=$(keystone service-get glance | awk '/ id / { print $4 }')
-            keystone endpoint-create --region RegionOne --service $ID --internalurl http://localhost:9292 --adminurl http://localhost:9292 --publicurl ${cfg.bootstrap.endpointPublic}
-
-            keystone user-create --name ${cfg.serviceUsername} --tenant service --pass ${getSecret cfg.servicePassword}
-            keystone user-role-add --tenant service --user ${cfg.serviceUsername} --role admin
-        fi
-        '';
-      serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
-        TimeoutStartSec = "600"; # 10min for initial db migrations
-        User = "glance";
-        Group = "glance";
-        ExecStart = "${cfg.package}/bin/glance-registry --config-file=${glanceRegistryConf}";
-      };
-    };
-    systemd.services.glance-api = {
-      description = "OpenStack Glance API Daemon";
-      after = [ "glance-registry.service" "network.target"];
-      requires = [ "glance-registry.service" "network.target"]; 
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        PermissionsStartOnly = true; # preStart must be run as root
-        User = "glance";
-        Group = "glance";
-        ExecStart = "${cfg.package}/bin/glance-api --config-file=${glanceApiConf}";
-      };
-    };
-  };
-
-}
diff --git a/nixos/modules/virtualisation/openstack/keystone.nix b/nixos/modules/virtualisation/openstack/keystone.nix
deleted file mode 100644
index e32c5a4cae1b..000000000000
--- a/nixos/modules/virtualisation/openstack/keystone.nix
+++ /dev/null
@@ -1,220 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib; with import ./common.nix {inherit lib;};
-
-let
-  cfg = config.virtualisation.openstack.keystone;
-  keystoneConfTpl = pkgs.writeText "keystone.conf" ''
-    [DEFAULT]
-    admin_token = ${cfg.adminToken.pattern}
-    policy_file=${cfg.package}/etc/policy.json
-
-    [database]
-
-    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
-
-    [paste_deploy]
-    config_file = ${cfg.package}/etc/keystone-paste.ini
-
-    ${cfg.extraConfig}
-  '';
-  keystoneConf = "/var/lib/keystone/keystone.conf";
-
-in {
-  options.virtualisation.openstack.keystone = {
-    package = mkOption {
-      type = types.package;
-      example = literalExample "pkgs.keystone";
-      description = ''
-        Keystone package to use.
-      '';
-    };
-
-    enable = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Enable Keystone, the OpenStack Identity Service
-      '';
-    };
-
-    extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      description = ''
-        Additional text appended to <filename>keystone.conf</filename>,
-        the main Keystone configuration file.
-      '';
-    };
-
-    adminToken = mkSecretOption {
-      name = "adminToken";
-      description = ''
-        This is the admin token used to boostrap keystone,
-        ie. to provision first resources.
-      '';
-    };
-
-    bootstrap = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Bootstrap the Keystone service by creating the service
-          tenant, an admin account and a public endpoint. This options
-          provides a ready-to-use admin account. This is only done at
-          the first Keystone execution by the systemd post start.
-
-          Note this option is a helper for setting up development or
-          testing environments.
-        '';
-      };
-
-      endpointPublic = mkOption {
-        type = types.str;
-        default = "http://localhost:5000/v2.0";
-        description = ''
-          The public identity endpoint. The link <link
-          xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
-          create keystone endpoint</link> provides more informations
-          about that.
-        '';
-      };
-
-      adminUsername = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          A keystone admin username.
-        '';
-      };
-
-      adminPassword = mkSecretOption {
-        name = "keystoneAdminPassword";
-        description = ''
-          The keystone admin user's password.
-        '';
-      };
-
-      adminTenant = mkOption {
-        type = types.str;
-        default = "admin";
-        description = ''
-          A keystone admin tenant name.
-        '';
-      };
-    };
-
-    database = {
-      host = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = ''
-          Host of the database.
-        '';
-      };
-
-      name = mkOption {
-        type = types.str;
-        default = "keystone";
-        description = ''
-          Name of the existing database.
-        '';
-      };
-
-      user = mkOption {
-        type = types.str;
-        default = "keystone";
-        description = ''
-          The database user. The user must exist and has access to
-          the specified database.
-        '';
-      };
-      password = mkSecretOption {
-        name = "mysqlPassword";
-        description = "The database user's password";};
-    };
-  };
-
-  config = mkIf cfg.enable {
-    # Note: when changing the default, make it conditional on
-    # ‘system.stateVersion’ to maintain compatibility with existing
-    # systems!
-    virtualisation.openstack.keystone.package = mkDefault pkgs.keystone;
-
-    users.extraUsers = [{
-      name = "keystone";
-      group = "keystone";
-      uid = config.ids.uids.keystone;
-    }];
-    users.extraGroups = [{
-      name = "keystone";
-      gid = config.ids.gids.keystone;
-    }];
-
-    systemd.services.keystone-all = {
-        description = "OpenStack Keystone Daemon";
-        after = [ "network.target"];
-        path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
-        wantedBy = [ "multi-user.target" ];
-        preStart = ''
-          mkdir -m 755 -p /var/lib/keystone
-
-          cp ${keystoneConfTpl} ${keystoneConf};
-          chown keystone:keystone ${keystoneConf};
-          chmod 640 ${keystoneConf}
-
-          ${replaceSecret cfg.database.password keystoneConf}
-          ${replaceSecret cfg.adminToken keystoneConf}
-
-          # Initialise the database
-          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
-          # Set up the keystone's PKI infrastructure
-          ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} pki_setup --keystone-user keystone --keystone-group keystone
-        '';
-        postStart = optionalString cfg.bootstrap.enable ''
-          set -eu
-          # Wait until the keystone is available for use
-          count=0
-          while ! curl --fail -s  http://localhost:35357/v2.0 > /dev/null 
-          do
-              if [ $count -eq 30 ]
-              then
-                  echo "Tried 30 times, giving up..."
-                  exit 1
-              fi
-
-              echo "Keystone not yet started. Waiting for 1 second..."
-              count=$((count++))
-              sleep 1
-          done
-
-          # We use the service token to create a first admin user
-          export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
-          export OS_SERVICE_TOKEN=${getSecret cfg.adminToken}
-
-          # If the tenant service doesn't exist, we consider
-          # keystone is not initialized
-          if ! keystone tenant-get service
-          then
-              keystone tenant-create --name service
-              keystone tenant-create --name ${cfg.bootstrap.adminTenant}
-              keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${getSecret cfg.bootstrap.adminPassword}
-              keystone role-create --name admin
-              keystone role-create --name Member
-              keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
-              keystone service-create --type identity --name keystone
-              ID=$(keystone service-get keystone | awk '/ id / { print $4 }')
-              keystone endpoint-create --region RegionOne --service $ID --publicurl ${cfg.bootstrap.endpointPublic} --adminurl http://localhost:35357/v2.0 --internalurl http://localhost:5000/v2.0
-          fi
-        '';
-        serviceConfig = {
-          PermissionsStartOnly = true; # preStart must be run as root
-          TimeoutStartSec = "600"; # 10min for initial db migrations
-          User = "keystone";
-          Group = "keystone";
-          ExecStart = "${cfg.package}/bin/keystone-all --config-file=${keystoneConf}";
-        };
-      };
-  };
-}
diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix
index 4218a3840fc1..38b138e06326 100644
--- a/nixos/modules/virtualisation/openvswitch.nix
+++ b/nixos/modules/virtualisation/openvswitch.nix
@@ -169,7 +169,7 @@ in {
         mkdir -p ${runDir}/ipsec/{etc/racoon,etc/init.d/,usr/sbin/}
         ln -fs ${pkgs.ipsecTools}/bin/setkey ${runDir}/ipsec/usr/sbin/setkey
         ln -fs ${pkgs.writeScript "racoon-restart" ''
-        #!${pkgs.stdenv.shell}
+        #!${pkgs.runtimeShell}
         /var/run/current-system/sw/bin/systemctl $1 racoon
         ''} ${runDir}/ipsec/etc/init.d/racoon
       '';
diff --git a/nixos/modules/virtualisation/parallels-guest.nix b/nixos/modules/virtualisation/parallels-guest.nix
index bd85973ee561..36ca7f356d44 100644
--- a/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixos/modules/virtualisation/parallels-guest.nix
@@ -3,9 +3,7 @@
 with lib;
 
 let
-
-  prl-tools = config.boot.kernelPackages.prl-tools;
-
+  prl-tools = config.hardware.parallels.package;
 in
 
 {
@@ -22,12 +20,31 @@ in
         '';
       };
 
+      autoMountShares = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Control prlfsmountd service. When this service is running, shares can not be manually
+          mounted through `mount -t prl_fs ...` as this service will remount and trample any set options.
+          Recommended to enable for simple file sharing, but extended share use such as for code should
+          disable this to manually mount shares.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = config.boot.kernelPackages.prl-tools;
+        defaultText = "config.boot.kernelPackages.prl-tools";
+        example = literalExample "config.boot.kernelPackages.prl-tools";
+        description = ''
+          Defines which package to use for prl-tools. Override to change the version.
+        '';
+      };
     };
 
   };
 
   config = mkIf config.hardware.parallels.enable {
-
     services.xserver = {
       drivers = singleton
         { name = "prlvideo"; modules = [ prl-tools ]; libPath = [ prl-tools ]; };
@@ -55,7 +72,7 @@ in
 
     boot.extraModulePackages = [ prl-tools ];
 
-    boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" "acpi_memhotplug" ];
+    boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" ];
 
     services.timesyncd.enable = false;
 
@@ -68,7 +85,7 @@ in
       };
     };
 
-    systemd.services.prlfsmountd = {
+    systemd.services.prlfsmountd = mkIf config.hardware.parallels.autoMountShares {
       description = "Parallels Shared Folders Daemon";
       wantedBy = [ "multi-user.target" ];
       serviceConfig = rec {
@@ -89,5 +106,50 @@ in
       };
     };
 
+    systemd.user.services = {
+      prlcc = {
+        description = "Parallels Control Center";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlcc";
+        };
+      };
+      prldnd = {
+        description = "Parallels Control Center";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prldnd";
+        };
+      };
+      prl_wmouse_d  = {
+        description = "Parallels Walking Mouse Daemon";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prl_wmouse_d";
+        };
+      };
+      prlcp = {
+        description = "Parallels CopyPaste Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlcp";
+        };
+      };
+      prlsga = {
+        description = "Parallels Shared Guest Applications Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlsga";
+        };
+      };
+      prlshprof = {
+        description = "Parallels Shared Profile Tool";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = "${prl-tools}/bin/prlshprof";
+        };
+      };
+    };
+
   };
 }
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 3c89ca68113b..45325c6b0d8d 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -10,6 +10,7 @@
 { config, lib, pkgs, ... }:
 
 with lib;
+with import ../../lib/qemu-flags.nix { inherit pkgs; };
 
 let
 
@@ -23,13 +24,13 @@ let
   cfg = config.virtualisation;
 
   qemuGraphics = if cfg.graphics then "" else "-nographic";
-  kernelConsole = if cfg.graphics then "" else "console=ttyS0";
+  kernelConsole = if cfg.graphics then "" else "console=${qemuSerialDevice}";
   ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ];
 
   # Shell script to start the VM.
   startVM =
     ''
-      #! ${pkgs.stdenv.shell}
+      #! ${pkgs.runtimeShell}
 
       NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
 
@@ -72,11 +73,10 @@ let
       '')}
 
       # Start QEMU.
-      exec ${qemu}/bin/qemu-kvm \
+      exec ${qemuBinary qemu} \
           -name ${vmName} \
           -m ${toString config.virtualisation.memorySize} \
           -smp ${toString config.virtualisation.cores} \
-          ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
           ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
           -virtfs local,path=/nix/store,security_model=none,mount_tag=store \
           -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
@@ -98,7 +98,7 @@ let
           ${qemuGraphics} \
           ${toString config.virtualisation.qemu.options} \
           $QEMU_OPTS \
-          $@
+          "$@"
     '';
 
 
@@ -319,8 +319,8 @@ in
       networkingOptions =
         mkOption {
           default = [
-            "-net nic,vlan=0,model=virtio"
-            "-net user,vlan=0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
+            "-net nic,netdev=user.0,model=virtio"
+            "-netdev user,id=user.0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
           ];
           type = types.listOf types.str;
           description = ''
@@ -434,7 +434,11 @@ in
 
     virtualisation.pathsInNixDB = [ config.system.build.toplevel ];
 
-    virtualisation.qemu.options = [ "-vga std" "-usbdevice tablet" ];
+    # FIXME: Consolidate this one day.
+    virtualisation.qemu.options = mkMerge [
+      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ])
+      (mkIf (pkgs.stdenv.isArm || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ])
+    ];
 
     # Mount the host filesystem via 9P, and bind-mount the Nix store
     # of the host into our own filesystem.  We use mkVMOverride to
diff --git a/nixos/modules/virtualisation/virtualbox-host.nix b/nixos/modules/virtualisation/virtualbox-host.nix
index bb0c38bd4eb8..7413e12c8f3d 100644
--- a/nixos/modules/virtualisation/virtualbox-host.nix
+++ b/nixos/modules/virtualisation/virtualbox-host.nix
@@ -124,7 +124,7 @@ in
           '';
       };
 
-    networking.interfaces.vboxnet0.ip4 = [ { address = "192.168.56.1"; prefixLength = 24; } ];
+    networking.interfaces.vboxnet0.ipv4.addresses = [{ address = "192.168.56.1"; prefixLength = 24; }];
     # Make sure NetworkManager won't assume this interface being up
     # means we have internet access.
     networking.networkmanager.unmanaged = ["vboxnet0"];
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index d68b3bb73904..64f145f77ca3 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -8,8 +8,6 @@ let
 
 in {
 
-  imports = [ ./grow-partition.nix ];
-
   options = {
     virtualbox = {
       baseImageSize = mkOption {
@@ -23,12 +21,11 @@ in {
   };
 
   config = {
-
     system.build.virtualBoxOVA = import ../../lib/make-disk-image.nix {
-      name = "nixos-ova-${config.system.nixosLabel}-${pkgs.stdenv.system}";
+      name = "nixos-ova-${config.system.nixos.label}-${pkgs.stdenv.system}";
 
       inherit pkgs lib config;
-      partitioned = true;
+      partitionTableType = "legacy";
       diskSize = cfg.baseImageSize;
 
       postVM =
@@ -40,7 +37,7 @@ in {
           VBoxManage internalcommands createrawvmdk -filename disk.vmdk -rawdisk $diskImage
 
           echo "creating VirtualBox VM..."
-          vmName="NixOS ${config.system.nixosLabel} (${pkgs.stdenv.system})"
+          vmName="NixOS ${config.system.nixos.label} (${pkgs.stdenv.system})"
           VBoxManage createvm --name "$vmName" --register \
             --ostype ${if pkgs.stdenv.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
           VBoxManage modifyvm "$vmName" \
@@ -56,7 +53,7 @@ in {
 
           echo "exporting VirtualBox VM..."
           mkdir -p $out
-          fn="$out/nixos-${config.system.nixosLabel}-${pkgs.stdenv.system}.ova"
+          fn="$out/nixos-${config.system.nixos.label}-${pkgs.stdenv.system}.ova"
           VBoxManage export "$vmName" --output "$fn"
 
           rm -v $diskImage
@@ -71,6 +68,7 @@ in {
       autoResize = true;
     };
 
+    boot.growPartition = true;
     boot.loader.grub.device = "/dev/sda";
 
     virtualisation.virtualbox.guest.enable = true;
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index c7656bc309c0..cf57868acef9 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -35,24 +35,19 @@ in
       description = ''
         The package used for Xen binary.
       '';
+      relatedPackages = [ "xen" "xen-light" ];
     };
 
-    virtualisation.xen.qemu = mkOption {
-      type = types.path;
-      defaultText = "\${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-      example = literalExample "''${pkgs.qemu_xen-light}/bin/qemu-system-i386";
-      description = ''
-        The qemu binary to use for Dom-0 backend.
-      '';
-    };
-
-    virtualisation.xen.qemu-package = mkOption {
+    virtualisation.xen.package-qemu = mkOption {
       type = types.package;
       defaultText = "pkgs.xen";
       example = literalExample "pkgs.qemu_xen-light";
       description = ''
-        The package with qemu binaries for xendomains.
+        The package with qemu binaries for dom0 qemu and xendomains.
       '';
+      relatedPackages = [ "xen"
+                          { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; }
+                        ];
     };
 
     virtualisation.xen.bootParams =
@@ -158,8 +153,7 @@ in
     } ];
 
     virtualisation.xen.package = mkDefault pkgs.xen;
-    virtualisation.xen.qemu = mkDefault "${pkgs.xen}/lib/xen/bin/qemu-system-i386";
-    virtualisation.xen.qemu-package = mkDefault pkgs.xen;
+    virtualisation.xen.package-qemu = mkDefault pkgs.xen;
     virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored";
 
     environment.systemPackages = [ cfg.package ];
@@ -247,6 +241,12 @@ in
           '';
           target = "default/xendomains";
         }
+      ]
+      ++ lib.optionals (builtins.compareVersions cfg.package.version "4.10" >= 0) [
+        # in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start
+        { source = "${cfg.package}/etc/xen/oxenstored.conf";
+          target = "xen/oxenstored.conf";
+        }
       ];
 
     # Xen provides udev rules.
@@ -268,7 +268,7 @@ in
         mkdir -p /var/lib/xen # so we create them here unconditionally.
         grep -q control_d /proc/xen/capabilities
         '';
-      serviceConfig = if cfg.package.version < "4.8" then
+      serviceConfig = if (builtins.compareVersions cfg.package.version "4.8" < 0) then
         { ExecStart = ''
             ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
             '';
@@ -281,7 +281,7 @@ in
           NotifyAccess    = "all";
         };
       postStart = ''
-        ${optionalString (cfg.package.version < "4.8") ''
+        ${optionalString (builtins.compareVersions cfg.package.version "4.8" < 0) ''
           time=0
           timeout=30
           # Wait for xenstored to actually come up, timing out after 30 seconds
@@ -326,7 +326,7 @@ in
       serviceConfig = {
         ExecStart = ''
           ${cfg.package}/bin/xenconsoled\
-            ${optionalString ((cfg.package.version >= "4.8")) " -i"}\
+            ${optionalString ((builtins.compareVersions cfg.package.version "4.8" >= 0)) " -i"}\
             ${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
           '';
       };
@@ -339,7 +339,8 @@ in
       after = [ "xen-console.service" ];
       requires = [ "xen-store.service" ];
       serviceConfig.ExecStart = ''
-        ${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \
+        ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \
+           -xen-attach -xen-domid 0 -name dom0 -M xenpv \
            -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
         '';
     };
@@ -448,7 +449,7 @@ in
       before = [ "dhcpd.service" ];
       restartIfChanged = false;
       serviceConfig.RemainAfterExit = "yes";
-      path = [ cfg.package cfg.qemu-package ];
+      path = [ cfg.package cfg.package-qemu ];
       environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
       preStart = "mkdir -p /var/lock/subsys -m 755";
       serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";