about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorOliver Charles <ollie@ocharles.org.uk>2014-04-20 19:17:05 +0100
committerOliver Charles <ollie@ocharles.org.uk>2014-04-20 19:17:05 +0100
commit42ae633445a72c4673b4abc3e214a2ddc8227724 (patch)
tree0b19dfa036d81786f15348a60d31dbdefbacf986 /nixos
parent41775167ac69a001f40c11e2ee9576887e17b8b4 (diff)
parent31a94915d2532273e3630206a9e13f2e49ce0775 (diff)
downloadnixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar.gz
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar.bz2
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar.lz
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar.xz
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.tar.zst
nixlib-42ae633445a72c4673b4abc3e214a2ddc8227724.zip
Merge branch 'master' into dbus-switch-to-configuration
Conflicts:
	nixos/modules/system/activation/switch-to-configuration.pl
Diffstat (limited to 'nixos')
-rw-r--r--nixos/.topmsg1
-rw-r--r--nixos/doc/manual/configuration.xml55
-rw-r--r--nixos/doc/manual/containers.xml242
-rw-r--r--nixos/doc/manual/development.xml393
-rw-r--r--nixos/doc/manual/installation.xml73
-rw-r--r--nixos/doc/manual/manual.xml10
-rw-r--r--nixos/doc/manual/options-to-docbook.xsl14
-rw-r--r--nixos/doc/manual/release-notes.xml84
-rw-r--r--nixos/doc/manual/running.xml12
-rw-r--r--nixos/doc/manual/troubleshooting.xml13
-rw-r--r--nixos/lib/eval-config.nix9
-rw-r--r--nixos/lib/test-driver/Machine.pm4
-rw-r--r--nixos/lib/test-driver/test-driver.pl7
-rw-r--r--nixos/lib/testing.nix138
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-ebs-amis.py2
-rwxr-xr-xnixos/maintainers/scripts/gce/create-gce.sh14
-rw-r--r--nixos/modules/config/fonts/corefonts.nix4
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix4
-rw-r--r--nixos/modules/config/fonts/fontdir.nix4
-rw-r--r--nixos/modules/config/fonts/fonts.nix4
-rw-r--r--nixos/modules/config/fonts/ghostscript.nix4
-rw-r--r--nixos/modules/config/gnu.nix6
-rw-r--r--nixos/modules/config/i18n.nix17
-rw-r--r--nixos/modules/config/krb5.nix4
-rw-r--r--nixos/modules/config/ldap.nix4
-rw-r--r--nixos/modules/config/networking.nix4
-rw-r--r--nixos/modules/config/no-x-libs.nix25
-rw-r--r--nixos/modules/config/nsswitch.nix4
-rw-r--r--nixos/modules/config/power-management.nix13
-rw-r--r--nixos/modules/config/pulseaudio.nix6
-rw-r--r--nixos/modules/config/shells-environment.nix4
-rw-r--r--nixos/modules/config/swap.nix4
-rw-r--r--nixos/modules/config/sysctl.nix35
-rw-r--r--nixos/modules/config/system-path.nix9
-rw-r--r--nixos/modules/config/timezone.nix4
-rw-r--r--nixos/modules/config/unix-odbc-drivers.nix4
-rw-r--r--nixos/modules/config/users-groups.nix482
-rw-r--r--nixos/modules/hardware/all-firmware.nix4
-rw-r--r--nixos/modules/hardware/cpu/amd-microcode.nix4
-rw-r--r--nixos/modules/hardware/cpu/intel-microcode.nix4
-rw-r--r--nixos/modules/hardware/network/b43.nix4
-rw-r--r--nixos/modules/hardware/opengl.nix (renamed from nixos/modules/services/x11/mesa.nix)59
-rw-r--r--nixos/modules/hardware/pcmcia.nix4
-rw-r--r--nixos/modules/hardware/video/bumblebee.nix41
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix6
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix13
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-pc.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix7
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball.nix4
-rw-r--r--nixos/modules/installer/scan/detected.nix4
-rw-r--r--nixos/modules/installer/scan/not-detected.nix4
-rw-r--r--nixos/modules/installer/tools/nixos-checkout.nix4
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl40
-rw-r--r--nixos/modules/installer/tools/nixos-option.sh2
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh76
-rw-r--r--nixos/modules/installer/tools/tools.nix4
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix6
-rw-r--r--nixos/modules/misc/assertions.nix4
-rw-r--r--nixos/modules/misc/check-config.nix4
-rw-r--r--nixos/modules/misc/crashdump.nix4
-rw-r--r--nixos/modules/misc/ids.nix37
-rw-r--r--nixos/modules/misc/locate.nix4
-rw-r--r--nixos/modules/misc/nixpkgs.nix4
-rw-r--r--nixos/modules/misc/version.nix4
-rw-r--r--nixos/modules/module-list.nix39
-rw-r--r--nixos/modules/profiles/clone-config.nix4
-rw-r--r--nixos/modules/profiles/demo.nix2
-rw-r--r--nixos/modules/profiles/headless.nix4
-rw-r--r--nixos/modules/profiles/installation-device.nix6
-rw-r--r--nixos/modules/profiles/minimal.nix7
-rw-r--r--nixos/modules/profiles/qemu-guest.nix10
-rw-r--r--nixos/modules/programs/atop.nix4
-rw-r--r--nixos/modules/programs/bash/bash.nix4
-rw-r--r--nixos/modules/programs/bash/command-not-found.nix4
-rw-r--r--nixos/modules/programs/environment.nix9
-rw-r--r--nixos/modules/programs/shadow.nix9
-rw-r--r--nixos/modules/programs/shell.nix56
-rw-r--r--nixos/modules/programs/ssh.nix42
-rw-r--r--nixos/modules/programs/ssmtp.nix4
-rw-r--r--nixos/modules/programs/venus.nix4
-rw-r--r--nixos/modules/programs/virtualbox.nix4
-rw-r--r--nixos/modules/programs/wvdial.nix4
-rw-r--r--nixos/modules/programs/zsh/zsh.nix7
-rw-r--r--nixos/modules/rename.nix28
-rw-r--r--nixos/modules/security/apparmor-suid.nix4
-rw-r--r--nixos/modules/security/apparmor.nix45
-rw-r--r--nixos/modules/security/ca.nix9
-rw-r--r--nixos/modules/security/duosec.nix198
-rw-r--r--nixos/modules/security/grsecurity.nix443
-rw-r--r--nixos/modules/security/pam.nix8
-rw-r--r--nixos/modules/security/pam_usb.nix4
-rw-r--r--nixos/modules/security/polkit.nix7
-rw-r--r--nixos/modules/security/prey.nix4
-rw-r--r--nixos/modules/security/rngd.nix4
-rw-r--r--nixos/modules/security/rtkit.nix4
-rw-r--r--nixos/modules/security/setuid-wrapper.c14
-rw-r--r--nixos/modules/security/setuid-wrappers.nix15
-rw-r--r--nixos/modules/security/sudo.nix4
-rw-r--r--nixos/modules/services/amqp/activemq/default.nix4
-rw-r--r--nixos/modules/services/amqp/rabbitmq.nix73
-rw-r--r--nixos/modules/services/audio/alsa.nix4
-rw-r--r--nixos/modules/services/audio/fuppes.nix4
-rw-r--r--nixos/modules/services/audio/mpd.nix4
-rw-r--r--nixos/modules/services/backup/almir.nix7
-rw-r--r--nixos/modules/services/backup/bacula.nix4
-rw-r--r--nixos/modules/services/backup/mysql-backup.nix4
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix4
-rw-r--r--nixos/modules/services/backup/rsnapshot.nix4
-rw-r--r--nixos/modules/services/backup/sitecopy-backup.nix4
-rw-r--r--nixos/modules/services/backup/tarsnap.nix204
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix119
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/slave.nix68
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix4
-rw-r--r--nixos/modules/services/databases/4store.nix4
-rw-r--r--nixos/modules/services/databases/couchdb.nix174
-rw-r--r--nixos/modules/services/databases/firebird.nix5
-rw-r--r--nixos/modules/services/databases/memcached.nix4
-rw-r--r--nixos/modules/services/databases/monetdb.nix88
-rw-r--r--nixos/modules/services/databases/mongodb.nix5
-rw-r--r--nixos/modules/services/databases/mysql.nix30
-rw-r--r--nixos/modules/services/databases/mysql55.nix248
-rw-r--r--nixos/modules/services/databases/openldap.nix4
-rw-r--r--nixos/modules/services/databases/postgresql.nix8
-rw-r--r--nixos/modules/services/databases/redis.nix5
-rw-r--r--nixos/modules/services/databases/virtuoso.nix4
-rw-r--r--nixos/modules/services/desktops/accountservice.nix40
-rw-r--r--nixos/modules/services/desktops/gnome3/at-spi2-core.nix39
-rw-r--r--nixos/modules/services/desktops/gnome3/evolution-data-server.nix39
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-keyring.nix40
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix39
-rw-r--r--nixos/modules/services/desktops/gnome3/gnome-user-share.nix42
-rw-r--r--nixos/modules/services/desktops/gnome3/sushi.nix38
-rw-r--r--nixos/modules/services/desktops/gnome3/tracker.nix39
-rw-r--r--nixos/modules/services/desktops/telepathy.nix39
-rw-r--r--nixos/modules/services/games/ghost-one.nix4
-rw-r--r--nixos/modules/services/games/minecraft-server.nix51
-rw-r--r--nixos/modules/services/hardware/80-net-name-slot.rules13
-rw-r--r--nixos/modules/services/hardware/acpid.nix4
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix4
-rw-r--r--nixos/modules/services/hardware/pcscd.nix40
-rw-r--r--nixos/modules/services/hardware/pommed.nix4
-rw-r--r--nixos/modules/services/hardware/sane.nix4
-rw-r--r--nixos/modules/services/hardware/thinkfan.nix4
-rw-r--r--nixos/modules/services/hardware/udev.nix12
-rw-r--r--nixos/modules/services/hardware/udisks.nix4
-rw-r--r--nixos/modules/services/hardware/udisks2.nix6
-rw-r--r--nixos/modules/services/hardware/upower.nix4
-rw-r--r--nixos/modules/services/logging/klogd.nix4
-rw-r--r--nixos/modules/services/logging/logcheck.nix11
-rw-r--r--nixos/modules/services/logging/logrotate.nix4
-rw-r--r--nixos/modules/services/logging/logstash.nix4
-rw-r--r--nixos/modules/services/logging/rsyslogd.nix4
-rw-r--r--nixos/modules/services/logging/syslogd.nix4
-rw-r--r--nixos/modules/services/mail/dovecot.nix4
-rw-r--r--nixos/modules/services/mail/freepops.nix4
-rw-r--r--nixos/modules/services/mail/mail.nix4
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix9
-rw-r--r--nixos/modules/services/mail/postfix.nix4
-rw-r--r--nixos/modules/services/mail/spamassassin.nix4
-rw-r--r--nixos/modules/services/misc/autofs.nix4
-rw-r--r--nixos/modules/services/misc/cgminer.nix5
-rw-r--r--nixos/modules/services/misc/dictd.nix63
-rw-r--r--nixos/modules/services/misc/disnix.nix4
-rw-r--r--nixos/modules/services/misc/felix.nix4
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix4
-rw-r--r--nixos/modules/services/misc/gpsd.nix34
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix33
-rw-r--r--nixos/modules/services/misc/nix-gc.nix4
-rw-r--r--nixos/modules/services/misc/nix-ssh-serve.nix45
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix4
-rw-r--r--nixos/modules/services/misc/rippled.nix314
-rw-r--r--nixos/modules/services/misc/rogue.nix4
-rw-r--r--nixos/modules/services/misc/svnserve.nix4
-rw-r--r--nixos/modules/services/misc/synergy.nix4
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix6
-rw-r--r--nixos/modules/services/monitoring/dd-agent.nix4
-rw-r--r--nixos/modules/services/monitoring/graphite.nix98
-rw-r--r--nixos/modules/services/monitoring/munin.nix6
-rw-r--r--nixos/modules/services/monitoring/nagios/default.nix4
-rw-r--r--nixos/modules/services/monitoring/smartd.nix4
-rw-r--r--nixos/modules/services/monitoring/statsd.nix8
-rw-r--r--nixos/modules/services/monitoring/systemhealth.nix4
-rw-r--r--nixos/modules/services/monitoring/ups.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-agent.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix4
-rw-r--r--nixos/modules/services/network-filesystems/drbd.nix4
-rw-r--r--nixos/modules/services/network-filesystems/nfsd.nix4
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix35
-rw-r--r--nixos/modules/services/network-filesystems/rsyncd.nix139
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix4
-rw-r--r--nixos/modules/services/networking/amuled.nix4
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix8
-rw-r--r--nixos/modules/services/networking/bind.nix4
-rw-r--r--nixos/modules/services/networking/bitlbee.nix4
-rw-r--r--nixos/modules/services/networking/btsync.nix280
-rw-r--r--nixos/modules/services/networking/chrony.nix4
-rw-r--r--nixos/modules/services/networking/cjdns.nix207
-rw-r--r--nixos/modules/services/networking/cntlm.nix6
-rw-r--r--nixos/modules/services/networking/connman.nix4
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix15
-rw-r--r--nixos/modules/services/networking/dhcpd.nix4
-rw-r--r--nixos/modules/services/networking/dnsmasq.nix4
-rw-r--r--nixos/modules/services/networking/ejabberd.nix4
-rw-r--r--nixos/modules/services/networking/firewall.nix81
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix4
-rw-r--r--nixos/modules/services/networking/freenet.nix4
-rw-r--r--nixos/modules/services/networking/git-daemon.nix6
-rw-r--r--nixos/modules/services/networking/gnunet.nix4
-rw-r--r--nixos/modules/services/networking/gogoclient.nix4
-rw-r--r--nixos/modules/services/networking/haproxy.nix4
-rw-r--r--nixos/modules/services/networking/hostapd.nix4
-rw-r--r--nixos/modules/services/networking/ifplugd.nix4
-rw-r--r--nixos/modules/services/networking/iodined.nix4
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/default.nix4
-rw-r--r--nixos/modules/services/networking/kippo.nix7
-rw-r--r--nixos/modules/services/networking/minidlna.nix4
-rw-r--r--nixos/modules/services/networking/murmur.nix253
-rw-r--r--nixos/modules/services/networking/nat.nix65
-rw-r--r--nixos/modules/services/networking/networkmanager.nix19
-rw-r--r--nixos/modules/services/networking/ngircd.nix58
-rw-r--r--nixos/modules/services/networking/notbit.nix93
-rw-r--r--nixos/modules/services/networking/ntopng.nix4
-rw-r--r--nixos/modules/services/networking/ntpd.nix11
-rw-r--r--nixos/modules/services/networking/oidentd.nix4
-rw-r--r--nixos/modules/services/networking/openfire.nix4
-rw-r--r--nixos/modules/services/networking/openvpn.nix4
-rw-r--r--nixos/modules/services/networking/prayer.nix4
-rw-r--r--nixos/modules/services/networking/privoxy.nix4
-rw-r--r--nixos/modules/services/networking/quassel.nix4
-rw-r--r--nixos/modules/services/networking/radvd.nix4
-rw-r--r--nixos/modules/services/networking/rdnssd.nix4
-rw-r--r--nixos/modules/services/networking/rpcbind.nix4
-rw-r--r--nixos/modules/services/networking/sabnzbd.nix4
-rw-r--r--nixos/modules/services/networking/searx.nix75
-rw-r--r--nixos/modules/services/networking/spiped.nix212
-rw-r--r--nixos/modules/services/networking/ssh/lshd.nix4
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix7
-rw-r--r--nixos/modules/services/networking/supybot.nix4
-rw-r--r--nixos/modules/services/networking/syncthing.nix78
-rw-r--r--nixos/modules/services/networking/tcpcrypt.nix4
-rw-r--r--nixos/modules/services/networking/tftpd.nix4
-rw-r--r--nixos/modules/services/networking/unbound.nix4
-rw-r--r--nixos/modules/services/networking/vsftpd.nix12
-rw-r--r--nixos/modules/services/networking/wakeonlan.nix4
-rw-r--r--nixos/modules/services/networking/websockify.nix4
-rw-r--r--nixos/modules/services/networking/wicd.nix4
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix4
-rw-r--r--nixos/modules/services/networking/xinetd.nix4
-rw-r--r--nixos/modules/services/printing/cupsd.nix8
-rw-r--r--nixos/modules/services/scheduling/atd.nix4
-rw-r--r--nixos/modules/services/scheduling/cron.nix4
-rw-r--r--nixos/modules/services/scheduling/fcron.nix4
-rw-r--r--nixos/modules/services/search/elasticsearch.nix54
-rw-r--r--nixos/modules/services/search/solr.nix147
-rw-r--r--nixos/modules/services/security/clamav.nix4
-rw-r--r--nixos/modules/services/security/fail2ban.nix4
-rw-r--r--nixos/modules/services/security/fprot.nix78
-rw-r--r--nixos/modules/services/security/haveged.nix4
-rw-r--r--nixos/modules/services/security/tor.nix4
-rw-r--r--nixos/modules/services/security/torify.nix4
-rw-r--r--nixos/modules/services/security/torsocks.nix4
-rw-r--r--nixos/modules/services/system/dbus.nix4
-rw-r--r--nixos/modules/services/system/nscd.nix15
-rw-r--r--nixos/modules/services/torrent/deluge.nix4
-rw-r--r--nixos/modules/services/torrent/transmission.nix77
-rw-r--r--nixos/modules/services/ttys/agetty.nix107
-rw-r--r--nixos/modules/services/ttys/gpm.nix15
-rw-r--r--nixos/modules/services/ttys/kmscon.nix13
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix37
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mediawiki.nix24
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix4
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/trac.nix4
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/zabbix.nix12
-rw-r--r--nixos/modules/services/web-servers/jboss/default.nix4
-rw-r--r--nixos/modules/services/web-servers/lighttpd/cgit.nix4
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix4
-rw-r--r--nixos/modules/services/web-servers/lighttpd/gitweb.nix4
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix30
-rw-r--r--nixos/modules/services/web-servers/phpfpm.nix84
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix4
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix4
-rw-r--r--nixos/modules/services/web-servers/winstone.nix129
-rw-r--r--nixos/modules/services/web-servers/zope2.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/e17.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix129
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde4.nix6
-rw-r--r--nixos/modules/services/x11/desktop-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/xterm.nix6
-rw-r--r--nixos/modules/services/x11/display-managers/auto.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix15
-rw-r--r--nixos/modules/services/x11/display-managers/kdm.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix8
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix11
-rw-r--r--nixos/modules/services/x11/hardware/multitouch.nix4
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix35
-rw-r--r--nixos/modules/services/x11/hardware/wacom.nix4
-rw-r--r--nixos/modules/services/x11/redshift.nix31
-rw-r--r--nixos/modules/services/x11/terminal-server.nix6
-rw-r--r--nixos/modules/services/x11/window-managers/awesome.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/compiz.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix5
-rw-r--r--nixos/modules/services/x11/window-managers/herbstluftwm.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/i3.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/icewm.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/metacity.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/twm.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/wmii.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/xfs.nix4
-rw-r--r--nixos/modules/services/x11/xserver.nix57
-rw-r--r--nixos/modules/system/activation/activation-script.nix4
-rw-r--r--nixos/modules/system/activation/no-clone.nix4
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl34
-rw-r--r--nixos/modules/system/activation/top-level.nix7
-rw-r--r--nixos/modules/system/boot/kernel.nix36
-rw-r--r--nixos/modules/system/boot/loader/efi.nix4
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix4
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix11
-rw-r--r--nixos/modules/system/boot/loader/grub/memtest.nix76
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py14
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot.nix4
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script.nix4
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix4
-rw-r--r--nixos/modules/system/boot/luksroot.nix305
-rw-r--r--nixos/modules/system/boot/modprobe.nix28
-rw-r--r--nixos/modules/system/boot/pbkdf2-sha512.c38
-rw-r--r--nixos/modules/system/boot/shutdown.nix4
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh8
-rw-r--r--nixos/modules/system/boot/stage-1.nix6
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh45
-rw-r--r--nixos/modules/system/boot/stage-2.nix7
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix90
-rw-r--r--nixos/modules/system/boot/systemd.nix404
-rw-r--r--nixos/modules/system/etc/etc.nix24
-rw-r--r--nixos/modules/system/etc/make-etc.sh4
-rw-r--r--nixos/modules/system/etc/setup-etc.pl8
-rw-r--r--nixos/modules/system/upstart/upstart.nix8
-rw-r--r--nixos/modules/tasks/cpu-freq.nix50
-rw-r--r--nixos/modules/tasks/filesystems.nix6
-rw-r--r--nixos/modules/tasks/filesystems/btrfs.nix4
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix4
-rw-r--r--nixos/modules/tasks/filesystems/reiserfs.nix4
-rw-r--r--nixos/modules/tasks/filesystems/vfat.nix4
-rw-r--r--nixos/modules/tasks/filesystems/xfs.nix4
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix225
-rw-r--r--nixos/modules/tasks/kbd.nix18
-rw-r--r--nixos/modules/tasks/lvm.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces.nix66
-rw-r--r--nixos/modules/tasks/scsi-link-power-management.nix4
-rw-r--r--nixos/modules/testing/minimal-kernel.nix2
-rw-r--r--nixos/modules/testing/service-runner.nix4
-rw-r--r--nixos/modules/testing/test-instrumentation.nix6
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix6
-rw-r--r--nixos/modules/virtualisation/container-config.nix91
-rw-r--r--nixos/modules/virtualisation/containers.nix244
-rw-r--r--nixos/modules/virtualisation/ec2-data.nix4
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix19
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix7
-rw-r--r--nixos/modules/virtualisation/nixos-container.pl238
-rw-r--r--nixos/modules/virtualisation/nova-image.nix4
-rw-r--r--nixos/modules/virtualisation/nova.nix4
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix12
-rw-r--r--nixos/modules/virtualisation/run-in-netns.c50
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix8
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix8
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix4
-rw-r--r--nixos/release-combined.nix4
-rw-r--r--nixos/release.nix50
-rw-r--r--nixos/tests/avahi.nix6
-rw-r--r--nixos/tests/bittorrent.nix17
-rw-r--r--nixos/tests/common/user-account.nix1
-rw-r--r--nixos/tests/containers.nix79
-rw-r--r--nixos/tests/default.nix40
-rw-r--r--nixos/tests/firefox.nix7
-rw-r--r--nixos/tests/firewall.nix7
-rw-r--r--nixos/tests/gnome3.nix29
-rw-r--r--nixos/tests/installer.nix40
-rw-r--r--nixos/tests/ipv6.nix5
-rw-r--r--nixos/tests/jenkins.nix41
-rw-r--r--nixos/tests/kde4.nix6
-rw-r--r--nixos/tests/kexec.nix4
-rw-r--r--nixos/tests/login.nix14
-rw-r--r--nixos/tests/logstash.nix11
-rw-r--r--nixos/tests/make-test.nix5
-rw-r--r--nixos/tests/misc.nix21
-rw-r--r--nixos/tests/mpich.nix6
-rw-r--r--nixos/tests/mumble.nix55
-rw-r--r--nixos/tests/munin.nix13
-rw-r--r--nixos/tests/mysql-replication.nix43
-rw-r--r--nixos/tests/mysql.nix8
-rw-r--r--nixos/tests/nat.nix7
-rw-r--r--nixos/tests/nfs.nix8
-rw-r--r--nixos/tests/openssh.nix5
-rw-r--r--nixos/tests/partition.nix4
-rw-r--r--nixos/tests/printing.nix11
-rw-r--r--nixos/tests/proxy.nix15
-rw-r--r--nixos/tests/quake3.nix9
-rw-r--r--nixos/tests/rabbitmq.nix17
-rw-r--r--nixos/tests/run-in-machine.nix8
-rw-r--r--nixos/tests/simple.nix4
-rw-r--r--nixos/tests/subversion.nix6
-rw-r--r--nixos/tests/tomcat.nix13
-rw-r--r--nixos/tests/trac.nix5
-rw-r--r--nixos/tests/udisks.nix56
-rw-r--r--nixos/tests/udisks2.nix56
-rw-r--r--nixos/tests/xfce.nix5
411 files changed, 9123 insertions, 2533 deletions
diff --git a/nixos/.topmsg b/nixos/.topmsg
deleted file mode 100644
index 9632e5926312..000000000000
--- a/nixos/.topmsg
+++ /dev/null
@@ -1 +0,0 @@
-improvements to vsftpd module
diff --git a/nixos/doc/manual/configuration.xml b/nixos/doc/manual/configuration.xml
index e6d7dee251af..0ffee826dc49 100644
--- a/nixos/doc/manual/configuration.xml
+++ b/nixos/doc/manual/configuration.xml
@@ -237,7 +237,7 @@ postgresql.package = pkgs.postgresql90;
 </section>
 
 
-<section><title>Abstractions</title>
+<section xml:id="sec-module-abstractions"><title>Abstractions</title>
 
 <para>If you find yourself repeating yourself over and over, it’s time
 to abstract.  Take, for instance, this Apache HTTP Server configuration:
@@ -399,7 +399,7 @@ of an expression to be spliced into a string.</para>
 </section>
 
 
-<section><title>Modularity</title>
+<section xml:id="sec-modularity"><title>Modularity</title>
 
 <para>The NixOS configuration mechanism is modular.  If your
 <filename>configuration.nix</filename> becomes too big, you can split
@@ -443,8 +443,20 @@ Note that both <filename>configuration.nix</filename> and
 define an option, NixOS will try to <emphasis>merge</emphasis> the
 definitions.  In the case of
 <option>environment.systemPackages</option>, that’s easy: the lists of
-packages can simply be concatenated.  For other types of options, a
-merge may not be possible: for instance, if two modules define
+packages can simply be concatenated.  The value in
+<filename>configuration.nix</filename> is merged last, so for
+list-type options, it will appear at the end of the merged list. If
+you want it to appear first, you can use <varname>mkBefore</varname>:
+
+<programlisting>
+boot.kernelModules = mkBefore [ "kvm-intel" ];
+</programlisting>
+
+This causes the <literal>kvm-intel</literal> kernel module to be
+loaded before any other kernel modules.</para>
+
+<para>For other types of options, a merge may not be possible. For
+instance, if two modules define
 <option>services.httpd.adminAddr</option>,
 <command>nixos-rebuild</command> will give an error:
 
@@ -526,7 +538,7 @@ nix-repl> map (x: x.hostName) config.services.httpd.virtualHosts
 </section>
 
 
-<section><title>Syntax summary</title>
+<section xml:id="sec-nix-syntax-summary"><title>Syntax summary</title>
 
 <para>Below is a summary of the most important syntactic constructs in
 the Nix expression language.  It’s not complete.  In particular, there
@@ -718,7 +730,7 @@ manual</link> for the rest.</para>
 
 <!--===============================================================-->
 
-<section><title>Package management</title>
+<section xml:id="sec-package-management"><title>Package management</title>
 
 <para>This section describes how to add additional packages to your
 system.  NixOS has two distinct styles of package management:
@@ -923,7 +935,7 @@ environment.systemPackages = [ (import ./my-hello.nix) ];
 </programlisting>
 where <filename>my-hello.nix</filename> contains:
 <programlisting>
-with &lt;nixpkgs> {}; # bring all of Nixpkgs into scope
+with import &lt;nixpkgs> {}; # bring all of Nixpkgs into scope
 
 stdenv.mkDerivation rec {
   name = "hello-2.8";
@@ -1025,7 +1037,6 @@ users.extraUsers.alice =
     home = "/home/alice";
     description = "Alice Foobar";
     extraGroups = [ "wheel" ];
-    isSystemUser = false;
     useDefaultShell = true;
     openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
   };
@@ -1172,7 +1183,7 @@ fileSystems."/".device = "/dev/mapper/crypted";
 
 <!--===============================================================-->
 
-<section><title>X Window System</title>
+<section xml:id="sec-x11"><title>X Window System</title>
 
 <para>The X Window System (X11) provides the basis of NixOS’ graphical
 user interface.  It can be enabled as follows:
@@ -1184,7 +1195,7 @@ driver from a set of X.org drivers (such as <literal>vesa</literal>
 and <literal>intel</literal>).  You can also specify a driver
 manually, e.g.
 <programlisting>
-services.xserver.videoDrivers = [ "r128" ];
+hardware.opengl.videoDrivers = [ "r128" ];
 </programlisting>
 to enable X.org’s <literal>xf86-video-r128</literal> driver.</para>
 
@@ -1227,7 +1238,7 @@ $ systemctl start display-manager.service
 has better 3D performance than the X.org drivers.  It is not enabled
 by default because it’s not free software.  You can enable it as follows:
 <programlisting>
-services.xserver.videoDrivers = [ "nvidia" ];
+hardware.opengl.videoDrivers = [ "nvidia" ];
 </programlisting>
 You may need to reboot after enabling this driver to prevent a clash
 with other kernel modules.</para>
@@ -1264,9 +1275,9 @@ services.xserver.synaptics.twoFingerScroll = true;
 
 <!--===============================================================-->
 
-<section><title>Networking</title>
+<section xml:id="sec-networking"><title>Networking</title>
 
-<section><title>Secure shell access</title>
+<section xml:id="sec-ssh"><title>Secure shell access</title>
 
 <para>Secure shell (SSH) access to your machine can be enabled by
 setting:
@@ -1294,7 +1305,7 @@ users.extraUsers.alice.openssh.authorizedKeys.keys =
 </section>
 
 
-<section><title>IPv4 configuration</title>
+<section xml:id="sec-ipv4"><title>IPv4 configuration</title>
 
 <para>By default, NixOS uses DHCP (specifically,
 <command>dhcpcd</command>) to automatically configure network
@@ -1337,7 +1348,7 @@ provide the host name.</para>
 </section>
 
 
-<section><title>IPv6 configuration</title>
+<section xml:id="sec-ipv6"><title>IPv6 configuration</title>
 
 <para>IPv6 is enabled by default.  Stateless address autoconfiguration
 is used to automatically assign IPv6 addresses to all interfaces.  You
@@ -1352,17 +1363,19 @@ networking.enableIPv6 = false;
 </section>
 
 
-<section><title>Firewall</title>
+<section xml:id="sec-firewall"><title>Firewall</title>
 
 <para>NixOS has a simple stateful firewall that blocks incoming
 connections and other unexpected packets.  The firewall applies to
-both IPv4 and IPv6 traffic.  It can be enabled as follows:
+both IPv4 and IPv6 traffic. It is enabled by default. It can be
+disabled as follows:
 
 <programlisting>
-networking.firewall.enable = true;
+networking.firewall.enable = false;
 </programlisting>
 
-You can open specific TCP ports to the outside world:
+If the firewall is enabled, you can open specific TCP ports to the
+outside world:
 
 <programlisting>
 networking.firewall.allowedTCPPorts = [ 80 443 ];
@@ -1384,7 +1397,7 @@ always allowed.)</para>
 </section>
 
 
-<section><title>Wireless networks</title>
+<section xml:id="sec-wireless"><title>Wireless networks</title>
 
 <para>
 NixOS will start wpa_supplicant for you if you enable this setting:
@@ -1445,7 +1458,7 @@ networking.localCommands =
 
 <!--===============================================================-->
 
-<section><title>Linux kernel</title>
+<section xml:id="sec-kernel-config"><title>Linux kernel</title>
 
 <para>You can override the Linux kernel and associated packages using
 the option <option>boot.kernelPackages</option>.  For instance, this
diff --git a/nixos/doc/manual/containers.xml b/nixos/doc/manual/containers.xml
new file mode 100644
index 000000000000..b8f170fc614f
--- /dev/null
+++ b/nixos/doc/manual/containers.xml
@@ -0,0 +1,242 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="ch-containers">
+
+<title>Containers</title>
+
+<para>NixOS allows you to easily run other NixOS instances as
+<emphasis>containers</emphasis>. Containers are a light-weight
+approach to virtualisation that runs software in the container at the
+same speed as in the host system. NixOS containers share the Nix store
+of the host, making container creation very efficient.</para>
+
+<warning><para>Currently, NixOS containers are not perfectly isolated
+from the host system. This means that a user with root access to the
+container can do things that affect the host. So you should not give
+container root access to untrusted users.</para></warning>
+
+<para>NixOS containers can be created in two ways: imperatively, using
+the command <command>nixos-container</command>, and declaratively, by
+specifying them in your <filename>configuration.nix</filename>. The
+declarative approach implies that containers get upgraded along with
+your host system when you run <command>nixos-rebuild</command>, which
+is often not what you want. By contrast, in the imperative approach,
+containers are configured and updated independently from the host
+system.</para>
+
+
+<section><title>Imperative container management</title>
+
+<para>We’ll cover imperative container management using
+<command>nixos-container</command> first. You create a container with
+identifier <literal>foo</literal> as follows:
+
+<screen>
+$ nixos-container create foo
+</screen>
+
+This creates the container’s root directory in
+<filename>/var/lib/containers/foo</filename> and a small configuration
+file in <filename>/etc/containers/foo.conf</filename>. It also builds
+the container’s initial system configuration and stores it in
+<filename>/nix/var/nix/profiles/per-container/foo/system</filename>. You
+can modify the initial configuration of the container on the command
+line. For instance, to create a container that has
+<command>sshd</command> running, with the given public key for
+<literal>root</literal>:
+
+<screen>
+$ nixos-container create foo --config 'services.openssh.enable = true; \
+  users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-dss AAAAB3N…"];'
+</screen>
+
+</para>
+
+<para>Creating a container does not start it. To start the container,
+run:
+
+<screen>
+$ nixos-container start foo
+</screen>
+
+This command will return as soon as the container has booted and has
+reached <literal>multi-user.target</literal>. On the host, the
+container runs within a systemd unit called
+<literal>container@<replaceable>container-name</replaceable>.service</literal>.
+Thus, if something went wrong, you can get status info using
+<command>systemctl</command>:
+
+<screen>
+$ systemctl status container@foo
+</screen>
+
+</para>
+
+<para>If the container has started succesfully, you can log in as
+root using the <command>root-login</command> operation:
+
+<screen>
+$ nixos-container root-login foo
+[root@foo:~]#
+</screen>
+
+Note that only root on the host can do this (since there is no
+authentication).  You can also get a regular login prompt using the
+<command>login</command> operation, which is available to all users on
+the host:
+
+<screen>
+$ nixos-container login foo
+foo login: alice
+Password: ***
+</screen>
+
+With <command>nixos-container run</command>, you can execute arbitrary
+commands in the container:
+
+<screen>
+$ nixos-container run foo -- uname -a
+Linux foo 3.4.82 #1-NixOS SMP Thu Mar 20 14:44:05 UTC 2014 x86_64 GNU/Linux
+</screen>
+
+</para>
+
+<para>There are several ways to change the configuration of the
+container. First, on the host, you can edit
+<literal>/var/lib/container/<replaceable>name</replaceable>/etc/nixos/configuration.nix</literal>,
+and run
+
+<screen>
+$ nixos-container update foo
+</screen>
+
+This will build and activate the new configuration. You can also
+specify a new configuration on the command line:
+
+<screen>
+$ nixos-container update foo --config 'services.httpd.enable = true; \
+  services.httpd.adminAddr = "foo@example.org";'
+
+$ curl http://$(nixos-container show-ip foo)/
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">…
+</screen>
+
+However, note that this will overwrite the container’s
+<filename>/etc/nixos/configuration.nix</filename>.</para>
+
+<para>Alternatively, you can change the configuration from within the
+container itself by running <command>nixos-rebuild switch</command>
+inside the container. Note that the container by default does not have
+a copy of the NixOS channel, so you should run <command>nix-channel
+--update</command> first.</para>
+
+<para>Containers can be stopped and started using
+<literal>nixos-container stop</literal> and <literal>nixos-container
+start</literal>, respectively, or by using
+<command>systemctl</command> on the container’s service unit. To
+destroy a container, including its file system, do
+
+<screen>
+$ nixos-container destroy foo
+</screen>
+
+</para>
+
+</section>
+
+
+<section><title>Declarative container specification</title>
+
+<para>You can also specify containers and their configuration in the
+host’s <filename>configuration.nix</filename>.  For example, the
+following specifies that there shall be a container named
+<literal>database</literal> running PostgreSQL:
+
+<programlisting>
+containers.database =
+  { config =
+      { config, pkgs, ... }:
+      { services.postgresql.enable = true;
+        services.postgresql.package = pkgs.postgresql92;
+      };
+  };
+</programlisting>
+
+If you run <literal>nixos-rebuild switch</literal>, the container will
+be built and started. If the container was already running, it will be
+updated in place, without rebooting.</para>
+
+<para>By default, declarative containers share the network namespace
+of the host, meaning that they can listen on (privileged)
+ports. However, they cannot change the network configuration. You can
+give a container its own network as follows:
+
+<programlisting>
+containers.database =
+  { privateNetwork = true;
+    hostAddress = "192.168.100.10";
+    localAddress = "192.168.100.11";
+  };
+</programlisting>
+
+This gives the container a private virtual Ethernet interface with IP
+address <literal>192.168.100.11</literal>, which is hooked up to a
+virtual Ethernet interface on the host with IP address
+<literal>192.168.100.10</literal>.  (See the next section for details
+on container networking.)</para>
+
+<para>To disable the container, just remove it from
+<filename>configuration.nix</filename> and run <literal>nixos-rebuild
+switch</literal>. Note that this will not delete the root directory of
+the container in <literal>/var/lib/containers</literal>.</para>
+
+</section>
+
+
+<section><title>Networking</title>
+
+<para>When you create a container using <literal>nixos-container
+create</literal>, it gets it own private IPv4 address in the range
+<literal>10.233.0.0/16</literal>. You can get the container’s IPv4
+address as follows:
+
+<screen>
+$ nixos-container show-ip foo
+10.233.4.2
+
+$ ping -c1 10.233.4.2
+64 bytes from 10.233.4.2: icmp_seq=1 ttl=64 time=0.106 ms
+</screen>
+
+</para>
+
+<para>Networking is implemented using a pair of virtual Ethernet
+devices. The network interface in the container is called
+<literal>eth0</literal>, while the matching interface in the host is
+called <literal>c-<replaceable>container-name</replaceable></literal>
+(e.g., <literal>c-foo</literal>).  The container has its own network
+namespace and the <literal>CAP_NET_ADMIN</literal> capability, so it
+can perform arbitrary network configuration such as setting up
+firewall rules, without affecting or having access to the host’s
+network.</para>
+
+<para>By default, containers cannot talk to the outside network. If
+you want that, you should set up Network Address Translation (NAT)
+rules on the host to rewrite container traffic to use your external
+IP address. This can be accomplished using the following configuration
+on the host:
+
+<programlisting>
+networking.nat.enable = true;
+networking.nat.internalInterfaces = ["c-+"];
+networking.nat.externalInterface = "eth0";
+</programlisting>
+where <literal>eth0</literal> should be replaced with the desired
+external interface. Note that <literal>c-+</literal> is a wildcard
+that matches all container interfaces.</para>
+
+</section>
+
+
+</chapter>
+
diff --git a/nixos/doc/manual/development.xml b/nixos/doc/manual/development.xml
index be1cbc97d5ed..a93b4b163bd5 100644
--- a/nixos/doc/manual/development.xml
+++ b/nixos/doc/manual/development.xml
@@ -1,5 +1,6 @@
 <chapter xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink">
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="ch-development">
 
 <title>Development</title>
 
@@ -9,7 +10,7 @@ NixOS.</para>
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-getting-sources">
 
 <title>Getting the sources</title>
 
@@ -74,7 +75,7 @@ in <filename>nixos/</filename> as packages.</para>
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-writing-modules">
 
 <title>Writing NixOS modules</title>
 
@@ -188,9 +189,9 @@ commands to be executed periodically by <command>cron</command>).</para>
 
 <example xml:id='locate-example'><title>NixOS module for the “locate” service</title>
 <programlisting>
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let locatedb = "/var/cache/locatedb"; in
 
@@ -579,7 +580,7 @@ systemd.services.dhcpcd =
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-building-parts">
 
 <title>Building specific parts of NixOS</title>
 
@@ -692,7 +693,7 @@ $ systemctl start tmp-httpd.service
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-building-cd">
 
 <title>Building your own NixOS CD</title>
 
@@ -748,57 +749,310 @@ $ ./result/bin/nixos-install</screen>
 
 <!--===============================================================-->
 
-<section><title>Whole-system testing using virtual machines</title>
+<section xml:id="sec-nixos-tests">
+
+<title>NixOS tests</title>
+
+<para>When you add some feature to NixOS, you should write a test for
+it. NixOS tests are kept in the directory <filename
+xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/tests">nixos/tests</filename>,
+and are executed (using Nix) by a testing framework that automatically
+starts one or more virtual machines containing the NixOS system(s)
+required for the test.</para>
+
+<simplesect><title>Writing tests</title>
+
+<para>A NixOS test is a Nix expression that has the following structure:
+
+<programlisting>
+import ./make-test.nix {
+
+  # Either the configuration of a single machine:
+  machine =
+    { config, pkgs, ... }:
+    { <replaceable>configuration…</replaceable>
+    };
+
+  # Or a set of machines:
+  nodes =
+    { <replaceable>machine1</replaceable> =
+        { config, pkgs, ... }: { <replaceable>…</replaceable> };
+      <replaceable>machine2</replaceable> =
+        { config, pkgs, ... }: { <replaceable>…</replaceable> };
+      …
+    };
+
+  testScript =
+    ''
+      <replaceable>Perl code…</replaceable>
+    '';
+}
+</programlisting>
+
+The attribute <literal>testScript</literal> is a bit of Perl code that
+executes the test (described below). During the test, it will start
+one or more virtual machines, the configuration of which is described
+by the attribute <literal>machine</literal> (if you need only one
+machine in your test) or by the attribute <literal>nodes</literal> (if
+you need multiple machines). For instance, <filename
+xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename>
+only needs a single machine to test whether users can log in on the
+virtual console, whether device ownership is correctly maintained when
+switching between consoles, and so on. On the other hand, <filename
+xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs.nix">nfs.nix</filename>,
+which tests NFS client and server functionality in the Linux kernel
+(including whether locks are maintained across server crashes),
+requires three machines: a server and two clients.</para>
+
+<para>There are a few special NixOS configuration options for test
+VMs:
+
+<!-- FIXME: would be nice to generate this automatically. -->
+
+<variablelist>
+
+  <varlistentry>
+    <term><option>virtualisation.memorySize</option></term>
+    <listitem><para>The memory of the VM in
+    megabytes.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>virtualisation.vlans</option></term>
+    <listitem><para>The virtual networks to which the VM is
+    connected. See <filename
+    xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix">nat.nix</filename>
+    for an example.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><option>virtualisation.writableStore</option></term>
+    <listitem><para>By default, the Nix store in the VM is not
+    writable. If you enable this option, a writable union file system
+    is mounted on top of the Nix store to make it appear
+    writable. This is necessary for tests that run Nix operations that
+    modify the store.</para></listitem>
+  </varlistentry>
+
+</variablelist>
+
+For more options, see the module <filename
+xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix">qemu-vm.nix</filename>.</para>
+
+<para>The test script is a sequence of Perl statements that perform
+various actions, such as starting VMs, executing commands in the VMs,
+and so on. Each virtual machine is represented as an object stored in
+the variable <literal>$<replaceable>name</replaceable></literal>,
+where <replaceable>name</replaceable> is the identifier of the machine
+(which is just <literal>machine</literal> if you didn’t specify
+multiple machines using the <literal>nodes</literal> attribute). For
+instance, the following starts the machine, waits until it has
+finished booting, then executes a command and checks that the output
+is more-or-less correct:
+
+<programlisting>
+$machine->start;
+$machine->waitForUnit("default.target");
+$machine->succeed("uname") =~ /Linux/;
+</programlisting>
+
+The first line is actually unnecessary; machines are implicitly
+started when you first execute an action on them (such as
+<literal>waitForUnit</literal> or <literal>succeed</literal>). If you
+have multiple machines, you can speed up the test by starting them in
+parallel:
+
+<programlisting>
+startAll;
+</programlisting>
+
+</para>
+
+<para>The following methods are available on machine objects:
+
+<variablelist>
+
+  <varlistentry>
+    <term><methodname>start</methodname></term>
+    <listitem><para>Start the virtual machine. This method is
+    asynchronous — it does not wait for the machine to finish
+    booting.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>shutdown</methodname></term>
+    <listitem><para>Shut down the machine, waiting for the VM to
+    exit.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>crash</methodname></term>
+    <listitem><para>Simulate a sudden power failure, by telling the VM
+    to exit immediately.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>block</methodname></term>
+    <listitem><para>Simulate unplugging the Ethernet cable that
+    connects the machine to the other machines.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>unblock</methodname></term>
+    <listitem><para>Undo the effect of
+    <methodname>block</methodname>.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>screenshot</methodname></term>
+    <listitem><para>Take a picture of the display of the virtual
+    machine, in PNG format. The screenshot is linked from the HTML
+    log.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>sendMonitorCommand</methodname></term>
+    <listitem><para>Send a command to the QEMU monitor. This is rarely
+    used, but allows doing stuff such as attaching virtual USB disks
+    to a running machine.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>sendKeys</methodname></term>
+    <listitem><para>Simulate pressing keys on the virtual keyboard,
+    e.g., <literal>sendKeys("ctrl-alt-delete")</literal>.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>sendChars</methodname></term>
+    <listitem><para>Simulate typing a sequence of characters on the
+    virtual keyboard, e.g., <literal>sendKeys("foobar\n")</literal>
+    will type the string <literal>foobar</literal> followed by the
+    Enter key.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>execute</methodname></term>
+    <listitem><para>Execute a shell command, returning a list
+    <literal>(<replaceable>status</replaceable>,
+    <replaceable>stdout</replaceable>)</literal>.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>succeed</methodname></term>
+    <listitem><para>Execute a shell command, raising an exception if
+    the exit status is not zero, otherwise returning the standard
+    output.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>fail</methodname></term>
+    <listitem><para>Like <methodname>succeed</methodname>, but raising
+    an exception if the command returns a zero status.</para></listitem>
+  </varlistentry>
 
-<para>Complete NixOS GNU/Linux systems can be tested in virtual
-machines (VMs).  This makes it possible to test a system upgrade or
-configuration change before rebooting into it, using the
-<command>nixos-rebuild build-vm</command> or <command>nixos-rebuild
-build-vm-with-bootloader</command> command.</para>
+  <varlistentry>
+    <term><methodname>waitUntilSucceeds</methodname></term>
+    <listitem><para>Repeat a shell command with 1-second intervals
+    until it succeeds.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitUntilFails</methodname></term>
+    <listitem><para>Repeat a shell command with 1-second intervals
+    until it fails.</para></listitem>
+  </varlistentry>
 
-<!-- The following is adapted from
-     http://wiki.nixos.org/wiki/NixOS_VM_tests, by Eelco Dolstra. -->
-<para>The <filename>tests/</filename> directory in the NixOS source
-tree contains several <emphasis>whole-system unit tests</emphasis>.
-These tests can be run<footnote><para>NixOS tests can be run both from
-NixOS and from a non-NixOS GNU/Linux distribution, provided the Nix
-package manager is installed.</para></footnote> from the NixOS source
-tree as follows:
+  <varlistentry>
+    <term><methodname>waitForUnit</methodname></term>
+    <listitem><para>Wait until the specified systemd unit has reached
+    the “active” state.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitForFile</methodname></term>
+    <listitem><para>Wait until the specified file
+    exists.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitForOpenPort</methodname></term>
+    <listitem><para>Wait until a process is listening on the given TCP
+    port (on <literal>localhost</literal>, at least).</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitForClosedPort</methodname></term>
+    <listitem><para>Wait until nobody is listening on the given TCP
+    port.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitForX</methodname></term>
+    <listitem><para>Wait until the X11 server is accepting
+    connections.</para></listitem>
+  </varlistentry>
+
+  <varlistentry>
+    <term><methodname>waitForWindow</methodname></term>
+    <listitem><para>Wait until an X11 window has appeared whose name
+    matches the given regular expression, e.g.,
+    <literal>waitForWindow(qr/Terminal/)</literal>.</para></listitem>
+  </varlistentry>
+
+</variablelist>
+
+</para>
+
+</simplesect>
+
+
+<simplesect><title>Running tests</title>
+
+<para>You can run tests using <command>nix-build</command>. For
+example, to run the test <filename
+xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix">login.nix</filename>,
+you just do:
 
 <screen>
-$ nix-build tests/ -A nfs.test
+$ nix-build '&lt;nixpkgs/nixos/tests/login.nix>'
 </screen>
 
-This performs an automated test of the NFS client and server
-functionality in the Linux kernel, including file locking semantics
-(e.g., whether locks are maintained across server crashes).  It will
-first build or download all the dependencies of the test (e.g., all
-packages needed to run a NixOS VM). The test is defined in <link
-xlink:href="https://nixos.org/repos/nix/nixos/trunk/tests/nfs.nix">
-<filename>tests/nfs.nix</filename></link>.  If the test succeeds,
-<command>nix-build</command> will place a symlink
-<filename>./result</filename> in the current directory pointing at the
-location in the Nix store of the test results (e.g., screenshots, test
-reports, and so on).  In particular, a pretty-printed log of the test
-is written to <filename>log.html</filename>, which can be viewed using
-a web browser like this:
+or, if you don’t want to rely on <envar>NIX_PATH</envar>:
+
+<screen>
+$ cd /my/nixpkgs/nixos/tests
+$ nix-build login.nix
+…
+running the VM test script
+machine: QEMU running (pid 8841)
+…
+6 out of 6 tests succeeded
+</screen>
+
+After building/downloading all required dependencies, this will
+perform a build that starts a QEMU/KVM virtual machine containing a
+NixOS system. The virtual machine mounts the Nix store of the host;
+this makes VM creation very fast, as no disk image needs to be
+created. Afterwards, you can view a pretty-printed log of the test:
 
 <screen>
 $ firefox result/log.html
 </screen>
+
 </para>
 
 <para>It is also possible to run the test environment interactively,
 allowing you to experiment with the VMs.  For example:
 
 <screen>
-$ nix-build tests/ -A nfs.driver
+$ nix-build login.nix -A driver
 $ ./result/bin/nixos-run-vms
 </screen>
 
-The script <command>nixos-run-vms</command> starts the three virtual
-machines defined in the NFS test using QEMU/KVM.  The root file system
-of the VMs is created on the fly and kept across VM restarts in
+The script <command>nixos-run-vms</command> starts the virtual
+machines defined by test.  The root file system of the VMs is created
+on the fly and kept across VM restarts in
 <filename>./</filename><varname>hostname</varname><filename>.qcow2</filename>.</para>
 
 <para>Finally, the test itself can be run interactively.  This is
@@ -811,17 +1065,11 @@ starting VDE switch for network 1
 &gt;
 </screen>
 
-Perl statements can now be typed in to start or manipulate the VMs:
+You can then take any Perl statement, e.g.
 
 <screen>
-&gt; startAll;
-(the VMs start booting)
-&gt; $server-&gt;waitForJob("nfs-kernel-nfsd");
-&gt; $client1-&gt;succeed("flock -x /data/lock -c 'sleep 100000' &amp;");
-&gt; $client2-&gt;fail("flock -n -s /data/lock true");
-&gt; $client1-&gt;shutdown;
-(this releases client1's lock)
-&gt; $client2-&gt;succeed("flock -n -s /data/lock true");
+&gt; startAll
+&gt; $machine->succeed("touch /tmp/foo")
 </screen>
 
 The function <command>testScript</command> executes the entire test
@@ -829,54 +1077,7 @@ script and drops you back into the test driver command line upon its
 completion.  This allows you to inspect the state of the VMs after the
 test (e.g. to debug the test script).</para>
 
-<para>This and other tests are continuously run on <link
-xlink:href="http://hydra.nixos.org/jobset/nixos/trunk">the Hydra
-instance at <literal>nixos.org</literal></link>, which allows
-developers to be notified of any regressions introduced by a NixOS or
-Nixpkgs change.</para>
-
-<para>The actual Nix programming interface to VM testing is in NixOS,
-under <link
-xlink:href="https://nixos.org/repos/nix/nixos/trunk/lib/testing.nix">
-<filename>lib/testing.nix</filename></link>.  This file defines a
-function which takes an attribute set containing a
-<literal>nixpkgs</literal> attribute (the path to a Nixpkgs checkout),
-and a <literal>system</literal> attribute (the system type).  It
-returns an attribute set containing several utility functions, among
-which the main entry point is <literal>makeTest</literal>.
-</para>
-
-<para>The <literal>makeTest</literal> function takes a function
-similar to that found in <link
-xlink:href="https://nixos.org/repos/nix/nixos/trunk/tests/nfs.nix">
-<filename>tests/nfs.nix</filename></link> (discussed above).  It
-returns an attribute set containing (among others):
-
-<variablelist>
-
-  <varlistentry>
-    <term><varname>test</varname></term>
-    <listitem><para>A derivation containing the test log as an HTML
-    file, as seen above, suitable for presentation in the Hydra
-    continuous build system.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>report</varname></term>
-    <listitem><para>A derivation containing a code coverage report, with
-    meta-data suitable for Hydra.</para></listitem>
-  </varlistentry>
-
-  <varlistentry>
-    <term><varname>driver</varname></term>
-    <listitem><para>A derivation containing scripts to run the VM test or
-    interact with the VM network interactively, as seen above.</para>
-    </listitem>
-  </varlistentry>
-
-</variablelist>
-
-</para>
+</simplesect>
 
 </section>
 
diff --git a/nixos/doc/manual/installation.xml b/nixos/doc/manual/installation.xml
index 70001577692e..03540aa83aaa 100644
--- a/nixos/doc/manual/installation.xml
+++ b/nixos/doc/manual/installation.xml
@@ -1,12 +1,13 @@
 <chapter xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink">
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="ch-installation">
 
 <title>Installing NixOS</title>
 
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-obtaining">
 
 <title>Obtaining NixOS</title>
 
@@ -50,7 +51,7 @@ running NixOS system through several other means:
 
 <!--===============================================================-->
 
-<section>
+<section xml:id="sec-installation">
 
 <title>Installation</title>
 
@@ -295,8 +296,74 @@ $ reboot</screen>
 }</screen>
 </example>
 
+<section xml:id="sec-uefi-installation">
+
+<title>UEFI Installation</title>
+
+<para>NixOS can also be installed on UEFI systems.  The procedure
+is by and large the same as a BIOS installation, with the following
+changes:
+
+<itemizedlist>
+  <listitem>
+    <para>You should boot the live CD in UEFI mode (consult your
+    specific hardware's documentation for instructions).</para>
+  </listitem>
+  <listitem>
+    <para>Instead of <command>fdisk</command>, you should use
+    <command>gdisk</command> to partition your disks. You will need to
+    have a separate partition for <filename>/boot</filename> with
+    partition code EF00, and it should be formatted as a
+    <literal>vfat</literal> filesystem.</para>
+  </listitem>
+  <listitem>
+    <para>You must set <option>boot.loader.gummiboot.enable</option> to
+    <literal>true</literal>, and <option>boot.loader.grub.enable</option>
+    to <literal>false</literal>. <command>nixos-generate-config</command>
+    should do this automatically for new configurations when booted in
+    UEFI mode.</para>
+  </listitem>
+  <listitem>
+    <para>You may want to look at the options starting with
+    <option>boot.loader.efi</option> and <option>boot.loader.gummiboot</option>
+    as well.</para>
+  </listitem>
+  <listitem>
+    <para>To see console messages during early boot, add <literal>"fbcon"</literal>
+    to your <option>boot.initrd.kernelModules</option>.</para>
+  </listitem>
+</itemizedlist>
+</para>
+
+</section>
+
+<section>
+
+<title xml:id="sec-booting-from-usb">Booting from a USB stick</title>
+
+<para>For systems withoua CD drive, the NixOS livecd can be booted from
+a usb stick. For non-UEFI installations,
+<link xlink:href="http://unetbootin.sourceforge.net/">unetbootin</link>
+will work. For UEFI installations, you should mount the ISO, copy its contents
+verbatim to your drive, then either:
+
+<itemizedlist>
+  <listitem>
+    <para>Change the label of the disk partition to the label of the ISO
+    (visible with the blkid command), or</para>
+  </listitem>
+  <listitem>
+    <para>Edit <filename>loader/entries/nixos-livecd.conf</filename> on the drive
+    and change the <literal>root=</literal> field in the <literal>options</literal>
+    line to point to your drive (see the documentation on <literal>root=</literal>
+    in <link xlink:href="https://www.kernel.org/doc/Documentation/kernel-parameters.txt">
+    the kernel documentation</link> for more details).</para>
+  </listitem>
+</itemizedlist>
+</para>
 </section>
 
+</section>
 
 
 <!--===============================================================-->
diff --git a/nixos/doc/manual/manual.xml b/nixos/doc/manual/manual.xml
index 6e13281cbd94..8d7c28dee733 100644
--- a/nixos/doc/manual/manual.xml
+++ b/nixos/doc/manual/manual.xml
@@ -54,10 +54,14 @@
   <xi:include href="running.xml" />
   <!-- <xi:include href="userconfiguration.xml" /> -->
   <xi:include href="troubleshooting.xml" />
+  <xi:include href="containers.xml" />
   <xi:include href="development.xml" />
-  <chapter xml:id="ch-options">
-    <title>List of options</title>
+
+  <xi:include href="release-notes.xml" />
+
+  <appendix xml:id="ch-options">
+    <title>Configuration options</title>
     <xi:include href="options-db.xml" />
-  </chapter>
+  </appendix>
 
 </book>
diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl
index 6d11ad7a6c4a..bb65a49217f0 100644
--- a/nixos/doc/manual/options-to-docbook.xsl
+++ b/nixos/doc/manual/options-to-docbook.xsl
@@ -18,15 +18,13 @@
       <variablelist>
 
         <xsl:for-each select="attrs">
-
+          <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'), '?', '_'))" />
           <varlistentry>
-             <term>
-               <option>
-                 <xsl:for-each select="attr[@name = 'name']/string">
-                   <xsl:value-of select="@value" />
-                   <xsl:if test="position() != last()">.</xsl:if>
-                 </xsl:for-each>
-               </option>
+            <term xlink:href="#{$id}">
+              <xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute>
+              <option>
+                <xsl:value-of select="attr[@name = 'name']/string/@value" />
+              </option>
              </term>
 
              <listitem>
diff --git a/nixos/doc/manual/release-notes.xml b/nixos/doc/manual/release-notes.xml
new file mode 100644
index 000000000000..7995497708e0
--- /dev/null
+++ b/nixos/doc/manual/release-notes.xml
@@ -0,0 +1,84 @@
+<appendix xmlns="http://docbook.org/ns/docbook"
+          xml:id="ch-release-notes">
+
+<title>Release notes</title>
+
+<!--==================================================================-->
+
+<section xml:id="sec-release-14.04">
+
+<title>Release 14.04 (“Baboon”, 2014/04/??)</title>
+
+<para>This is the second stable release branch of NixOS.  The main
+enhancements are the following:
+
+<itemizedlist>
+
+  <listitem><para>Installation on UEFI systems is now supported.  See
+  <xref linkend="sec-uefi-installation"/> for
+  details.</para></listitem>
+
+  <listitem><para>NixOS is now based on Glibc 2.19 and GCC
+  4.8.</para></listitem>
+
+</itemizedlist>
+
+</para>
+
+<para>When upgrading from a previous release, please be aware of the
+following incompatible changes:
+
+<itemizedlist>
+
+  <listitem><para>Nixpkgs no longer exposes unfree packages by
+  default. If your NixOS configuration requires unfree packages from
+  Nixpkgs, you need to enable support for them explicitly by setting:
+
+<programlisting>
+nixpkgs.config.allowUnfree = true;
+</programlisting>
+
+  Otherwise, you get an error message such as:
+
+<screen>
+error: package ‘nvidia-x11-331.49-3.12.17’ in ‘…/nvidia-x11/default.nix:56’
+  has an unfree license, refusing to evaluate
+</screen>
+
+  </para></listitem>
+
+  <listitem><para>The firewall is now enabled by default. If you don’t
+  want this, you need to disable it explicitly:
+
+<programlisting>
+networking.firewall.enable = false;
+</programlisting>
+
+  </para></listitem>
+
+  <listitem><para>The option
+  <option>boot.loader.grub.memtest86</option> has been renamed to
+  <option>boot.loader.grub.memtest86.enable</option>.</para></listitem>
+
+  <listitem><para>The <literal>mysql55</literal> service has been
+  merged into the <literal>mysql</literal> service, which no longer
+  sets a default for the option
+  <option>services.mysql.package</option>.</para></listitem>
+
+</itemizedlist>
+
+</para>
+
+</section>
+
+<!--==================================================================-->
+
+<section xml:id="sec-release-13.10">
+
+<title>Release 13.10 (“Aardvark”, 2013/10/31)</title>
+
+<para>This is the first stable release branch of NixOS.</para>
+
+</section>
+
+</appendix>
diff --git a/nixos/doc/manual/running.xml b/nixos/doc/manual/running.xml
index e50099707cc5..e1a358df2aac 100644
--- a/nixos/doc/manual/running.xml
+++ b/nixos/doc/manual/running.xml
@@ -11,7 +11,7 @@ service manager.</para>
 
 <!--===============================================================-->
 
-<section><title>Service management</title>
+<section xml:id="sec-systemctl"><title>Service management</title>
 
 <para>In NixOS, all system services are started and monitored using
 the systemd program.  Systemd is the “init” process of the system
@@ -92,7 +92,7 @@ necessary).</para>
 
 <!--===============================================================-->
 
-<section><title>Rebooting and shutting down</title>
+<section xml:id="sec-rebooting"><title>Rebooting and shutting down</title>
 
 <para>The system can be shut down (and automatically powered off) by
 doing:
@@ -134,7 +134,7 @@ authentication.</para>
 
 <!--===============================================================-->
 
-<section><title>User sessions</title>
+<section xml:id="sec-user-sessions"><title>User sessions</title>
 
 <para>Systemd keeps track of all users who are logged into the system
 (e.g. on a virtual console or remotely via SSH).  The command
@@ -185,7 +185,7 @@ $ loginctl terminate-session c3
 
 <!--===============================================================-->
 
-<section><title>Control groups</title>
+<section xml:id="sec-cgroups"><title>Control groups</title>
 
 <para>To keep track of the processes in a running system, systemd uses
 <emphasis>control groups</emphasis> (cgroups).  A control group is a
@@ -258,7 +258,7 @@ usage.</para>
 
 <!--===============================================================-->
 
-<section><title>Logging</title>
+<section xml:id="sec-logging"><title>Logging</title>
 
 <para>System-wide logging is provided by systemd’s
 <emphasis>journal</emphasis>, which subsumes traditional logging
@@ -308,7 +308,7 @@ groups.  All users have a private journal that can be read using
 
 <!--===============================================================-->
 
-<section><title>Cleaning up the Nix store</title>
+<section xml:id="sec-nix-gc"><title>Cleaning up the Nix store</title>
 
 <para>Nix has a purely functional model, meaning that packages are
 never upgraded in place.  Instead new versions of packages end up in a
diff --git a/nixos/doc/manual/troubleshooting.xml b/nixos/doc/manual/troubleshooting.xml
index c6e0a3a7888c..c7d65112b649 100644
--- a/nixos/doc/manual/troubleshooting.xml
+++ b/nixos/doc/manual/troubleshooting.xml
@@ -1,12 +1,13 @@
 <chapter xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink">
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="ch-troubleshooting">
 
 <title>Troubleshooting</title>
 
 
 <!--===============================================================-->
 
-<section><title>Boot problems</title>
+<section xml:id="sec-boot-problems"><title>Boot problems</title>
 
 <para>If NixOS fails to boot, there are a number of kernel command
 line parameters that may help you to identify or fix the issue.  You
@@ -69,7 +70,7 @@ unless something is very wrong.)</para>
 
 <!--===============================================================-->
 
-<section><title>Maintenance mode</title>
+<section xml:id="sec-maintenance-mode"><title>Maintenance mode</title>
 
 <para>You can enter rescue mode by running:
 
@@ -85,7 +86,7 @@ just exit from the rescue shell.</para>
 
 <!--===============================================================-->
 
-<section><title>Rolling back configuration changes</title>
+<section xml:id="sec-rollback"><title>Rolling back configuration changes</title>
 
 <para>After running <command>nixos-rebuild</command> to switch to a
 new configuration, you may find that the new configuration doesn’t
@@ -131,7 +132,7 @@ lrwxrwxrwx 1 root root 78 Aug 12 13:54 /nix/var/nix/profiles/system-268-link ->
 
 <!--===============================================================-->
 
-<section><title>Nix store corruption</title>
+<section xml:id="sec-nix-store-corruption"><title>Nix store corruption</title>
 
 <para>After a system crash, it’s possible for files in the Nix store
 to become corrupted.  (For instance, the Ext4 file system has the
@@ -166,7 +167,7 @@ binary cache; otherwise, they cannot be repaired.</para>
 
 <!--===============================================================-->
 
-<section><title>Nix network issues</title>
+<section xml:id="sec-nix-network-issues"><title>Nix network issues</title>
 
 <para>Nix uses a so-called <emphasis>binary cache</emphasis> to
 optimise building a package from source into downloading it as a
diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix
index 4b8c7354a7ec..0fa00637a93a 100644
--- a/nixos/lib/eval-config.nix
+++ b/nixos/lib/eval-config.nix
@@ -26,10 +26,15 @@ rec {
 
   # These are the extra arguments passed to every module.  In
   # particular, Nixpkgs is passed through the "pkgs" argument.
+  # FIXME: we enable config.allowUnfree to make packages like
+  # nvidia-x11 available. This isn't a problem because if the user has
+  # ‘nixpkgs.config.allowUnfree = false’, then evaluation will fail on
+  # the 64-bit package anyway. However, it would be cleaner to respect
+  # nixpkgs.config here.
   extraArgs = extraArgs_ // {
     inherit pkgs modules baseModules;
     modulesPath = ../modules;
-    pkgs_i686 = import ./nixpkgs.nix { system = "i686-linux"; };
+    pkgs_i686 = import ./nixpkgs.nix { system = "i686-linux"; config.allowUnfree = true; };
     utils = import ./utils.nix pkgs;
   };
 
@@ -53,7 +58,7 @@ rec {
           inherit system extraArgs modules prefix;
           # For efficiency, leave out most NixOS modules; they don't
           # define nixpkgs.config, so it's pointless to evaluate them.
-          baseModules = [ ../modules/misc/nixpkgs.nix ];
+          baseModules = [ ../modules/misc/nixpkgs.nix ../modules/config/no-x-libs.nix ];
           pkgs = import ./nixpkgs.nix { system = system_; config = {}; };
           check = false;
         }).config.nixpkgs;
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index a28214ea934f..e2bd3393d872 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -495,7 +495,9 @@ sub waitForX {
     my ($self, $regexp) = @_;
     $self->nest("waiting for the X11 server", sub {
         retry sub {
-            my ($status, $out) = $self->execute("xwininfo -root > /dev/null 2>&1");
+            my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'session opened'");
+            return 0 if $status != 0;
+            ($status, $out) = $self->execute("xwininfo -root > /dev/null 2>&1");
             return 1 if $status == 0;
         }
     });
diff --git a/nixos/lib/test-driver/test-driver.pl b/nixos/lib/test-driver/test-driver.pl
index c6a707cdf6b9..358c29e515f2 100644
--- a/nixos/lib/test-driver/test-driver.pl
+++ b/nixos/lib/test-driver/test-driver.pl
@@ -144,6 +144,13 @@ sub runTests {
         }
     });
 
+    $log->nest("syncing", sub {
+        foreach my $vm (values %vms) {
+            next unless $vm->isUp();
+            $vm->execute("sync");
+        }
+    });
+
     if ($nrTests != 0) {
         $log->log("$nrSucceeded out of $nrTests tests succeeded",
             ($nrSucceeded < $nrTests ? { error => 1 } : { }));
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 3407229e921a..8d17958b9d2f 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -67,103 +67,55 @@ rec {
     };
 
 
-  # Generate a coverage report from the coverage data produced by
-  # runTests.
-  makeReport = x: runCommand "report" { buildInputs = [rsync]; }
-    ''
-      mkdir -p $TMPDIR/gcov/
-
-      for d in ${x}/coverage-data/*; do
-          echo "doing $d"
-          [ -n "$(ls -A "$d")" ] || continue
-
-          for i in $(cd $d/nix/store && ls); do
-              if ! test -e $TMPDIR/gcov/nix/store/$i; then
-                  echo "copying $i"
-                  mkdir -p $TMPDIR/gcov/$(echo $i | cut -c34-)
-                  rsync -rv /nix/store/$i/.build/* $TMPDIR/gcov/
-              fi
-          done
-
-          chmod -R u+w $TMPDIR/gcov
-
-          find $TMPDIR/gcov -name "*.gcda" -exec rm {} \;
-
-          for i in $(cd $d/nix/store && ls); do
-              rsync -rv $d/nix/store/$i/.build/* $TMPDIR/gcov/
-          done
-
-          find $TMPDIR/gcov -name "*.gcda" -exec chmod 644 {} \;
-
-          echo "producing info..."
-          ${pkgs.lcov}/bin/geninfo --ignore-errors source,gcov $TMPDIR/gcov --output-file $TMPDIR/app.info
-          cat $TMPDIR/app.info >> $TMPDIR/full.info
-      done
-
-      echo "making report..."
-      mkdir -p $out/coverage
-      ${pkgs.lcov}/bin/genhtml --show-details $TMPDIR/full.info -o $out/coverage
-      cp $TMPDIR/full.info $out/coverage/
-
-      mkdir -p $out/nix-support
-      cat ${x}/nix-support/hydra-build-products >> $out/nix-support/hydra-build-products
-      echo "report coverage $out/coverage" >> $out/nix-support/hydra-build-products
-      [ ! -e ${x}/nix-support/failed ] || touch $out/nix-support/failed
-    ''; # */
+  makeTest =
+    { testScript, makeCoverageReport ? false, ... } @ t:
 
+    let
 
-  makeTest = testFun: complete (call testFun);
-  makeTests = testsFun: lib.mapAttrs (name: complete) (call testsFun);
-
-  apply = makeTest; # compatibility
-  call = f: f { inherit pkgs system; };
-
-  complete = t: t // rec {
-    nodes = buildVirtualNetwork (
-      if t ? nodes then t.nodes else
-      if t ? machine then { machine = t.machine; }
-      else { } );
+      nodes = buildVirtualNetwork (
+        t.nodes or (if t ? machine then { machine = t.machine; } else { }));
 
-    testScript =
-      # Call the test script with the computed nodes.
-      if builtins.isFunction t.testScript
-      then t.testScript { inherit nodes; }
-      else t.testScript;
+      testScript' =
+        # Call the test script with the computed nodes.
+        if builtins.isFunction testScript
+        then testScript { inherit nodes; }
+        else testScript;
 
-    vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
+      vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
 
-    vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
+      vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 
-    # Generate onvenience wrappers for running the test driver
-    # interactively with the specified network, and for starting the
-    # VMs from the command line.
-    driver = runCommand "nixos-test-driver"
-      { buildInputs = [ makeWrapper];
-        inherit testScript;
-        preferLocalBuild = true;
-      }
-      ''
-        mkdir -p $out/bin
-        echo "$testScript" > $out/test-script
-        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
-        vms="$(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)"
-        wrapProgram $out/bin/nixos-test-driver \
-          --add-flags "$vms" \
-          --run "testScript=\"\$(cat $out/test-script)\"" \
-          --set testScript '"$testScript"' \
-          --set VLANS '"${toString vlans}"'
-        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
-        wrapProgram $out/bin/nixos-run-vms \
-          --add-flags "$vms" \
-          --set tests '"startAll; joinAll;"' \
-          --set VLANS '"${toString vlans}"' \
-          ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
-      ''; # "
-
-    test = runTests driver;
-
-    report = makeReport test;
-  };
+      # Generate onvenience wrappers for running the test driver
+      # interactively with the specified network, and for starting the
+      # VMs from the command line.
+      driver = runCommand "nixos-test-driver"
+        { buildInputs = [ makeWrapper];
+          testScript = testScript';
+          preferLocalBuild = true;
+        }
+        ''
+          mkdir -p $out/bin
+          echo "$testScript" > $out/test-script
+          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
+          vms="$(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)"
+          wrapProgram $out/bin/nixos-test-driver \
+            --add-flags "$vms" \
+            --run "testScript=\"\$(cat $out/test-script)\"" \
+            --set testScript '"$testScript"' \
+            --set VLANS '"${toString vlans}"'
+          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
+          wrapProgram $out/bin/nixos-run-vms \
+            --add-flags "$vms" \
+            --set tests '"startAll; joinAll;"' \
+            --set VLANS '"${toString vlans}"' \
+            ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
+        ''; # "
+
+      test = runTests driver;
+
+      report = releaseTools.gcovReport { coverageRuns = [ test ]; };
+
+    in (if makeCoverageReport then report else test) // { inherit driver test; };
 
 
   runInMachine =
@@ -193,7 +145,7 @@ rec {
         exit $?
       '';
 
-      testscript = ''
+      testScript = ''
         startAll;
         $client->waitForUnit("multi-user.target");
         ${preBuild}
@@ -206,7 +158,7 @@ rec {
         ${coreutils}/bin/mkdir $out
         ${coreutils}/bin/mkdir -p vm-state-client/xchg
         export > vm-state-client/xchg/saved-env
-        export tests='${testscript}'
+        export tests='${testScript}'
         ${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
       ''; # */
 
diff --git a/nixos/maintainers/scripts/ec2/create-ebs-amis.py b/nixos/maintainers/scripts/ec2/create-ebs-amis.py
index 541eadd7b8c9..eab111a2665b 100755
--- a/nixos/maintainers/scripts/ec2/create-ebs-amis.py
+++ b/nixos/maintainers/scripts/ec2/create-ebs-amis.py
@@ -203,7 +203,7 @@ f = open("{0}.{1}.ami-id".format(args.region, image_type), "w")
 f.write("{0}".format(ami_id))
 f.close()
 
-for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1']:
+for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1']:
     if args.region != dest:
         print >> sys.stderr, "copying image from region {0} to {1}".format(args.region, dest)
         conn = boto.ec2.connect_to_region(dest)
diff --git a/nixos/maintainers/scripts/gce/create-gce.sh b/nixos/maintainers/scripts/gce/create-gce.sh
new file mode 100755
index 000000000000..8bf36f33c7db
--- /dev/null
+++ b/nixos/maintainers/scripts/gce/create-gce.sh
@@ -0,0 +1,14 @@
+#! /bin/sh -e
+
+export NIX_PATH=nixpkgs=../../../..
+export NIXOS_CONFIG=$(dirname $(readlink -f $0))/../../../modules/virtualisation/google-compute-image.nix
+export TIMESTAMP=$(date +%Y%m%d%H%M)
+
+nix-build '<nixpkgs/nixos>' \
+   -A config.system.build.googleComputeImage --argstr system x86_64-linux -o gce --option extra-binary-caches http://hydra.nixos.org -j 10
+
+img=$(echo gce/*.tar.gz)
+if ! gsutil ls gs://nixos/$(basename $img); then
+  gsutil cp $img gs://nixos/$(basename $img)
+fi
+gcutil addimage $(basename $img .raw.tar.gz | sed 's|\.|-|' | sed 's|_|-|') gs://nixos/$(basename $img)
diff --git a/nixos/modules/config/fonts/corefonts.nix b/nixos/modules/config/fonts/corefonts.nix
index 7de95200cfa9..51a6676fe4a0 100644
--- a/nixos/modules/config/fonts/corefonts.nix
+++ b/nixos/modules/config/fonts/corefonts.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 987bb1088c0d..cf70ca264d6a 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/config/fonts/fontdir.nix b/nixos/modules/config/fonts/fontdir.nix
index a4f69809b2a4..c78b52fe29e1 100644
--- a/nixos/modules/config/fonts/fontdir.nix
+++ b/nixos/modules/config/fonts/fontdir.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
index f43784f6d03e..16df197d87f3 100644
--- a/nixos/modules/config/fonts/fonts.nix
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/config/fonts/ghostscript.nix b/nixos/modules/config/fonts/ghostscript.nix
index 9ef00396808c..a41f00a76c57 100644
--- a/nixos/modules/config/fonts/ghostscript.nix
+++ b/nixos/modules/config/fonts/ghostscript.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
index 6f5d2950463f..f8c35b440d12 100644
--- a/nixos/modules/config/gnu.nix
+++ b/nixos/modules/config/gnu.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
@@ -36,7 +36,7 @@ with pkgs.lib;
     # GNU lsh.
     services.openssh.enable = false;
     services.lshd.enable = true;
-    services.xserver.startOpenSSHAgent = false;
+    programs.ssh.startAgent = false;
     services.xserver.startGnuPGAgent = true;
 
     # TODO: GNU dico.
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 56d541cb9b3b..8182b8ae8081 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -53,7 +53,11 @@ in
       };
 
       consoleKeyMap = mkOption {
-        type = types.str;
+        type = mkOptionType {
+          name = "string or path";
+          check = t: (isString t || types.path.check t);
+        };
+
         default = "us";
         example = "fr";
         description = ''
@@ -72,7 +76,12 @@ in
 
     environment.systemPackages = [ glibcLocales ];
 
-    environment.variables.LANG = config.i18n.defaultLocale;
+    environment.variables =
+      { LANG = config.i18n.defaultLocale;
+        LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+      };
+
+    systemd.globalEnvironment.LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";
 
     # ‘/etc/locale.conf’ is used by systemd.
     environment.etc = singleton
diff --git a/nixos/modules/config/krb5.nix b/nixos/modules/config/krb5.nix
index 3323046ac5b4..bb5a95ebc844 100644
--- a/nixos/modules/config/krb5.nix
+++ b/nixos/modules/config/krb5.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix
index 113f5d8bcbde..8171f460385b 100644
--- a/nixos/modules/config/ldap.nix
+++ b/nixos/modules/config/ldap.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 with pkgs;
 
 let
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
index 9ac68b428190..43717697ebd3 100644
--- a/nixos/modules/config/networking.nix
+++ b/nixos/modules/config/networking.nix
@@ -1,8 +1,8 @@
 # /etc files related to networking, such as /etc/services.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index ec7bf3fea7b5..f91dbb4cc281 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -1,6 +1,9 @@
-{ config, pkgs, ... }:
+# This module gets rid of all dependencies on X11 client libraries
+# (including fontconfig).
 
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+
+with lib;
 
 {
   options = {
@@ -8,18 +11,22 @@ with pkgs.lib;
       type = types.bool;
       default = false;
       description = ''
-        Switch off the options in the default configuration that require X libraries.
-        Currently this includes: ssh X11 forwarding, dbus, fonts.enableCoreFonts,
-        fonts.enableFontConfig
+        Switch off the options in the default configuration that
+        require X11 libraries. This includes client-side font
+        configuration and SSH forwarding of X11 authentication
+        in. Thus, you probably do not want to enable this option if
+        you want to run X11 programs on this machine via SSH.
       '';
     };
   };
 
   config = mkIf config.environment.noXlibs {
     programs.ssh.setXAuthLocation = false;
-    fonts = {
-      enableCoreFonts = false;
-      enableFontConfig = false;
-    };
+    security.pam.services.su.forwardXAuth = lib.mkForce false;
+
+    fonts.enableFontConfig = false;
+
+    nixpkgs.config.packageOverrides = pkgs:
+      { dbus = pkgs.dbus.override { useX11 = false; }; };
   };
 }
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index 2e2125d44f7b..45695d9cb89f 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -1,8 +1,8 @@
 # Configuration for the Name Service Switch (/etc/nsswitch.conf).
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
index 7299136235ed..17f3ed00b9be 100644
--- a/nixos/modules/config/power-management.nix
+++ b/nixos/modules/config/power-management.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -65,14 +65,7 @@ in
 
   config = mkIf cfg.enable {
 
-    # Enable the ACPI daemon.  Not sure whether this is essential.
-    services.acpid.enable = true;
-
-    boot.kernelModules =
-      [ "acpi_cpufreq" "powernow-k8" "cpufreq_performance" "cpufreq_powersave" "cpufreq_ondemand"
-        "cpufreq_conservative"
-      ];
-
+    # FIXME: Implement powersave governor for sandy bridge or later Intel CPUs
     powerManagement.cpuFreqGovernor = mkDefault "ondemand";
     powerManagement.scsiLinkPolicy = mkDefault "min_power";
 
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index e7cbe7a28f36..eedc4ca2b2f2 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 with pkgs;
 
 let
@@ -77,7 +77,7 @@ in {
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pulseaudio;
         example = literalExample "pulseaudio.override { jackaudioSupport = true; }";
         description = ''
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index 0b4f75a35216..9e212847e489 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -1,9 +1,9 @@
 # This module defines a global environment configuration and
 # a common configuration for all shells.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index 65d7722abfa7..427b2519cbdf 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, utils, ... }:
+{ config, lib, pkgs, utils, ... }:
 
-with pkgs.lib;
+with lib;
 with utils;
 
 {
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
index 8f9b31dccff3..542360219193 100644
--- a/nixos/modules/config/sysctl.nix
+++ b/nixos/modules/config/sysctl.nix
@@ -1,12 +1,12 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   sysctlOption = mkOptionType {
     name = "sysctl option value";
-    check = x: isBool x || isString x || isInt x;
+    check = x: isBool x || isString x || isInt x || isNull x;
     merge = args: defs: (last defs).value; # FIXME: hacky way to allow overriding in configuration.nix.
   };
 
@@ -29,8 +29,9 @@ in
         <manvolnum>8</manvolnum></citerefentry>.  Note that sysctl
         parameters names must be enclosed in quotes
         (e.g. <literal>"vm.swappiness"</literal> instead of
-        <literal>vm.swappiness</literal>).  The value of each parameter
-        may be a string, integer or Boolean.
+        <literal>vm.swappiness</literal>).  The value of each
+        parameter may be a string, integer, boolean, or null
+        (signifying the option will not appear at all).
       '';
     };
 
@@ -39,22 +40,13 @@ in
   config = {
 
     environment.etc."sysctl.d/nixos.conf".text =
-      concatStrings (mapAttrsToList (n: v: "${n}=${if v == false then "0" else toString v}\n") config.boot.kernel.sysctl);
+      concatStrings (mapAttrsToList (n: v:
+        optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n"
+      ) config.boot.kernel.sysctl);
 
     systemd.services.systemd-sysctl =
-      { description = "Apply Kernel Variables";
-        before = [ "sysinit.target" "shutdown.target" ];
-        wantedBy = [ "sysinit.target" "multi-user.target" ];
+      { wantedBy = [ "multi-user.target" ];
         restartTriggers = [ config.environment.etc."sysctl.d/nixos.conf".source ];
-        unitConfig = {
-          DefaultDependencies = false; # needed to prevent a cycle
-          ConditionPathIsReadWrite = "/proc/sys/"; # prevent systemd-sysctl in containers
-        };
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${config.systemd.package}/lib/systemd/systemd-sysctl";
-        };
       };
 
     # Enable hardlink and symlink restrictions.  See
@@ -65,8 +57,9 @@ in
 
     # Hide kernel pointers (e.g. in /proc/modules) for unprivileged
     # users as these make it easier to exploit kernel vulnerabilities.
-    boot.kernel.sysctl."kernel.kptr_restrict" = 1;
-
+    #
+    # Removed under grsecurity.
+    boot.kernel.sysctl."kernel.kptr_restrict" =
+      if config.security.grsecurity.enable then null else 1;
   };
-
 }
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index 2f61947c3bcc..2ea998bbb635 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -1,9 +1,9 @@
 # This module defines the packages that appear in
 # /run/current-system/sw.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -46,6 +46,7 @@ let
       pkgs.rsync
       pkgs.strace
       pkgs.sysvtools
+      pkgs.su
       pkgs.time
       pkgs.usbutils
       pkgs.utillinux
@@ -134,6 +135,10 @@ in
           if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
               $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
           fi
+
+          if [ -x $out/bin/update-desktop-database -a -w $out/share/applications ]; then
+              $out/bin/update-desktop-database $out/share/applications
+          fi
         '';
     };
 
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
index 42fbe841d070..88aa7866c2b2 100644
--- a/nixos/modules/config/timezone.nix
+++ b/nixos/modules/config/timezone.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
diff --git a/nixos/modules/config/unix-odbc-drivers.nix b/nixos/modules/config/unix-odbc-drivers.nix
index 0f6084690589..b725e6cae732 100644
--- a/nixos/modules/config/unix-odbc-drivers.nix
+++ b/nixos/modules/config/unix-odbc-drivers.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 # unixODBC drivers (this solution is not perfect.. Because the user has to
 # ask the admin to add a driver.. but it's simple and works
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 714de646eb7a..8b8f6bd909e3 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -1,11 +1,32 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   ids = config.ids;
-  users = config.users;
+  cfg = config.users;
+
+  nonUidUsers = filterAttrs (n: u: u.createUser && u.uid == null) cfg.extraUsers;
+  nonGidGroups = filterAttrs (n: g: g.gid == null) cfg.extraGroups;
+
+  passwordDescription = ''
+    The options <literal>hashedPassword</literal>,
+    <literal>password</literal> and <literal>passwordFile</literal>
+    controls what password is set for the user.
+    <literal>hashedPassword</literal> overrides both
+    <literal>password</literal> and <literal>passwordFile</literal>.
+    <literal>password</literal> overrides <literal>passwordFile</literal>.
+    If none of these three options are set, no password is assigned to
+    the user, and the user will not be able to do password logins.
+    If the option <literal>users.mutableUsers</literal> is true, the
+    password defined in one of the three options will only be set when
+    the user is created for the first time. After that, you are free to
+    change the password with the ordinary user management commands. If
+    <literal>users.mutableUsers</literal> is false, you cannot change
+    user passwords, they will always be set according to the password
+    options.
+  '';
 
   userOpts = { name, config, ... }: {
 
@@ -13,7 +34,10 @@ let
 
       name = mkOption {
         type = types.str;
-        description = "The name of the user account. If undefined, the name of the attribute set will be used.";
+        description = ''
+          The name of the user account. If undefined, the name of the
+          attribute set will be used.
+        '';
       };
 
       description = mkOption {
@@ -28,9 +52,14 @@ let
       };
 
       uid = mkOption {
-        type = with types; uniq (nullOr int);
+        type = with types; nullOr int;
         default = null;
-        description = "The account UID. If undefined, NixOS will select a free UID.";
+        description = ''
+          The account UID. If the <literal>mutableUsers</literal> option
+          is false, the UID cannot be null. Otherwise, the UID might be
+          null, in which case a free UID is picked on activation (by the
+          useradd command).
+        '';
       };
 
       group = mkOption {
@@ -60,31 +89,54 @@ let
       createHome = mkOption {
         type = types.bool;
         default = false;
-        description = "If true, the home directory will be created automatically.";
+        description = ''
+          If true, the home directory will be created automatically. If this
+          option is true and the home directory already exists but is not
+          owned by the user, directory owner and group will be changed to
+          match the user.
+        '';
       };
 
       useDefaultShell = mkOption {
         type = types.bool;
         default = false;
-        description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
+        description = ''
+          If true, the user's shell will be set to
+          <literal>cfg.defaultUserShell</literal>.
+        '';
+      };
+
+      hashedPassword = mkOption {
+        type = with types; uniq (nullOr str);
+        default = null;
+        description = ''
+          Specifies the (hashed) password for the user.
+          ${passwordDescription}
+        '';
       };
 
       password = mkOption {
         type = with types; uniq (nullOr str);
         default = null;
         description = ''
-          The user's password. If undefined, no password is set for
-          the user.  Warning: do not set confidential information here
-          because it is world-readable in the Nix store.  This option
-          should only be used for public accounts such as
-          <literal>guest</literal>.
+          Specifies the (clear text) password for the user.
+          Warning: do not set confidential information here
+          because it is world-readable in the Nix store. This option
+          should only be used for public accounts.
+          ${passwordDescription}
         '';
       };
 
-      isSystemUser = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Indicates if the user is a system user or not.";
+      passwordFile = mkOption {
+        type = with types; uniq (nullOr string);
+        default = null;
+        description = ''
+          The path to a file that contains the user's password. The password
+          file is read on each system activation. The file should contain
+          exactly one line, which should be the password in an encrypted form
+          that is suitable for the <literal>chpasswd -e</literal> command.
+          ${passwordDescription}
+        '';
       };
 
       createUser = mkOption {
@@ -96,19 +148,11 @@ let
           then not modify any of the basic properties for the user account.
         '';
       };
-
-      isAlias = mkOption {
-        type = types.bool;
-        default = false;
-        description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
-      };
-
     };
 
     config = {
       name = mkDefault name;
-      uid = mkDefault (attrByPath [name] null ids.uids);
-      shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
+      shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
     };
 
   };
@@ -119,41 +163,167 @@ let
 
       name = mkOption {
         type = types.str;
-        description = "The name of the group. If undefined, the name of the attribute set will be used.";
+        description = ''
+          The name of the group. If undefined, the name of the attribute set
+          will be used.
+        '';
       };
 
       gid = mkOption {
-        type = with types; uniq (nullOr int);
+        type = with types; nullOr int;
         default = null;
-        description = "The GID of the group. If undefined, NixOS will select a free GID.";
+        description = ''
+          The group GID. If the <literal>mutableUsers</literal> option
+          is false, the GID cannot be null. Otherwise, the GID might be
+          null, in which case a free GID is picked on activation (by the
+          groupadd command).
+        '';
+      };
+
+      members = mkOption {
+        type = with types; listOf string;
+        default = [];
+        description = ''
+          The user names of the group members, added to the
+          <literal>/etc/group</literal> file.
+        '';
       };
 
     };
 
     config = {
       name = mkDefault name;
-      gid = mkDefault (attrByPath [name] null ids.gids);
     };
 
   };
 
-  # Note: the 'X' in front of the password is to distinguish between
-  # having an empty password, and not having a password.
-  serializedUser = u: "${u.name}\n${u.description}\n${if u.uid != null then toString u.uid else ""}\n${u.group}\n${toString (concatStringsSep "," u.extraGroups)}\n${u.home}\n${u.shell}\n${toString u.createHome}\n${if u.password != null then "X" + u.password else ""}\n${toString u.isSystemUser}\n${toString u.createUser}\n${toString u.isAlias}\n";
-
-  usersFile = pkgs.writeText "users" (
+  getGroup = gname:
+    let
+      groups = mapAttrsToList (n: g: g) (
+        filterAttrs (n: g: g.name == gname) cfg.extraGroups
+      );
+    in
+      if length groups == 1 then head groups
+      else if groups == [] then throw "Group ${gname} not defined"
+      else throw "Group ${gname} has multiple definitions";
+
+  getUser = uname:
+    let
+      users = mapAttrsToList (n: u: u) (
+        filterAttrs (n: u: u.name == uname) cfg.extraUsers
+      );
+    in
+      if length users == 1 then head users
+      else if users == [] then throw "User ${uname} not defined"
+      else throw "User ${uname} has multiple definitions";
+
+  mkGroupEntry = gname:
     let
-      p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
-    in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
+      g = getGroup gname;
+      users = mapAttrsToList (n: u: u.name) (
+        filterAttrs (n: u: elem g.name u.extraGroups) cfg.extraUsers
+      );
+    in concatStringsSep ":" [
+      g.name "x" (toString g.gid)
+      (concatStringsSep "," (users ++ (filter (u: !(elem u users)) g.members)))
+    ];
+
+  mkPasswdEntry = uname: let u = getUser uname; in
+    concatStringsSep ":" [
+      u.name "x" (toString u.uid)
+      (toString (getGroup u.group).gid)
+      u.description u.home u.shell
+    ];
+
+  sortOn = a: sort (as1: as2: lessThan (getAttr a as1) (getAttr a as2));
+
+  groupFile = pkgs.writeText "group" (
+    concatStringsSep "\n" (map (g: mkGroupEntry g.name) (
+      let f = g: g.gid != null; in
+        sortOn "gid" (filter f (attrValues cfg.extraGroups))
+    ))
+  );
+
+  passwdFile = pkgs.writeText "passwd" (
+    concatStringsSep "\n" (map (u: mkPasswdEntry u.name) (
+      let f = u: u.createUser && (u.uid != null); in
+        sortOn "uid" (filter f (attrValues cfg.extraUsers))
+    ))
+  );
+
+  # If mutableUsers is true, this script adds all users/groups defined in
+  # users.extra{Users,Groups} to /etc/{passwd,group} iff there isn't any
+  # existing user/group with the same name in those files.
+  # If mutableUsers is false, the /etc/{passwd,group} files will simply be
+  # replaced with the users/groups defined in the NixOS configuration.
+  # The merging procedure could certainly be improved, and instead of just
+  # keeping the lines as-is from /etc/{passwd,group} they could be combined
+  # in some way with the generated content from the NixOS configuration.
+  merger = src: pkgs.writeScript "merger" ''
+    #!${pkgs.bash}/bin/bash
+
+    PATH=${pkgs.gawk}/bin:${pkgs.gnugrep}/bin:$PATH
+
+    ${if !cfg.mutableUsers
+      then ''cp ${src} $1.tmp''
+      else ''awk -F: '{ print "^"$1":.*" }' $1 | egrep -vf - ${src} | cat $1 - > $1.tmp''
+    }
+
+    # set mtime to +1, otherwise change might go unnoticed (vipw/vigr only looks at mtime)
+    touch -m -t $(date -d @$(($(stat -c %Y $1)+1)) +%Y%m%d%H%M.%S) $1.tmp
+
+    mv -f $1.tmp $1
+  '';
+
+  idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
+    let
+      id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
+      exists = builtins.hasAttr id acc;
+      newAcc = acc // (builtins.listToAttrs [ { name = id; value = true; } ]);
+    in if dup then args else if exists
+      then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
+      else { dup = false; acc = newAcc; }
+    ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
 
-in
+  uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
+  gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
 
-{
+in {
 
   ###### interface
 
   options = {
 
+    users.mutableUsers = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        If true, you are free to add new users and groups to the system
+        with the ordinary <literal>useradd</literal> and
+        <literal>groupadd</literal> commands. On system activation, the
+        existing contents of the <literal>/etc/passwd</literal> and
+        <literal>/etc/group</literal> files will be merged with the
+        contents generated from the <literal>users.extraUsers</literal> and
+        <literal>users.extraGroups</literal> options. If
+        <literal>mutableUsers</literal> is false, the contents of the user and
+        group files will simply be replaced on system activation. This also
+        holds for the user passwords; if this option is false, all changed
+        passwords will be reset according to the
+        <literal>users.extraUsers</literal> configuration on activation. If
+        this option is true, the initial password for a user will be set
+        according to <literal>users.extraUsers</literal>, but existing passwords
+        will not be changed.
+      '';
+    };
+
+    users.enforceIdUniqueness = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to require that no two users/groups share the same uid/gid.
+      '';
+    };
+
     users.extraUsers = mkOption {
       default = {};
       type = types.loaOf types.optionSet;
@@ -194,11 +364,17 @@ in
       example = "!";
       description = ''
         The (hashed) password for the root account set on initial
-        installation.  The empty string denotes that root can login
+        installation. The empty string denotes that root can login
         locally without a password (but not via remote services such
         as SSH, or indirectly via <command>su</command> or
-        <command>sudo</command>).  The string <literal>!</literal>
+        <command>sudo</command>). The string <literal>!</literal>
         prevents root from logging in using a password.
+        Note, setting this option sets
+        <literal>users.extraUsers.root.hashedPassword</literal>.
+        Note, if <literal>users.mutableUsers</literal> is false
+        you cannot change the root password manually, so in that case
+        the name of this option is a bit misleading, since it will define
+        the root password beyond the user initialisation phase.
       '';
     };
 
@@ -211,144 +387,120 @@ in
 
     users.extraUsers = {
       root = {
+        uid = ids.uids.root;
         description = "System administrator";
         home = "/root";
-        shell = config.users.defaultUserShell;
+        shell = cfg.defaultUserShell;
         group = "root";
+        extraGroups = [ "grsecurity" ];
+        hashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
+        uid = ids.uids.nobody;
         description = "Unprivileged account (don't use!)";
+        group = "nogroup";
       };
     };
 
     users.extraGroups = {
-      root = { };
-      wheel = { };
-      disk = { };
-      kmem = { };
-      tty = { };
-      floppy = { };
-      uucp = { };
-      lp = { };
-      cdrom = { };
-      tape = { };
-      audio = { };
-      video = { };
-      dialout = { };
-      nogroup = { };
-      users = { };
-      nixbld = { };
-      utmp = { };
-      adm = { }; # expected by journald
+      root.gid = ids.gids.root;
+      wheel.gid = ids.gids.wheel;
+      disk.gid = ids.gids.disk;
+      kmem.gid = ids.gids.kmem;
+      tty.gid = ids.gids.tty;
+      floppy.gid = ids.gids.floppy;
+      uucp.gid = ids.gids.uucp;
+      lp.gid = ids.gids.lp;
+      cdrom.gid = ids.gids.cdrom;
+      tape.gid = ids.gids.tape;
+      audio.gid = ids.gids.audio;
+      video.gid = ids.gids.video;
+      dialout.gid = ids.gids.dialout;
+      nogroup.gid = ids.gids.nogroup;
+      users.gid = ids.gids.users;
+      nixbld.gid = ids.gids.nixbld;
+      utmp.gid = ids.gids.utmp;
+      adm.gid = ids.gids.adm;
+      grsecurity.gid = ids.gids.grsecurity;
     };
 
-    system.activationScripts.rootPasswd = stringAfter [ "etc" ]
-      ''
-        # If there is no password file yet, create a root account with an
-        # empty password.
-        if ! test -e /etc/passwd; then
-            rootHome=/root
-            touch /etc/passwd; chmod 0644 /etc/passwd
-            touch /etc/group; chmod 0644 /etc/group
-            touch /etc/shadow; chmod 0600 /etc/shadow
-            # Can't use useradd, since it complains that it doesn't know us
-            # (bootstrap problem!).
-            echo "root:x:0:0:System administrator:$rootHome:${config.users.defaultUserShell}" >> /etc/passwd
-            echo "root:${config.security.initialRootPassword}:::::::" >> /etc/shadow
-        fi
-      '';
-
-    # Print a reminder for users to set a root password.
-    environment.interactiveShellInit =
-      ''
-        if [ "$UID" = 0 ]; then
-            read _l < /etc/shadow
-            if [ "''${_l:0:6}" = root:: ]; then
-                cat >&2 <<EOF
-        Warning: Your root account has a null password, allowing local users
-        to login as root.  Please set a non-null password using \`passwd', or
-        disable password-based root logins using \`passwd -l'.
-        EOF
-            fi
-            unset _l
-        fi
-      '';
-
-    system.activationScripts.users = stringAfter [ "groups" ]
-      ''
-        echo "updating users..."
-
-        cat ${usersFile} | while true; do
-            read name || break
-            read description
-            read uid
-            read group
-            read extraGroups
-            read home
-            read shell
-            read createHome
-            read password
-            read isSystemUser
-            read createUser
-            read isAlias
-
-            if [ -z "$createUser" ]; then
-                continue
-            fi
-
-            if ! curEnt=$(getent passwd "$name"); then
-                useradd ''${isSystemUser:+--system} \
-                    --comment "$description" \
-                    ''${uid:+--uid $uid} \
-                    --gid "$group" \
-                    --groups "$extraGroups" \
-                    --home "$home" \
-                    --shell "$shell" \
-                    ''${createHome:+--create-home} \
-                    ''${isAlias:+--non-unique} \
-                    "$name"
-                if test "''${password:0:1}" = 'X'; then
-                    (echo "''${password:1}"; echo "''${password:1}") | ${pkgs.shadow}/bin/passwd "$name"
-                fi
-            else
-                #echo "updating user $name..."
-                oldIFS="$IFS"; IFS=:; set -- $curEnt; IFS="$oldIFS"
-                prevUid=$3
-                prevHome=$6
-                # Don't change the home directory if it's the same to prevent
-                # unnecessary warnings about logged in users.
-                if test "$prevHome" = "$home"; then unset home; fi
-                usermod \
-                    --comment "$description" \
-                    --gid "$group" \
-                    --groups "$extraGroups" \
-                    ''${home:+--home "$home"} \
-                    --shell "$shell" \
-                    "$name"
-            fi
-
-        done
+    system.activationScripts.users =
+      let
+        mkhomeUsers = filterAttrs (n: u: u.createHome) cfg.extraUsers;
+        setpwUsers = filterAttrs (n: u: u.createUser) cfg.extraUsers;
+        pwFile = u: if !(isNull u.hashedPassword)
+          then pkgs.writeTextFile { name = "password-file"; text = u.hashedPassword; }
+          else if !(isNull u.password)
+          then pkgs.runCommand "password-file" { pw = u.password; } ''
+            echo -n "$pw" | ${pkgs.mkpasswd}/bin/mkpasswd -s > $out
+          '' else u.passwordFile;
+        setpw = n: u: ''
+          setpw=yes
+          ${optionalString cfg.mutableUsers ''
+            test "$(getent shadow '${u.name}' | cut -d: -f2)" != "x" && setpw=no
+          ''}
+          if [ "$setpw" == "yes" ]; then
+            ${if !(isNull (pwFile u))
+              then ''
+                echo -n "${u.name}:" | cat - "${pwFile u}" | \
+                  ${pkgs.shadow}/sbin/chpasswd -e
+              ''
+              else "passwd -l '${u.name}' &>/dev/null"
+            }
+          fi
+        '';
+        mkhome = n: u: ''
+          uid="$(id -u ${u.name})"
+          gid="$(id -g ${u.name})"
+          h="${u.home}"
+          test -a "$h" || mkdir -p "$h" || true
+          test "$(stat -c %u "$h")" = $uid || chown $uid "$h" || true
+          test "$(stat -c %g "$h")" = $gid || chgrp $gid "$h" || true
+        '';
+        groupadd = n: g: ''
+          if [ -z "$(getent group "${g.name}")" ]; then
+            echo "Adding group ${g.name}"
+            ${pkgs.shadow}/sbin/groupadd "${g.name}"
+          fi
+        '';
+        useradd = n: u: ''
+          if ! id "${u.name}" &>/dev/null; then
+            echo "Adding user ${u.name}"
+            ${pkgs.shadow}/sbin/useradd \
+              -g "${u.group}" \
+              -s "${u.shell}" \
+              -d "${u.home}" \
+              "${u.name}"
+            echo "${u.name}:x" | ${pkgs.shadow}/sbin/chpasswd -e
+          fi
+        '';
+      in stringAfter [ "etc" ] ''
+        touch /etc/group
+        touch /etc/passwd
+        VISUAL=${merger groupFile} ${pkgs.shadow}/sbin/vigr &>/dev/null
+        VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null
+        ${pkgs.shadow}/sbin/grpconv
+        ${pkgs.shadow}/sbin/pwconv
+        ${concatStrings (mapAttrsToList groupadd nonGidGroups)}
+        ${concatStrings (mapAttrsToList useradd nonUidUsers)}
+        ${concatStrings (mapAttrsToList mkhome mkhomeUsers)}
+        ${concatStrings (mapAttrsToList setpw setpwUsers)}
       '';
 
-    system.activationScripts.groups = stringAfter [ "rootPasswd" "binsh" "etc" "var" ]
-      ''
-        echo "updating groups..."
-
-        createGroup() {
-            name="$1"
-            gid="$2"
-
-            if ! curEnt=$(getent group "$name"); then
-                groupadd --system \
-                    ''${gid:+--gid $gid} \
-                    "$name"
-            fi
-        }
-
-        ${flip concatMapStrings (attrValues config.users.extraGroups) (g: ''
-          createGroup '${g.name}' '${toString g.gid}'
-        '')}
-      '';
+    # for backwards compatibility
+    system.activationScripts.groups = stringAfter [ "users" ] "";
+
+    assertions = [
+      { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
+        message = "uids and gids must be unique!";
+      }
+      { assertion = cfg.mutableUsers || (nonUidUsers == {});
+        message = "When mutableUsers is false, no uid can be null";
+      }
+      { assertion = cfg.mutableUsers || (nonGidGroups == {});
+        message = "When mutableUsers is false, no gid can be null";
+      }
+    ];
 
   };
 
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index 027dd827b4dd..3820a95b12e2 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/hardware/cpu/amd-microcode.nix b/nixos/modules/hardware/cpu/amd-microcode.nix
index 5720a63834f5..86a3df5da21d 100644
--- a/nixos/modules/hardware/cpu/amd-microcode.nix
+++ b/nixos/modules/hardware/cpu/amd-microcode.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/hardware/cpu/intel-microcode.nix b/nixos/modules/hardware/cpu/intel-microcode.nix
index 9046ddf83bbf..800c391b293a 100644
--- a/nixos/modules/hardware/cpu/intel-microcode.nix
+++ b/nixos/modules/hardware/cpu/intel-microcode.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/hardware/network/b43.nix b/nixos/modules/hardware/network/b43.nix
index 03f81f92ef0b..e63f2d04d1a6 100644
--- a/nixos/modules/hardware/network/b43.nix
+++ b/nixos/modules/hardware/network/b43.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let kernelVersion = config.boot.kernelPackages.kernel.version; in
 
diff --git a/nixos/modules/services/x11/mesa.nix b/nixos/modules/hardware/opengl.nix
index f892a1517582..485ac008dd8a 100644
--- a/nixos/modules/services/x11/mesa.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -2,19 +2,19 @@
 let
   inherit (pkgs.lib) mkOption types mkIf optional optionals elem optionalString optionalAttrs;
 
-  cfg = config.services.mesa;
+  cfg = config.hardware.opengl;
 
   kernelPackages = config.boot.kernelPackages;
 in {
   options = {
-    services.mesa.enable = mkOption {
-      description = "Whether this configuration requires mesa.";
+    hardware.opengl.enable = mkOption {
+      description = "Whether this configuration requires opengl.";
       type = types.bool;
       default = false;
       internal = true;
     };
 
-    services.mesa.driSupport = mkOption {
+    hardware.opengl.driSupport = mkOption {
       type = types.bool;
       default = true;
       description = ''
@@ -23,51 +23,55 @@ in {
       '';
     };
 
-    services.mesa.driSupport32Bit = mkOption {
+    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> driver and for
-        <literal>mesa</literal>.
+        <literal>Mesa</literal>.
       '';
     };
 
-    services.mesa.s3tcSupport = mkOption {
+    hardware.opengl.s3tcSupport = mkOption {
       type = types.bool;
       default = false;
       description = ''
         Make S3TC(S3 Texture Compression) via libtxc_dxtn available
-        to OpenGL drivers. It is essential for many games to work
-        with FOSS GPU drivers.
+        to OpenGL drivers instead of the patent-free S2TC replacement.
 
         Using this library may require a patent license depending on your location.
       '';
     };
 
 
-    services.mesa.videoDrivers = mkOption {
+    hardware.opengl.videoDrivers = mkOption {
       type = types.listOf types.str;
       # !!! We'd like "nv" here, but it segfaults the X server.
       default = [ "ati" "cirrus" "intel" "vesa" "vmware" ];
       example = [ "vesa" ];
       description = ''
-        The names of the video drivers that the mesa should
-        support.  Mesa will try all of the drivers listed
-        here until it finds one that supports your video card.
+        The names of the opengl video drivers the configuration
+        supports. They will be tried in order until one that
+        supports your card is found.
       '';
     };
   };
 
   config = mkIf cfg.enable {
+    assertions = pkgs.lib.singleton {
+      assertion = cfg.driSupport32Bit -> pkgs.stdenv.isx86_64;
+      message = "Option driSupport32Bit only makes sens on a 64-bit system.";
+    };
+
     system.activationScripts.setup-opengl.deps = [];
     system.activationScripts.setup-opengl.text = ''
       rm -f /run/opengl-driver{,-32}
-      ${optionalString (!cfg.driSupport32Bit) "ln -sf opengl-driver /run/opengl-driver-32"}
-
-      ${# !!! The OpenGL driver depends on what's detected at runtime.
-        if elem "nvidia" cfg.videoDrivers then
+      ${optionalString (pkgs.stdenv.isi686) "ln -sf opengl-driver /run/opengl-driver-32"}
+    ''
+      #TODO:  The OpenGL driver should depend on what's detected at runtime.
+     +( if elem "nvidia" cfg.videoDrivers then
           ''
             ln -sf ${kernelPackages.nvidia_x11} /run/opengl-driver
             ${optionalString cfg.driSupport32Bit
@@ -84,18 +88,25 @@ in {
         else if elem "ati_unfree" cfg.videoDrivers then
           "ln -sf ${kernelPackages.ati_drivers_x11} /run/opengl-driver"
         else
+          let
+            lib_fun = p: p.buildEnv {
+              name = "mesa-drivers+txc-${p.mesa_drivers.version}";
+              paths = [
+                p.mesa_drivers
+                p.mesa_noglu # mainly for libGL
+                (if cfg.s3tcSupport then p.libtxc_dxtn else p.libtxc_dxtn_s2tc)
+              ];
+            };
+          in
           ''
-            ${optionalString cfg.driSupport "ln -sf ${pkgs.mesa_drivers} /run/opengl-driver"}
+            ${optionalString cfg.driSupport "ln -sf ${lib_fun pkgs} /run/opengl-driver"}
             ${optionalString cfg.driSupport32Bit
-              "ln -sf ${pkgs_i686.mesa_drivers} /run/opengl-driver-32"}
+              "ln -sf ${lib_fun pkgs_i686} /run/opengl-driver-32"}
           ''
-      }
-    '';
+      );
 
     environment.variables.LD_LIBRARY_PATH =
-      [ "/run/opengl-driver/lib" "/run/opengl-driver-32/lib" ]
-      ++ optional cfg.s3tcSupport "${pkgs.libtxc_dxtn}/lib"
-      ++ optional (cfg.s3tcSupport && cfg.driSupport32Bit) "${pkgs_i686.libtxc_dxtn}/lib";
+      [ "/run/opengl-driver/lib" "/run/opengl-driver-32/lib" ];
 
     boot.extraModulePackages =
       optional (elem "nvidia" cfg.videoDrivers) kernelPackages.nvidia_x11 ++
diff --git a/nixos/modules/hardware/pcmcia.nix b/nixos/modules/hardware/pcmcia.nix
index 206846567505..d7d002ae6c8a 100644
--- a/nixos/modules/hardware/pcmcia.nix
+++ b/nixos/modules/hardware/pcmcia.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/hardware/video/bumblebee.nix b/nixos/modules/hardware/video/bumblebee.nix
new file mode 100644
index 000000000000..f06139adc445
--- /dev/null
+++ b/nixos/modules/hardware/video/bumblebee.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+let kernel = config.boot.kernelPackages; in
+with lib;
+
+{
+
+  options = {
+    hardware.bumblebee.enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Enable the bumblebee daemon to manage Optimus hybrid video cards.
+        This should power off secondary GPU until its use is requested
+        by running an application with optirun.
+
+        Only nvidia driver is supported so far.
+      '';
+    };
+  };
+
+  config = mkIf config.hardware.bumblebee.enable {
+    boot.blacklistedKernelModules = [ "nouveau" "nvidia" ];
+    boot.kernelModules = [ "bbswitch" ];
+    boot.extraModulePackages = [ kernel.bbswitch kernel.nvidia_x11 ];
+
+    environment.systemPackages = [ pkgs.bumblebee ];
+
+    systemd.services.bumblebeed = {
+      description = "Bumblebee Hybrid Graphics Switcher";
+      wantedBy = [ "display-manager.service" ];
+      script = "bumblebeed --use-syslog";
+      path = [ kernel.bbswitch pkgs.bumblebee ];
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = 60;
+        CPUSchedulingPolicy = "idle";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
index 9aca5b89d258..ca0e233f9e3f 100644
--- a/nixos/modules/installer/cd-dvd/channel.nix
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -1,9 +1,9 @@
 # Provide an initial copy of the NixOS channel so that the user
 # doesn't need to run "nix-channel --update" first.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -28,7 +28,7 @@ in
 {
   # Provide the NixOS/Nixpkgs sources in /etc/nixos.  This is required
   # for nixos-install.
-  boot.postBootCommands =
+  boot.postBootCommands = mkAfter
     ''
       if ! [ -e /var/lib/nixos/did-channel-init ]; then
         echo "unpacking the NixOS/Nixpkgs sources..."
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 1aba67dcd9e9..a120a01041bd 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -1,9 +1,9 @@
 # This module contains the basic configuration for building a NixOS
 # installation CD.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports =
@@ -19,7 +19,7 @@ with pkgs.lib;
   # ISO naming.
   isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixosVersion}-${pkgs.stdenv.system}.iso";
 
-  isoImage.volumeID = substring 0 32 "NIXOS_${config.system.nixosVersion}";
+  isoImage.volumeID = substring 0 11 "NIXOS_${config.system.nixosVersion}";
 
   # Make the installer more likely to succeed in low memory
   # environments.  The kernel's overcommit heustistics bite us
@@ -29,14 +29,15 @@ with pkgs.lib;
   boot.kernel.sysctl."vm.overcommit_memory" = "1";
 
   # To speed up installation a little bit, include the complete stdenv
-  # in the Nix store on the CD.
-  isoImage.storeContents = [ pkgs.stdenv pkgs.busybox ];
+  # in the Nix store on the CD.  Archive::Cpio is needed for the
+  # initrd builder.
+  isoImage.storeContents = [ pkgs.stdenv pkgs.busybox pkgs.perlPackages.ArchiveCpio ];
 
   # EFI booting
   isoImage.makeEfiBootable = true;
 
   # Add Memtest86+ to the CD.
-  boot.loader.grub.memtest86 = true;
+  boot.loader.grub.memtest86.enable = true;
 
   # Get a console as soon as the initrd loads fbcon on EFI boot
   boot.initrd.kernelModules = [ "fbcon" ];
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix
index debf3e7db906..65aa11670893 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix
@@ -1,9 +1,9 @@
 # This module defines a NixOS installation CD that contains X11 and
 # KDE 4.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports = [ ./installation-cd-base.nix ../../profiles/graphical.nix ];
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 00a9e91c7338..00f5fae84342 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -2,9 +2,9 @@
 # configuration.  The derivation for the ISO image will be placed in
 # config.system.build.isoImage.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
index 13ed95d4cebd..c274970c5536 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-pc.nix b/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
index fcb96f7a24fe..0357bf801960 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
@@ -1,9 +1,9 @@
 # This module contains the basic configuration for building a NixOS
 # tarball, that can directly boot, maybe using PXE or unpacking on a fs.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
index 7f253d595dc3..1008bd5d3d0d 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
@@ -1,9 +1,9 @@
 # This module contains the basic configuration for building a NixOS
 # tarball for the sheevaplug.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -138,8 +138,7 @@ in
   };
 
   # Setting vesa, we don't get the nvidia driver, which can't work in arm.
-  services.xserver.videoDriver = "vesa";
-  services.xserver.videoDrivers = [];
+  hardware.opengl.videoDrivers = [ "vesa" ];
   services.nixosManual.enable = false;
 
   # Include the firmware for various wireless cards.
diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix
index 8d678fba71f5..eaecbe1381f5 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball.nix
@@ -2,9 +2,9 @@
 # configuration.  The derivation for the ISO image will be placed in
 # config.system.build.tarball.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/installer/scan/detected.nix b/nixos/modules/installer/scan/detected.nix
index 09d04608e685..f350cd986afa 100644
--- a/nixos/modules/installer/scan/detected.nix
+++ b/nixos/modules/installer/scan/detected.nix
@@ -1,8 +1,8 @@
 # List all devices which are detected by nixos-hardware-scan.
 # Common devices are enabled by default.
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   config = mkDefault {
diff --git a/nixos/modules/installer/scan/not-detected.nix b/nixos/modules/installer/scan/not-detected.nix
index 814858fdffda..b30c569ed2a7 100644
--- a/nixos/modules/installer/scan/not-detected.nix
+++ b/nixos/modules/installer/scan/not-detected.nix
@@ -1,8 +1,8 @@
 # List all devices which are _not_ detected by nixos-hardware-scan.
 # Common devices are enabled by default.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   hardware.enableAllFirmware = true;
diff --git a/nixos/modules/installer/tools/nixos-checkout.nix b/nixos/modules/installer/tools/nixos-checkout.nix
index 418998556864..3338e5119acb 100644
--- a/nixos/modules/installer/tools/nixos-checkout.nix
+++ b/nixos/modules/installer/tools/nixos-checkout.nix
@@ -1,9 +1,9 @@
 # This module generates the nixos-checkout script, which replaces the
 # Nixpkgs source trees in /etc/nixos/nixpkgs with a Git checkout.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 6b42058a8926..da1d26882772 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -61,7 +61,7 @@ my @attrs = ();
 my @kernelModules = ();
 my @initrdKernelModules = ();
 my @modulePackages = ();
-my @imports = ("<nixos/modules/installer/scan/not-detected.nix>");
+my @imports = ("<nixpkgs/nixos/modules/installer/scan/not-detected.nix>");
 
 
 sub debug {
@@ -96,9 +96,9 @@ my $videoDriver;
 
 sub pciCheck {
     my $path = shift;
-    my $vendor = read_file "$path/vendor";
-    my $device = read_file "$path/device";
-    my $class = read_file "$path/class";
+    my $vendor = read_file "$path/vendor"; chomp $vendor;
+    my $device = read_file "$path/device"; chomp $device;
+    my $class = read_file "$path/class"; chomp $class;
 
     my $module;
     if (-e "$path/driver/module") {
@@ -130,6 +130,7 @@ sub pciCheck {
 
     # broadcom STA driver (wl.ko)
     # list taken from http://www.broadcom.com/docs/linux_sta/README.txt
+    # FIXME: still needed?
     if ($vendor eq "0x14e4" &&
         ($device eq "0x4311" || $device eq "0x4312" || $device eq "0x4313" ||
          $device eq "0x4315" || $device eq "0x4327" || $device eq "0x4328" ||
@@ -156,6 +157,7 @@ sub pciCheck {
 
     # Assume that all NVIDIA cards are supported by the NVIDIA driver.
     # There may be exceptions (e.g. old cards).
+    # FIXME: do we want to enable an unfree driver here?
     $videoDriver = "nvidia" if $vendor eq "0x10de" && $class =~ /^0x03/;
 }
 
@@ -163,16 +165,16 @@ foreach my $path (glob "/sys/bus/pci/devices/*") {
     pciCheck $path;
 }
 
-push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
+push @attrs, "hardware.opengl.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
 
 
 # Idem for USB devices.
 
 sub usbCheck {
     my $path = shift;
-    my $class = read_file "$path/bInterfaceClass";
-    my $subclass = read_file "$path/bInterfaceSubClass";
-    my $protocol = read_file "$path/bInterfaceProtocol";
+    my $class = read_file "$path/bInterfaceClass"; chomp $class;
+    my $subclass = read_file "$path/bInterfaceSubClass"; chomp $subclass;
+    my $protocol = read_file "$path/bInterfaceProtocol"; chomp $protocol;
 
     my $module;
     if (-e "$path/driver/module") {
@@ -216,14 +218,22 @@ foreach my $path (glob "/sys/class/block/*") {
 }
 
 
+my $dmi = `@dmidecode@/sbin/dmidecode`;
+
+
 # Check if we're a VirtualBox guest.  If so, enable the guest
 # additions.
-my $dmi = `@dmidecode@/sbin/dmidecode`;
 if ($dmi =~ /Manufacturer: innotek/) {
     push @attrs, "services.virtualbox.enable = true;"
 }
 
 
+# Likewise for QEMU.
+if ($dmi =~ /Manufacturer: Bochs/) {
+    push @imports, "<nixpkgs/nixos/modules/profiles/qemu-guest.nix>";
+}
+
+
 # Generate the swapDevices option from the currently activated swap
 # devices.
 my @swaps = read_file("/proc/swaps");
@@ -256,7 +266,7 @@ foreach my $fs (read_file("/proc/self/mountinfo")) {
     $mountPoint = "/" if $mountPoint eq "";
 
     # Skip special filesystems.
-    next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run");
+    next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run") || $mountPoint eq "/var/lib/nfs/rpc_pipefs";
 
     # Skip the optional fields.
     my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
@@ -305,7 +315,15 @@ EOF
   fileSystems.\"$mountPoint\" =
     { device = \"$device\";
       fsType = \"$fsType\";
-      options = \"${\join ",", uniq(@extraOptions, @superOptions, @mountOptions)}\";
+EOF
+
+    if (scalar @extraOptions > 0) {
+      $fileSystems .= <<EOF;
+      options = \"${\join ",", uniq(@extraOptions)}\";
+EOF
+    }
+
+    $fileSystems .= <<EOF;
     };
 
 EOF
diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh
index 60cee2519da0..edc94d732084 100644
--- a/nixos/modules/installer/tools/nixos-option.sh
+++ b/nixos/modules/installer/tools/nixos-option.sh
@@ -228,7 +228,7 @@ else
     escapeQuotes () { eval echo "$1"; }
     nixMap escapeQuotes "$names"
   else
-    echo 1>&2 "An error occured while looking for attribute names."
+    echo 1>&2 "An error occurred while looking for attribute names."
     echo $result
   fi
 fi
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 5c89394abceb..d7b749573fa9 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -1,4 +1,8 @@
-#! @shell@ -e
+#! @shell@
+
+if [ -x "@shell@" ]; then export SHELL="@shell@"; fi;
+
+set -e
 
 showSyntax() {
     exec man nixos-rebuild
@@ -7,6 +11,7 @@ showSyntax() {
 
 
 # Parse the command line.
+origArgs=("$@")
 extraBuildFlags=()
 action=
 buildNix=1
@@ -40,7 +45,7 @@ while [ "$#" -gt 0 ]; do
         repair=1
         extraBuildFlags+=("$i")
         ;;
-      --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair)
+      --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q)
         extraBuildFlags+=("$i")
         ;;
       --max-jobs|-j|--cores|-I)
@@ -76,8 +81,30 @@ done
 
 if [ -z "$action" ]; then showSyntax; fi
 
-if [ -n "$rollback" ]; then
-    buildNix=
+# Only run shell scripts from the Nixpkgs tree if the action is
+# "switch", "boot", or "test". With other actions (such as "build"),
+# the user may reasonably expect that no code from the Nixpkgs tree is
+# executed, so it's safe to run nixos-rebuild against a potentially
+# untrusted tree.
+canRun=
+if [ "$action" = switch -o "$action" = boot -o "$action" = test ]; then
+    canRun=1
+fi
+
+
+# If ‘--upgrade’ is given, run ‘nix-channel --update nixos’.
+if [ -n "$upgrade" -a -z "$_NIXOS_REBUILD_REEXEC" ]; then
+    nix-channel --update nixos
+fi
+
+
+# Re-execute nixos-rebuild from the Nixpkgs tree.
+if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" ]; then
+    if p=$(nix-instantiate --find-file nixpkgs/nixos/modules/installer/tools/nixos-rebuild.sh "${extraBuildFlags[@]}"); then
+        export _NIXOS_REBUILD_REEXEC=1
+        exec $SHELL -e $p "${origArgs[@]}"
+        exit 1
+    fi
 fi
 
 
@@ -98,20 +125,33 @@ if [ -z "$repair" ] && systemctl show nix-daemon.socket nix-daemon.service | gre
 fi
 
 
-# If ‘--upgrade’ is given, run ‘nix-channel --update nixos’.
-if [ -n "$upgrade" ]; then
-    nix-channel --update nixos
+# First build Nix, since NixOS may require a newer version than the
+# current one.
+if [ -n "$rollback" -o "$action" = dry-run ]; then
+    buildNix=
 fi
 
-
-# First build Nix, since NixOS may require a newer version than the
-# current one.  Of course, the same goes for Nixpkgs, but Nixpkgs is
-# more conservative.
-if [ "$action" != dry-run -a -n "$buildNix" ]; then
+if [ -n "$buildNix" ]; then
     echo "building Nix..." >&2
     if ! nix-build '<nixpkgs/nixos>' -A config.nix.package -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then
         if ! nix-build '<nixpkgs/nixos>' -A nixFallback -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then
-            nix-build '<nixpkgs>' -A nixUnstable -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null
+            if ! nix-build '<nixpkgs>' -A nix -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then
+                machine="$(uname -m)"
+                if [ "$machine" = x86_64 ]; then
+                    nixStorePath=/nix/store/d34q3q2zj9nriq4ifhn3dnnngqvinjb3-nix-1.7
+                elif [[ "$machine" =~ i.86 ]]; then
+                    nixStorePath=/nix/store/qlah0darpcn6sf3lr2226rl04l1gn4xz-nix-1.7
+                else
+                    echo "$0: unsupported platform"
+                    exit 1
+                fi
+                if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \
+                    --option extra-binary-caches http://cache.nixos.org/; then
+                    echo "warning: don't know how to get latest Nix" >&2
+                fi
+                # Older version of nix-store -r don't support --add-root.
+                [ -e $tmpDir/nix ] || ln -sf $nixStorePath $tmpDir/nix
+            fi
         fi
     fi
     PATH=$tmpDir/nix/bin:$PATH
@@ -120,10 +160,12 @@ fi
 
 # Update the version suffix if we're building from Git (so that
 # nixos-version shows something useful).
-if nixpkgs=$(nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then
-    suffix=$(@shell@ $nixpkgs/nixos/modules/installer/tools/get-version-suffix "${extraBuildFlags[@]}" || true)
-    if [ -n "$suffix" ]; then
-        echo -n "$suffix" > "$nixpkgs/.version-suffix" || true
+if [ -n "$canRun" ]; then
+    if nixpkgs=$(nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then
+        suffix=$($SHELL $nixpkgs/nixos/modules/installer/tools/get-version-suffix "${extraBuildFlags[@]}" || true)
+        if [ -n "$suffix" ]; then
+            echo -n "$suffix" > "$nixpkgs/.version-suffix" || true
+        fi
     fi
 fi
 
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 652bfa917dfa..7dbcc261485d 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -80,9 +80,9 @@ in
   /*
   options = {
 
-    installer.enableGraphicalTools = pkgs.lib.mkOption {
+    installer.enableGraphicalTools = mkOption {
       default = false;
-      type = with pkgs.lib.types; bool;
+      type = types.bool;
       example = true;
       description = ''
         Enable the installation of graphical tools.
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 76cc29a1facd..0a52cbea2897 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports =
@@ -15,5 +15,5 @@ with pkgs.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" ];
+  hardware.opengl.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" ];
 }
diff --git a/nixos/modules/misc/assertions.nix b/nixos/modules/misc/assertions.nix
index 5fb88308b776..786ec7d250c4 100644
--- a/nixos/modules/misc/assertions.nix
+++ b/nixos/modules/misc/assertions.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/misc/check-config.nix b/nixos/modules/misc/check-config.nix
index f759c88d3a1f..e9803de21961 100644
--- a/nixos/modules/misc/check-config.nix
+++ b/nixos/modules/misc/check-config.nix
@@ -1,6 +1,6 @@
-{ pkgs, ... }:
+{ lib, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
diff --git a/nixos/modules/misc/crashdump.nix b/nixos/modules/misc/crashdump.nix
index 6e71baa9a431..d68f38bae2f5 100644
--- a/nixos/modules/misc/crashdump.nix
+++ b/nixos/modules/misc/crashdump.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   crashdump = config.boot.crashDump;
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 16eec9043212..2d9ea1450ff0 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -110,6 +110,27 @@
       openldap = 99;
       memcached = 100;
       cgminer = 101;
+      munin = 102;
+      logcheck = 103;
+      nix-ssh = 104;
+      dictd = 105;
+      couchdb = 106;
+      searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
+      notbit = 111;
+      ngircd = 112;
+      btsync = 113;
+      minecraft = 114;
+      monetdb = 115;
+      rippled = 116;
+      murmur = 117;
+      foundationdb = 118;
+      newrelic = 119;
+      starbound = 120;
+      hydra     = 122;
+      spiped    = 123;
 
       # When adding a uid, make sure it doesn't match an existing gid.
 
@@ -199,6 +220,22 @@
       haproxy = 92;
       openldap = 93;
       connman = 94;
+      munin = 95;
+      keys = 96;
+      dictd = 105;
+      couchdb = 106;
+      searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
+      notbit = 111;
+      monetdb = 115;
+      foundationdb = 118;
+      newrelic = 119;
+      starbound = 120;
+      grsecurity = 121;
+      hydra = 122;
+      spiped = 123;
 
       # When adding a gid, make sure it doesn't match an existing uid.
 
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index b6408be5844f..45da0df7967c 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 7433fab168e2..a5dad7dd9071 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   isConfig = x:
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index ae9fb5fb2a0e..67bafac4c455 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 442edd8029de..5c30d74be694 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -29,7 +29,9 @@
   ./hardware/network/intel-3945abg.nix
   ./hardware/network/ralink.nix
   ./hardware/network/rtl8192c.nix
+  ./hardware/opengl.nix
   ./hardware/pcmcia.nix
+  ./hardware/video/bumblebee.nix
   ./installer/tools/nixos-checkout.nix
   ./installer/tools/tools.nix
   ./misc/assertions.nix
@@ -59,6 +61,8 @@
   ./security/apparmor.nix
   ./security/apparmor-suid.nix
   ./security/ca.nix
+  ./security/duosec.nix
+  ./security/grsecurity.nix
   ./security/pam.nix
   ./security/pam_usb.nix
   ./security/polkit.nix
@@ -76,20 +80,34 @@
   ./services/backup/bacula.nix
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
-  ./services/backup/sitecopy-backup.nix
   ./services/backup/rsnapshot.nix
+  ./services/backup/sitecopy-backup.nix
+  ./services/backup/tarsnap.nix
+  ./services/continuous-integration/jenkins/default.nix
+  ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
+  ./services/databases/couchdb.nix
   ./services/databases/firebird.nix
   ./services/databases/memcached.nix
   ./services/databases/mongodb.nix
   ./services/databases/redis.nix
   ./services/databases/mysql.nix
-  ./services/databases/mysql55.nix
   ./services/databases/openldap.nix
   ./services/databases/postgresql.nix
   ./services/databases/virtuoso.nix
+  ./services/databases/monetdb.nix
+  ./services/desktops/accountservice.nix
+  ./services/desktops/gnome3/at-spi2-core.nix
+  ./services/desktops/gnome3/evolution-data-server.nix
+  ./services/desktops/gnome3/gnome-keyring.nix
+  ./services/desktops/gnome3/gnome-online-accounts.nix
+  ./services/desktops/gnome3/gnome-user-share.nix
+  ./services/desktops/gnome3/sushi.nix
+  ./services/desktops/gnome3/tracker.nix
+  ./services/desktops/telepathy.nix
   ./services/games/ghost-one.nix
+  ./services/games/minecraft-server.nix
   ./services/hardware/acpid.nix
   ./services/hardware/amd-hybrid-graphics.nix
   ./services/hardware/bluetooth.nix
@@ -116,13 +134,16 @@
   ./services/mail/spamassassin.nix
   ./services/misc/autofs.nix
   ./services/misc/cgminer.nix
+  ./services/misc/dictd.nix
   ./services/misc/disnix.nix
   ./services/misc/felix.nix
   ./services/misc/folding-at-home.nix
   ./services/misc/gpsd.nix
   ./services/misc/nix-daemon.nix
   ./services/misc/nix-gc.nix
+  ./services/misc/nix-ssh-serve.nix
   ./services/misc/nixos-manual.nix
+  ./services/misc/rippled.nix
   ./services/misc/rogue.nix
   ./services/misc/svnserve.nix
   ./services/misc/synergy.nix
@@ -142,11 +163,14 @@
   ./services/network-filesystems/drbd.nix
   ./services/network-filesystems/nfsd.nix
   ./services/network-filesystems/openafs-client/default.nix
+  ./services/network-filesystems/rsyncd.nix
   ./services/network-filesystems/samba.nix
   ./services/networking/amuled.nix
   ./services/networking/avahi-daemon.nix
   ./services/networking/bind.nix
   ./services/networking/bitlbee.nix
+  ./services/networking/btsync.nix
+  ./services/networking/cjdns.nix
   ./services/networking/connman.nix
   ./services/networking/cntlm.nix
   ./services/networking/chrony.nix
@@ -170,8 +194,11 @@
   ./services/networking/ircd-hybrid/default.nix
   ./services/networking/kippo.nix
   ./services/networking/minidlna.nix
+  ./services/networking/murmur.nix
   ./services/networking/nat.nix
   ./services/networking/networkmanager.nix
+  ./services/networking/ngircd.nix
+  ./services/networking/notbit.nix
   ./services/networking/ntopng.nix
   ./services/networking/ntpd.nix
   ./services/networking/oidentd.nix
@@ -184,7 +211,10 @@
   ./services/networking/rdnssd.nix
   ./services/networking/rpcbind.nix
   ./services/networking/sabnzbd.nix
+  ./services/networking/searx.nix
+  ./services/networking/spiped.nix
   ./services/networking/supybot.nix
+  ./services/networking/syncthing.nix
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
   ./services/networking/tftpd.nix
@@ -200,6 +230,7 @@
   ./services/scheduling/cron.nix
   ./services/scheduling/fcron.nix
   ./services/search/elasticsearch.nix
+  ./services/search/solr.nix
   ./services/security/clamav.nix
   ./services/security/haveged.nix
   ./services/security/fprot.nix
@@ -222,8 +253,10 @@
   ./services/web-servers/lighttpd/cgit.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/nginx/default.nix
+  ./services/web-servers/phpfpm.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/varnish/default.nix
+  ./services/web-servers/winstone.nix
   ./services/web-servers/zope2.nix
   ./services/x11/desktop-managers/default.nix
   ./services/x11/display-managers/auto.nix
@@ -234,7 +267,6 @@
   ./services/x11/hardware/multitouch.nix
   ./services/x11/hardware/synaptics.nix
   ./services/x11/hardware/wacom.nix
-  ./services/x11/mesa.nix
   ./services/x11/window-managers/awesome.nix
   #./services/x11/window-managers/compiz.nix
   ./services/x11/window-managers/default.nix
@@ -282,6 +314,7 @@
   ./tasks/scsi-link-power-management.nix
   ./tasks/swraid.nix
   ./testing/service-runner.nix
+  ./virtualisation/container-config.nix
   ./virtualisation/containers.nix
   ./virtualisation/libvirtd.nix
   #./virtualisation/nova.nix
diff --git a/nixos/modules/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix
index 04ee76d8d3e9..f0d60bb6c428 100644
--- a/nixos/modules/profiles/clone-config.nix
+++ b/nixos/modules/profiles/clone-config.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, modules, ... }:
+{ config, lib, pkgs, modules, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
index 396dcf6c5d3b..605cc6aad1de 100644
--- a/nixos/modules/profiles/demo.nix
+++ b/nixos/modules/profiles/demo.nix
@@ -11,6 +11,6 @@
       createHome = true;
       useDefaultShell = true;
       password = "demo";
-      isSystemUser = false;
+      uid = 1000;
     };
 }
diff --git a/nixos/modules/profiles/headless.nix b/nixos/modules/profiles/headless.nix
index 541c46ca50c2..14fc905f783e 100644
--- a/nixos/modules/profiles/headless.nix
+++ b/nixos/modules/profiles/headless.nix
@@ -1,9 +1,9 @@
 # Common configuration for headless machines (e.g., Amazon EC2
 # instances).
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   sound.enable = false;
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
index 3b058c6e9715..5aab2a2954e7 100644
--- a/nixos/modules/profiles/installation-device.nix
+++ b/nixos/modules/profiles/installation-device.nix
@@ -1,7 +1,7 @@
 # Provide a basic configuration for installation devices like CDs.
-{ config, pkgs, modules, ... }:
+{ config, lib, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports =
@@ -45,7 +45,7 @@ with pkgs.lib;
 
     # Enable wpa_supplicant, but don't start it by default.
     networking.wireless.enable = true;
-    jobs.wpa_supplicant.startOn = pkgs.lib.mkOverride 50 "";
+    jobs.wpa_supplicant.startOn = mkOverride 50 "";
 
     # Tell the Nix evaluator to garbage collect more aggressively.
     # This is desirable in memory-constrained environments that don't
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index 821b9f93465a..5067622aaf16 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -1,11 +1,8 @@
 # This module defines a small NixOS configuration.  It does not
 # contain any graphical stuff.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
 {
-  # Don't include X libraries.
-  programs.ssh.setXAuthLocation = false;
-  fonts.enableFontConfig = false;
-  fonts.enableCoreFonts = false;
+  environment.noXlibs = true;
 }
diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix
index c8e6fd4aa768..0a92b7ace507 100644
--- a/nixos/modules/profiles/qemu-guest.nix
+++ b/nixos/modules/profiles/qemu-guest.nix
@@ -5,5 +5,13 @@
 
 {
   boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "9p" "9pnet_virtio" ];
-  boot.kernelModules = [ "virtio_balloon" "virtio_console" ];
+  boot.initrd.kernelModules = [ "virtio_balloon" "virtio_console" ];
+
+  boot.initrd.postDeviceCommands =
+    ''
+      # Set the system time from the hardware clock to work around a
+      # bug in qemu-kvm > 1.5.2 (where the VM clock is initialised
+      # to the *boot time* of the host).
+      hwclock -s
+    '';
 }
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
index 7fdaab9d67df..e457db22333b 100644
--- a/nixos/modules/programs/atop.nix
+++ b/nixos/modules/programs/atop.nix
@@ -1,8 +1,8 @@
 # Global configuration for atop.
 
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.programs.atop;
 
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 9d33e26c4959..9584f07b0945 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -1,9 +1,9 @@
 # This module defines global configuration for the Bash shell, in
 # particular /etc/bashrc and /etc/profile.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/programs/bash/command-not-found.nix b/nixos/modules/programs/bash/command-not-found.nix
index 502320446a37..8c86d48b0808 100644
--- a/nixos/modules/programs/bash/command-not-found.nix
+++ b/nixos/modules/programs/bash/command-not-found.nix
@@ -3,9 +3,9 @@
 # SQLite database that maps program names to Nix package names (e.g.,
 # "pdflatex" is mapped to "tetex").
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index 7c1922cdfd89..aa9aec078342 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -2,9 +2,9 @@
 
 # Most of the stuff here should probably be moved elsewhere sometime.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -17,8 +17,7 @@ in
   config = {
 
     environment.variables =
-      { LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
-        LOCATE_PATH = "/var/cache/locatedb";
+      { LOCATE_PATH = "/var/cache/locatedb";
         NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
         NIX_PATH =
           [ "/nix/var/nix/profiles/per-user/root/channels/nixos"
@@ -45,7 +44,7 @@ in
         TERMINFO_DIRS = [ "${i}/share/terminfo" ];
         PERL5LIB = [ "${i}/lib/perl5/site_perl" ];
         ALSA_PLUGIN_DIRS = [ "${i}/lib/alsa-lib" ];
-        GST_PLUGIN_PATH = [ "${i}/lib/gstreamer-0.10" ];
+        GST_PLUGIN_SYSTEM_PATH = [ "${i}/lib/gstreamer-0.10" ];
         KDEDIRS = [ "${i}" ];
         STRIGI_PLUGIN_PATH = [ "${i}/lib/strigi/" ];
         QT_PLUGIN_PATH = [ "${i}/lib/qt4/plugins" "${i}/lib/kde4/plugins" ];
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 9e46ab8b298f..a24350994992 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -1,8 +1,8 @@
 # Configuration for the pwdutils suite of tools: passwd, useradd, etc.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -58,7 +58,8 @@ in
 
   config = {
 
-    environment.systemPackages = [ pkgs.shadow ];
+    environment.systemPackages =
+      pkgs.lib.optional config.users.mutableUsers pkgs.shadow;
 
     environment.etc =
       [ { # /etc/login.defs: global configuration for pwdutils.  You
@@ -94,6 +95,8 @@ in
         groupmems = { rootOK = true; };
         groupdel = { rootOK = true; };
         login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
+        chpasswd = { rootOK = true; };
+        chgpasswd = { rootOK = true; };
       };
 
     security.setuidPrograms = [ "passwd" "chfn" "su" "newgrp" ];
diff --git a/nixos/modules/programs/shell.nix b/nixos/modules/programs/shell.nix
index 8052502c21ea..80d40a7c708c 100644
--- a/nixos/modules/programs/shell.nix
+++ b/nixos/modules/programs/shell.nix
@@ -1,8 +1,8 @@
 # This module defines a standard configuration for NixOS shells.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -28,34 +28,36 @@ in
             echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
         fi
 
-        if ! test -L $HOME/.nix-profile; then
-            if test "$USER" != root; then
-                ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
-            else
-                # Root installs in the system-wide profile by default.
-                ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
-            fi
-        fi
+        if test -w $HOME; then
+          if ! test -L $HOME/.nix-profile; then
+              if test "$USER" != root; then
+                  ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
+              else
+                  # Root installs in the system-wide profile by default.
+                  ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
+              fi
+          fi
 
-        # Subscribe the root user to the NixOS channel by default.
-        if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
-            echo "${config.system.defaultChannel} nixos" > $HOME/.nix-channels
-        fi
+          # Subscribe the root user to the NixOS channel by default.
+          if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
+              echo "${config.system.defaultChannel} nixos" > $HOME/.nix-channels
+          fi
 
-        # Create the per-user garbage collector roots directory.
-        NIX_USER_GCROOTS_DIR=/nix/var/nix/gcroots/per-user/$USER
-        mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
-        if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
-            echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
-        fi
+          # Create the per-user garbage collector roots directory.
+          NIX_USER_GCROOTS_DIR=/nix/var/nix/gcroots/per-user/$USER
+          mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
+          if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
+              echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
+          fi
 
-        # Set up a default Nix expression from which to install stuff.
-        if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
-            rm -f $HOME/.nix-defexpr
-            mkdir $HOME/.nix-defexpr
-            if [ "$USER" != root ]; then
-                ln -s /nix/var/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
-            fi
+          # Set up a default Nix expression from which to install stuff.
+          if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
+              rm -f $HOME/.nix-defexpr
+              mkdir $HOME/.nix-defexpr
+              if [ "$USER" != root ]; then
+                  ln -s /nix/var/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
+              fi
+          fi
         fi
       '';
 
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index a66679dff90d..fdb9dfd4b8c2 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -1,8 +1,8 @@
 # Global configuration for the SSH client.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg  = config.programs.ssh;
     cfgd = config.services.openssh;
@@ -31,7 +31,7 @@ in
 
       setXAuthLocation = mkOption {
         type = types.bool;
-        default = true;
+        default = config.services.xserver.enable;
         description = ''
           Whether to set the path to <command>xauth</command> for X11-forwarded connections.
           This causes a dependency on X11 packages.
@@ -47,7 +47,20 @@ in
           for help.
         '';
       };
+
+      startAgent = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to start the OpenSSH agent when you log in.  The OpenSSH agent
+          remembers private keys for you so that you don't have to type in
+          passphrases every time you make an SSH connection.  Use
+          <command>ssh-add</command> to add a key to the agent.
+        '';
+      };
+
     };
+
   };
 
   config = {
@@ -71,5 +84,28 @@ in
           target = "ssh/ssh_config";
         }
       ];
+
+    # FIXME: this should really be socket-activated for über-awesomeness.
+    systemd.user.services.ssh-agent =
+      { enable = cfg.startAgent;
+        description = "SSH Agent";
+        wantedBy = [ "default.target" ];
+        serviceConfig =
+          { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
+            ExecStart = "${pkgs.openssh}/bin/ssh-agent -a %t/ssh-agent";
+            StandardOutput = "null";
+            Type = "forking";
+            Restart = "on-failure";
+            SuccessExitStatus = "0 2";
+          };
+      };
+
+    environment.extraInit = optionalString cfg.startAgent
+      ''
+        if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then
+          export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"
+        fi
+      '';
+
   };
 }
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
index 904989d57a09..34eafd4fa846 100644
--- a/nixos/modules/programs/ssmtp.nix
+++ b/nixos/modules/programs/ssmtp.nix
@@ -3,9 +3,9 @@
 # directly to an SMTP server defined in its configuration file, wihout
 # queueing mail locally.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix
index 7ab653dd19fd..2b70a795f4fd 100644
--- a/nixos/modules/programs/venus.nix
+++ b/nixos/modules/programs/venus.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 let
   cfg = config.services.venus;
 
diff --git a/nixos/modules/programs/virtualbox.nix b/nixos/modules/programs/virtualbox.nix
index 340fec0496ae..10e657abd3c5 100644
--- a/nixos/modules/programs/virtualbox.nix
+++ b/nixos/modules/programs/virtualbox.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let virtualbox = config.boot.kernelPackages.virtualbox; in
 
diff --git a/nixos/modules/programs/wvdial.nix b/nixos/modules/programs/wvdial.nix
index da3f7dce98a1..8e7d0e51a4e0 100644
--- a/nixos/modules/programs/wvdial.nix
+++ b/nixos/modules/programs/wvdial.nix
@@ -1,8 +1,8 @@
 # Global configuration for wvdial.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index cff751934d7d..7bcf8da2c659 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -1,8 +1,8 @@
 # This module defines global configuration for the zshell.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -116,8 +116,9 @@ in
         # This file is read for all shells.
 
         # Only execute this file once per shell.
+        # But don't clobber the environment of interactive non-login children!
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
-        __ETC_ZSHENV_SOURCED=1
+        export __ETC_ZSHENV_SOURCED=1
 
         ${cfg.shellInit}
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 6ff5277cf9ca..0a67aeb81e56 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, options, ... }:
+{ config, lib, options, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -12,18 +12,20 @@ let
     visible = true;
   };
 
+  # warn option was renamed
   obsolete = from: to: rename {
     inherit from to;
     name = "Obsolete name";
-    use = x: builtins.trace "Obsolete option `${showOption from}' is used instead of `${showOption to}'." x;
-    define = x: builtins.trace "Obsolete option `${showOption from}' is defined instead of `${showOption to}'." x;
+    use = x: builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'." x;
+    define = x: builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'." x;
   };
 
+  # abort if deprecated option is used
   deprecated = from: to: rename {
     inherit from to;
     name = "Deprecated name";
-    use = x: abort "Deprecated option `${showOption from}' is used instead of `${showOption to}'.";
-    define = x: abort "Deprecated option `${showOption from}' is defined instead of `${showOption to}'.";
+    use = x: abort "Deprecated option `${showOption from}' is used. It was renamed to `${showOption to}'.";
+    define = x: abort "Deprecated option `${showOption from}' is used. It was renamed to `${showOption to}'.";
   };
 
   showOption = concatStringsSep ".";
@@ -54,7 +56,7 @@ let
             inherit visible;
           });
         }
-        { config = setTo (mkIf (fromOf options).isDefined (define (mkMerge (fromOf options).definitions)));
+        { config = setTo (mkMerge (if (fromOf options).isDefined then [ (define (mkMerge (fromOf options).definitions)) ] else []));
         }
       ];
 
@@ -101,6 +103,8 @@ in zipModules ([]
 ++ obsolete [ "services" "sshd" "gatewayPorts" ] [ "services" "openssh" "gatewayPorts" ]
 ++ obsolete [ "services" "sshd" "permitRootLogin" ] [ "services" "openssh" "permitRootLogin" ]
 ++ obsolete [ "services" "xserver" "startSSHAgent" ] [ "services" "xserver" "startOpenSSHAgent" ]
+++ obsolete [ "services" "xserver" "startOpenSSHAgent" ] [ "programs" "ssh" "startAgent" ]
+++ obsolete [ "services" "xserver" "windowManager" "xbmc" ] [ "services" "xserver" "desktopManager" "xbmc" ]
 
 # KDE
 ++ deprecated [ "kde" "extraPackages" ] [ "environment" "kdePackages" ]
@@ -113,10 +117,12 @@ in zipModules ([]
 # !!! this hardcodes bash, could we detect from config which shell is actually used?
 ++ obsolete [ "environment" "promptInit" ] [ "programs" "bash" "promptInit" ]
 
-++ obsolete [ "services" "xserver" "driSupport" ] [ "services" "mesa" "driSupport" ]
-++ obsolete [ "services" "xserver" "driSupport32Bit" ] [ "services" "mesa" "driSupport32Bit" ]
-++ obsolete [ "services" "xserver" "s3tcSupport" ] [ "services" "mesa" "s3tcSupport" ]
-++ obsolete [ "services" "xserver" "videoDrivers" ] [ "services" "mesa" "videoDrivers" ]
+++ obsolete [ "services" "xserver" "driSupport" ] [ "hardware" "opengl" "driSupport" ]
+++ obsolete [ "services" "xserver" "driSupport32Bit" ] [ "hardware" "opengl" "driSupport32Bit" ]
+++ obsolete [ "services" "xserver" "s3tcSupport" ] [ "hardware" "opengl" "s3tcSupport" ]
+++ obsolete [ "services" "xserver" "videoDrivers" ] [ "hardware" "opengl" "videoDrivers" ]
+
+++ obsolete [ "services" "mysql55" ] [ "services" "mysql" ]
 
 # Options that are obsolete and have no replacement.
 ++ obsolete' [ "boot" "loader" "grub" "bootDevice" ]
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
index bc661164fdc2..b89b379ae666 100644
--- a/nixos/modules/security/apparmor-suid.nix
+++ b/nixos/modules/security/apparmor-suid.nix
@@ -1,8 +1,8 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 let
   cfg = config.security.apparmor;
 in
-with pkgs.lib;
+with lib;
 {
 
   options.security.apparmor.confineSUIDApplications = mkOption {
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
index b9f151590028..da7c93beee98 100644
--- a/nixos/modules/security/apparmor.nix
+++ b/nixos/modules/security/apparmor.nix
@@ -1,55 +1,39 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
+
+with lib;
 
 let
   cfg = config.security.apparmor;
 in
-
-with pkgs.lib;
-
 {
-
-  ###### interface
-
   options = {
-
     security.apparmor = {
-
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
-          Enable AppArmor application security system. Enable only if
-          you want to further improve AppArmor.
-        '';
+        description = "Enable the AppArmor Mandatory Access Control system.";
       };
 
       profiles = mkOption {
         type = types.listOf types.path;
         default = [];
-        description = ''
-          List of file names of AppArmor profiles.
-        '';
+        description = "List of files containing AppArmor profiles.";
       };
-
     };
   };
 
-
-  ###### implementation
-
-  config = mkIf (cfg.enable) {
-
-    assertions = [ { assertion = config.boot.kernelPackages.kernel.features ? apparmor
-                               && config.boot.kernelPackages.kernel.features.apparmor;
-                     message = "AppArmor is enabled, but the kernel doesn't have AppArmor support"; }
-                 ];
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = config.boot.kernelPackages.kernel.features ? apparmor
+                   && config.boot.kernelPackages.kernel.features.apparmor;
+          message = "Your selected kernel does not have AppArmor support";
+        }
+      ];
 
     environment.systemPackages = [ pkgs.apparmor ];
-
     systemd.services.apparmor = {
-      #wantedBy = [ "basic.target" ];
       wantedBy = [ "local-fs.target" ];
-      path = [ pkgs.apparmor ];
+      path     = [ pkgs.apparmor ];
 
       serviceConfig = {
         Type = "oneshot";
@@ -61,9 +45,6 @@ with pkgs.lib;
           ''${pkgs.apparmor}/sbin/apparmor_parser -Rv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; ''
         ) cfg.profiles;
       };
-
     };
-
   };
-
 }
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index 2e93fb36b450..f159e359f968 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
@@ -10,11 +10,6 @@ with pkgs.lib;
       [ { source = "${pkgs.cacert}/etc/ca-bundle.crt";
           target = "ssl/certs/ca-bundle.crt";
         }
-
-        # Backward compatibility; may remove at some point.
-        { source = "${pkgs.cacert}/etc/ca-bundle.crt";
-          target = "ca-bundle.crt";
-        }
       ];
 
     environment.variables.OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt";
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
new file mode 100644
index 000000000000..bd9611384828
--- /dev/null
+++ b/nixos/modules/security/duosec.nix
@@ -0,0 +1,198 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.security.duosec;
+
+  boolToStr = b: if b then "yes" else "no";
+
+  configFile = ''
+    [duo]
+    ikey=${cfg.ikey}
+    skey=${cfg.skey}
+    host=${cfg.host}
+    ${optionalString (cfg.group != "") ("group="+cfg.group)}
+    failmode=${cfg.failmode}
+    pushinfo=${boolToStr cfg.pushinfo}
+    autopush=${boolToStr cfg.autopush}
+    motd=${boolToStr cfg.motd}
+    prompts=${toString cfg.prompts}
+    accept_env_factor=${boolToStr cfg.acceptEnvFactor}
+    fallback_local_ip=${boolToStr cfg.fallbackLocalIP}
+  '';
+
+  loginCfgFile = optional cfg.ssh.enable
+    { source = pkgs.writeText "login_duo.conf" configFile;
+      mode   = "0600";
+      uid    = config.ids.uids.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;
+      target = "duo/pam_duo.conf";
+    };
+in
+{
+  options = {
+    security.duosec = {
+      ssh.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, protect SSH logins with Duo Security.";
+      };
+
+      pam.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, protect logins with Duo Security using PAM support.";
+      };
+
+      ikey = mkOption {
+        type = types.str;
+        description = "Integration key.";
+      };
+
+      skey = mkOption {
+        type = types.str;
+        description = "Secret key.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        description = "Duo API hostname.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "";
+        description = "Use Duo authentication for users only in this group.";
+      };
+
+      failmode = mkOption {
+        type = types.str;
+        default = "safe";
+        description = ''
+          On service or configuration errors that prevent Duo
+          authentication, fail "safe" (allow access) or "secure" (deny
+          access). The default is "safe".
+        '';
+      };
+
+      pushinfo = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Include information such as the command to be executed in
+          the Duo Push message.
+        '';
+      };
+
+      autopush = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If <literal>true</literal>, Duo Unix will automatically send
+          a push login request to the user’s phone, falling back on a
+          phone call if push is unavailable. If
+          <literal>false</literal>, the user will be prompted to
+          choose an authentication method. When configured with
+          <literal>autopush = yes</literal>, we recommend setting
+          <literal>prompts = 1</literal>.
+        '';
+      };
+
+      motd = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Print the contents of <literal>/etc/motd</literal> to screen
+          after a succesful login.
+        '';
+      };
+
+      prompts = mkOption {
+        type = types.int;
+        default = 3;
+        description = ''
+          If a user fails to authenticate with a second factor, Duo
+          Unix will prompt the user to authenticate again. This option
+          sets the maximum number of prompts that Duo Unix will
+          display before denying access. Must be 1, 2, or 3. Default
+          is 3.
+
+          For example, when <literal>prompts = 1</literal>, the user
+          will have to successfully authenticate on the first prompt,
+          whereas if <literal>prompts = 2</literal>, if the user
+          enters incorrect information at the initial prompt, he/she
+          will be prompted to authenticate again.
+
+          When configured with <literal>autopush = true</literal>, we
+          recommend setting <literal>prompts = 1</literal>.
+        '';
+      };
+
+      acceptEnvFactor = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Look for factor selection or passcode in the
+          <literal>$DUO_PASSCODE</literal> environment variable before
+          prompting the user for input.
+
+          When $DUO_PASSCODE is non-empty, it will override
+          autopush. The SSH client will need SendEnv DUO_PASSCODE in
+          its configuration, and the SSH server will similarily need
+          AcceptEnv DUO_PASSCODE.
+        '';
+      };
+
+      fallbackLocalIP = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Duo Unix reports the IP address of the authorizing user, for
+          the purposes of authorization and whitelisting. If Duo Unix
+          cannot detect the IP address of the client, setting
+          <literal>fallbackLocalIP = yes</literal> will cause Duo Unix
+          to send the IP address of the server it is running on.
+
+          If you are using IP whitelisting, enabling this option could
+          cause unauthorized logins if the local IP is listed in the
+          whitelist.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
+    assertions =
+      [ { assertion = cfg.failmode == "safe" || cfg.failmode == "secure";
+          message   = "Invalid value for failmode (must be safe or secure).";
+        }
+        { assertion = cfg.prompts == 1 || cfg.prompts == 2 || cfg.prompts == 3;
+          message   = "Invalid value for prompts (must be 1, 2, or 3).";
+        }
+        { assertion = !cfg.pam.enable;
+          message   = "PAM support is currently not implemented.";
+        }
+      ];
+
+     environment.systemPackages = [ pkgs.duo-unix ];
+     security.setuidPrograms    = [ "login_duo" ];
+     environment.etc = loginCfgFile ++ pamCfgFile;
+
+     /* If PAM *and* SSH are enabled, then don't do anything special.
+     If PAM isn't used, set the default SSH-only options. */
+     services.openssh.extraConfig = mkIf (cfg.ssh.enable || cfg.pam.enable) (
+     if cfg.pam.enable then "UseDNS no" else ''
+       # Duo Security configuration
+       ForceCommand ${config.security.wrapperDir}/login_duo
+       PermitTunnel no
+       AllowTcpForwarding no
+     '');
+  };
+}
diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix
new file mode 100644
index 000000000000..90462a2d6d0d
--- /dev/null
+++ b/nixos/modules/security/grsecurity.nix
@@ -0,0 +1,443 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.security.grsecurity;
+
+  mkKernel = kernel: patch:
+    assert patch.kversion == kernel.version;
+      { inherit kernel patch;
+        inherit (patch) grversion revision;
+      };
+
+  stable-patch = with pkgs.kernelPatches;
+    if cfg.vserver then grsecurity_vserver else grsecurity_stable;
+  stableKernel = mkKernel pkgs.linux_3_2  stable-patch;
+  testKernel   = mkKernel pkgs.linux_3_13 pkgs.kernelPatches.grsecurity_unstable;
+
+  ## -- grsecurity configuration -----------------------------------------------
+
+  grsecPrioCfg =
+    if cfg.config.priority == "security" then
+      "GRKERNSEC_CONFIG_PRIORITY_SECURITY y"
+    else
+      "GRKERNSEC_CONFIG_PRIORITY_PERF y";
+
+  grsecSystemCfg =
+    if cfg.config.system == "desktop" then
+      "GRKERNSEC_CONFIG_DESKTOP y"
+    else
+      "GRKERNSEC_CONFIG_SERVER y";
+
+  grsecVirtCfg =
+    if cfg.config.virtualisationConfig == "none" then
+      "GRKERNSEC_CONFIG_VIRT_NONE y"
+    else if cfg.config.virtualisationConfig == "host" then
+      "GRKERNSEC_CONFIG_VIRT_HOST y"
+    else
+      "GRKERNSEC_CONFIG_VIRT_GUEST y";
+
+  grsecHwvirtCfg = if cfg.config.virtualisationConfig == "none" then "" else
+    if cfg.config.hardwareVirtualisation == true then
+      "GRKERNSEC_CONFIG_VIRT_EPT y"
+    else
+      "GRKERNSEC_CONFIG_VIRT_SOFT y";
+
+  grsecVirtswCfg =
+    let virtCfg = opt: "GRKERNSEC_CONFIG_VIRT_"+opt+" y";
+    in
+      if cfg.config.virtualisationConfig == "none" then ""
+      else if cfg.config.virtualisationSoftware == "xen"    then virtCfg "XEN"
+      else if cfg.config.virtualisationSoftware == "kvm"    then virtCfg "KVM"
+      else if cfg.config.virtualisationSoftware == "vmware" then virtCfg "VMWARE"
+      else                                                       virtCfg "VIRTUALBOX";
+
+  grsecMainConfig = if cfg.config.mode == "custom" then "" else ''
+    GRKERNSEC_CONFIG_AUTO y
+    ${grsecPrioCfg}
+    ${grsecSystemCfg}
+    ${grsecVirtCfg}
+    ${grsecHwvirtCfg}
+    ${grsecVirtswCfg}
+  '';
+
+  grsecConfig =
+    let boolToKernOpt = b: if b then "y" else "n";
+        # Disable RANDSTRUCT under virtualbox, as it has some kind of
+        # breakage with the vbox guest drivers
+        randstruct = optionalString config.services.virtualbox.enable
+          "GRKERNSEC_RANDSTRUCT n";
+        # Disable restricting links under the testing kernel, as something
+        # has changed causing it to fail miserably during boot.
+        restrictLinks = optionalString cfg.testing
+          "GRKERNSEC_LINK n";
+    in ''
+      SECURITY_APPARMOR y
+      DEFAULT_SECURITY_APPARMOR y
+      GRKERNSEC y
+      ${grsecMainConfig}
+
+      ${if cfg.config.restrictProc then
+          "GRKERNSEC_PROC_USER y"
+        else
+          optionalString cfg.config.restrictProcWithGroup ''
+            GRKERNSEC_PROC_USERGROUP y
+            GRKERNSEC_PROC_GID ${toString cfg.config.unrestrictProcGid}
+          ''
+      }
+
+      GRKERNSEC_SYSCTL ${boolToKernOpt cfg.config.sysctl}
+      GRKERNSEC_CHROOT_CHMOD ${boolToKernOpt cfg.config.denyChrootChmod}
+      GRKERNSEC_NO_RBAC ${boolToKernOpt cfg.config.disableRBAC}
+      ${randstruct}
+      ${restrictLinks}
+
+      ${cfg.config.kernelExtraConfig}
+    '';
+
+  ## -- grsecurity kernel packages ---------------------------------------------
+
+  localver = grkern:
+    "-grsec" + optionalString cfg.config.verboseVersion
+       "-${grkern.grversion}-${grkern.revision}";
+
+  grsecurityOverrider = args: grkern: {
+    # Apparently as of gcc 4.6, gcc-plugin headers (which are needed by PaX plugins)
+    # include libgmp headers, so we need these extra tweaks
+    buildInputs = args.buildInputs ++ [ pkgs.gmp ];
+    preConfigure = ''
+      ${args.preConfigure or ""}
+      sed -i 's|-I|-I${pkgs.gmp}/include -I|' scripts/gcc-plugin.sh
+      sed -i 's|HOST_EXTRACFLAGS +=|HOST_EXTRACFLAGS += -I${pkgs.gmp}/include|' tools/gcc/Makefile
+      sed -i 's|HOST_EXTRACXXFLAGS +=|HOST_EXTRACXXFLAGS += -I${pkgs.gmp}/include|' tools/gcc/Makefile
+      rm localversion-grsec
+      echo ${localver grkern} > localversion-grsec
+    '';
+  };
+
+  mkGrsecPkg = grkern:
+    let kernelPkg = lowPrio (overrideDerivation (grkern.kernel.override (args: {
+          kernelPatches = args.kernelPatches ++ [ grkern.patch pkgs.kernelPatches.grsec_fix_path ];
+          argsOverride = {
+            modDirVersion = "${grkern.kernel.modDirVersion}${localver grkern}";
+          };
+          extraConfig = grsecConfig;
+        })) (args: grsecurityOverrider args grkern));
+    in pkgs.linuxPackagesFor kernelPkg (mkGrsecPkg grkern);
+
+  grsecPackage = mkGrsecPkg (if cfg.stable then stableKernel else testKernel);
+in
+{
+  options = {
+    security.grsecurity = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable grsecurity support. This enables advanced exploit
+          hardening for the Linux kernel, and adds support for
+          administrative Role-Based Acess Control (RBAC) via
+          <literal>gradm</literal>. It also includes traditional
+          utilities for PaX.
+        '';
+      };
+
+      stable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the stable grsecurity patch, based on Linux 3.2.
+        '';
+      };
+
+      vserver = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the stable grsecurity/vserver patches, based on Linux 3.2.
+        '';
+      };
+
+      testing = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the testing grsecurity patch, based on Linux 3.13.
+        '';
+      };
+
+      config = {
+        mode = mkOption {
+          type = types.str;
+          default = "auto";
+          example = "custom";
+          description = ''
+            grsecurity configuration mode. This specifies whether
+            grsecurity is auto-configured or otherwise completely
+            manually configured. Can either by
+            <literal>custom</literal> or <literal>auto</literal>.
+
+            <literal>auto</literal> is recommended.
+          '';
+        };
+
+        priority = mkOption {
+          type = types.str;
+          default = "security";
+          example = "performance";
+          description = ''
+            grsecurity configuration priority. This specifies whether
+            the kernel configuration should emphasize speed or
+            security. Can either by <literal>security</literal> or
+            <literal>performance</literal>.
+          '';
+        };
+
+        system = mkOption {
+          type = types.str;
+          default = "";
+          example = "desktop";
+          description = ''
+            grsecurity system configuration. This specifies whether
+            the kernel configuration should be suitable for a Desktop
+            or a Server. Can either by <literal>server</literal> or
+            <literal>desktop</literal>.
+          '';
+        };
+
+        virtualisationConfig = mkOption {
+          type = types.str;
+          default = "none";
+          example = "host";
+          description = ''
+            grsecurity virtualisation configuration. This specifies
+            the virtualisation role of the machine - that is, whether
+            it will be a virtual machine guest, a virtual machine
+            host, or neither. Can be one of <literal>none</literal>,
+            <literal>host</literal>, or <literal>guest</literal>.
+          '';
+        };
+
+        hardwareVirtualisation = mkOption {
+          type = types.nullOr types.bool;
+          default = null;
+          example = true;
+          description = ''
+            grsecurity hardware virtualisation configuration. Set to
+            <literal>true</literal> if your machine supports hardware
+            accelerated virtualisation.
+          '';
+        };
+
+        virtualisationSoftware = mkOption {
+          type = types.str;
+          default = "";
+          example = "kvm";
+          description = ''
+            grsecurity virtualisation software. Set this to the
+            specified virtual machine technology if the machine is
+            running as a guest, or a host.
+
+            Can be one of <literal>kvm</literal>,
+            <literal>xen</literal>, <literal>vmware</literal> or
+            <literal>virtualbox</literal>.
+          '';
+        };
+
+        sysctl = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            If true, then set <literal>GRKERN_SYSCTL y</literal>. If
+            enabled then grsecurity can be controlled using sysctl
+            (and turned off). You are advised to *never* enable this,
+            but if you do, make sure to always set the sysctl
+            <literal>kernel.grsecurity.grsec_lock</literal> to
+            non-zero as soon as all sysctl options are set. *THIS IS
+            EXTREMELY IMPORTANT*!
+
+            If disabled, this also turns off the
+            <literal>systemd-sysctl</literal> service.
+          '';
+        };
+
+        denyChrootChmod = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            If true, then set <literal>GRKERN_CHROOT_CHMOD
+            y</literal>. If enabled, this denies processes inside a
+            chroot from setting the suid or sgid bits using
+            <literal>chmod</literal> or <literal>fchmod</literal>.
+
+            By default this protection is disabled - it makes it
+            impossible to use Nix to build software on your system,
+            which is what most users want.
+
+            If you are using NixOps to deploy your software to a
+            remote machine, you're encouraged to enable this as you
+            won't need to compile code.
+          '';
+        };
+
+        restrictProc = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            If true, then set <literal>GRKERN_PROC_USER
+            y</literal>. This restricts non-root users to only viewing
+            their own processes and restricts network-related
+            information, kernel symbols, and module information.
+          '';
+        };
+
+        restrictProcWithGroup = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            If true, then set <literal>GRKERN_PROC_USERGROUP
+            y</literal>. This is similar to
+            <literal>restrictProc</literal> except it allows a special
+            group (specified by <literal>unrestrictProcGid</literal>)
+            to still access otherwise classified information in
+            <literal>/proc</literal>.
+          '';
+        };
+
+        unrestrictProcGid = mkOption {
+          type = types.int;
+          default = config.ids.gids.grsecurity;
+          description = ''
+            If set, specifies a GID which is exempt from
+            <literal>/proc</literal> restrictions (set by
+            <literal>GRKERN_PROC_USERGROUP</literal>). By default,
+            this is set to the GID for <literal>grsecurity</literal>,
+            a predefined NixOS group, which the
+            <literal>root</literal> account is a member of. You may
+            conveniently add other users to this group if you need
+            access to <literal>/proc</literal>
+          '';
+        };
+
+        disableRBAC = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            If true, then set <literal>GRKERN_NO_RBAC
+            y</literal>. This disables the
+            <literal>/dev/grsec</literal> device, which in turn
+            disables the RBAC system (and <literal>gradm</literal>).
+          '';
+        };
+
+        verboseVersion = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Use verbose version in kernel localversion.";
+        };
+
+        kernelExtraConfig = mkOption {
+          type = types.str;
+          default = "";
+          description = "Extra kernel configuration parameters.";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.stable || cfg.testing;
+          message   = ''
+            If grsecurity is enabled, you must select either the
+            stable patch (with kernel 3.2), or the testing patch (with
+            kernel 3.13) to continue.
+          '';
+        }
+        { assertion = (cfg.stable -> !cfg.testing) || (cfg.testing -> !cfg.stable);
+          message   = ''
+            You must select either the stable or testing patch, not
+            both.
+          '';
+        }
+        { assertion = (cfg.testing -> !cfg.vserver);
+          message   = "The vserver patches are only supported in the stable kernel.";
+        }
+        { assertion = (cfg.config.restrictProc -> !cfg.config.restrictProcWithGroup) ||
+                      (cfg.config.restrictProcWithGroup -> !cfg.config.restrictProc);
+          message   = "You cannot enable both restrictProc and restrictProcWithGroup";
+        }
+        { assertion = config.boot.kernelPackages.kernel.features ? grsecurity
+                   && config.boot.kernelPackages.kernel.features.grsecurity;
+          message = "grsecurity enabled, but kernel doesn't have grsec support";
+        }
+        { assertion = elem cfg.config.mode [ "auto" "custom" ];
+          message = "grsecurity mode must either be 'auto' or 'custom'.";
+        }
+        { assertion = cfg.config.mode == "auto" -> elem cfg.config.system [ "desktop" "server" ];
+          message = "when using auto grsec mode, system must be either 'desktop' or 'server'";
+        }
+        { assertion = cfg.config.mode == "auto" -> elem cfg.config.priority [ "performance" "security" ];
+          message = "when using auto grsec mode, priority must be 'performance' or 'security'.";
+        }
+        { assertion = cfg.config.mode == "auto" -> elem cfg.config.virtualisationConfig [ "host" "guest" "none" ];
+          message = "when using auto grsec mode, 'virt' must be 'host', 'guest' or 'none'.";
+        }
+        { assertion = (cfg.config.mode == "auto" && (elem cfg.config.virtualisationConfig [ "host" "guest" ])) ->
+              cfg.config.hardwareVirtualisation != null;
+          message   = "when using auto grsec mode with virtualisation, you must specify if your hardware has virtualisation extensions";
+        }
+        { assertion = (cfg.config.mode == "auto" && (elem cfg.config.virtualisationConfig [ "host" "guest" ])) ->
+              elem cfg.config.virtualisationSoftware [ "kvm" "xen" "virtualbox" "vmware" ];
+          message   = "virtualisation software must be 'kvm', 'xen', 'vmware' or 'virtualbox'";
+        }
+      ];
+
+    systemd.services.grsec-lock = mkIf cfg.config.sysctl {
+      description     = "grsecurity sysctl-lock Service";
+      requires        = [ "sysctl.service" ];
+      wantedBy        = [ "multi-user.target" ];
+      serviceConfig.Type = "oneshot";
+      serviceConfig.RemainAfterExit = "yes";
+      script = ''
+        locked=`cat /proc/sys/kernel/grsecurity/grsec_lock`
+        if [ "$locked" == "0" ]; then
+            echo 1 > /proc/sys/kernel/grsecurity/grsec_lock
+            echo grsecurity sysctl lock - enabled
+        else
+            echo grsecurity sysctl lock already enabled - doing nothing
+        fi
+      '';
+    };
+
+#   systemd.services.grsec-learn = {
+#     description     = "grsecurity learning Service";
+#     wantedBy        = [ "local-fs.target" ];
+#     serviceConfig   = {
+#       Type = "oneshot";
+#       RemainAfterExit = "yes";
+#       ExecStart = "${pkgs.gradm}/sbin/gradm -VFL /etc/grsec/learning.logs";
+#       ExecStop  = "${pkgs.gradm}/sbin/gradm -D";
+#     };
+#   };
+
+    system.activationScripts.grsec =
+      ''
+        mkdir -p /etc/grsec
+        if [ ! -f /etc/grsec/learn_config ]; then
+          cp ${pkgs.gradm}/etc/grsec/learn_config /etc/grsec
+        fi
+        if [ ! -f /etc/grsec/policy ]; then
+          cp ${pkgs.gradm}/etc/grsec/policy /etc/grsec
+        fi
+        chmod -R 0600 /etc/grsec
+      '';
+
+    # Enable apparmor support, gradm udev rules, and utilities
+    security.apparmor.enable   = true;
+    boot.kernelPackages        = grsecPackage;
+    services.udev.packages     = [ pkgs.gradm ];
+    environment.systemPackages = [ pkgs.gradm pkgs.paxctl pkgs.pax-utils ];
+  };
+}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 93d12d292e4d..6a5eb4c720f8 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -1,9 +1,9 @@
 # This module provides configuration for the PAM (Pluggable
 # Authentication Modules) system.
 
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -187,6 +187,8 @@ let
 
           # Session management.
           session required pam_unix.so
+          ${optionalString cfg.setLoginUid
+              "session required pam_loginuid.so"}
           ${optionalString cfg.updateWtmp
               "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
           ${optionalString config.users.ldap.enable
@@ -197,8 +199,6 @@ let
               "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
           ${optionalString cfg.startSession
               "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
-          ${optionalString cfg.setLoginUid
-              "session required pam_loginuid.so"}
           ${optionalString cfg.forwardXAuth
               "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
           ${optionalString (cfg.limits != [])
diff --git a/nixos/modules/security/pam_usb.nix b/nixos/modules/security/pam_usb.nix
index 4cc99995fbca..11708a1f0167 100644
--- a/nixos/modules/security/pam_usb.nix
+++ b/nixos/modules/security/pam_usb.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
index dbec4ad98d16..5933cdc0cec3 100644
--- a/nixos/modules/security/polkit.nix
+++ b/nixos/modules/security/polkit.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -63,6 +63,9 @@ in
 
     systemd.packages = [ pkgs.polkit ];
 
+    systemd.services.polkit.restartTriggers = [ config.system.path ];
+    systemd.services.polkit.unitConfig.X-StopIfChanged = false;
+
     # The polkit daemon reads action/rule files
     environment.pathsToLink = [ "/share/polkit-1" ];
 
diff --git a/nixos/modules/security/prey.nix b/nixos/modules/security/prey.nix
index 75b95d5fbb04..e29fa5395a1a 100644
--- a/nixos/modules/security/prey.nix
+++ b/nixos/modules/security/prey.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.prey;
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
index 720ac02f2e81..c31e57e6f6f8 100644
--- a/nixos/modules/security/rngd.nix
+++ b/nixos/modules/security/rngd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
diff --git a/nixos/modules/security/rtkit.nix b/nixos/modules/security/rtkit.nix
index 164ad9b3aa7d..ba07591bb770 100644
--- a/nixos/modules/security/rtkit.nix
+++ b/nixos/modules/security/rtkit.nix
@@ -1,9 +1,9 @@
 # A module for ‘rtkit’, a DBus system service that hands out realtime
 # scheduling priority to processes that ask for it.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/security/setuid-wrapper.c b/nixos/modules/security/setuid-wrapper.c
index 007ffbc34fe9..ffd0b65b7629 100644
--- a/nixos/modules/security/setuid-wrapper.c
+++ b/nixos/modules/security/setuid-wrapper.c
@@ -30,8 +30,8 @@ int main(int argc, char * * argv)
        creating hard link `X' from some other location, along with a
        false `X.real' file, to allow arbitrary programs from being
        executed setuid.  */
-    assert ((strncmp(self, wrapperDir, sizeof(wrapperDir)) == 0) &&
-	    (self[strlen(wrapperDir)] == '/'));
+    assert ((strncmp(self, wrapperDir, strlen(wrapperDir)) == 0) &&
+            (self[strlen(wrapperDir)] == '/'));
 
     /* Make *really* *really* sure that we were executed as `self',
        and not, say, as some other setuid program.  That is, our
@@ -42,12 +42,12 @@ int main(int argc, char * * argv)
     assert (lstat(self, &st) != -1);
 
     //printf("%d %d\n", st.st_uid, st.st_gid);
-    
+
     assert ((st.st_mode & S_ISUID) == 0 ||
-	    (st.st_uid == geteuid()));
+            (st.st_uid == geteuid()));
 
     assert ((st.st_mode & S_ISGID) == 0 ||
-	    st.st_gid == getegid());
+            st.st_gid == getegid());
 
     /* And, of course, we shouldn't be writable. */
     assert (!(st.st_mode & (S_IWGRP | S_IWOTH)));
@@ -69,13 +69,13 @@ int main(int argc, char * * argv)
     real[len] = 0;
 
     close(fdSelf);
-    
+
     //printf("real = %s, len = %d\n", real, len);
 
     execve(real, argv, environ);
 
     fprintf(stderr, "%s: cannot run `%s': %s\n",
         argv[0], real, strerror(errno));
-    
+
     exit(1);
 }
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
index 62df85816e50..4cdc1023baab 100644
--- a/nixos/modules/security/setuid-wrappers.nix
+++ b/nixos/modules/security/setuid-wrappers.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -9,10 +9,11 @@ let
   setuidWrapper = pkgs.stdenv.mkDerivation {
     name = "setuid-wrapper";
     buildCommand = ''
-      ensureDir $out/bin
+      mkdir -p $out/bin
+      cp ${./setuid-wrapper.c} setuid-wrapper.c
       gcc -Wall -O2 -DWRAPPER_DIR=\"${wrapperDir}\" \
-          ${./setuid-wrapper.c} -o $out/bin/setuid-wrapper
-      strip -s $out/bin/setuid-wrapper
+          setuid-wrapper.c -o $out/bin/setuid-wrapper
+      strip -S $out/bin/setuid-wrapper
     '';
   };
 
@@ -46,6 +47,7 @@ in
             group = "postdrop";
             setuid = false;
             setgid = true;
+            permissions = "u+rx,g+x,o+x";
           }
         ];
       description = ''
@@ -115,8 +117,7 @@ in
           # programs to be wrapped.
           SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin
 
-          if test -d ${wrapperDir}; then rm -f ${wrapperDir}/*; fi # */
-          mkdir -p ${wrapperDir}
+          rm -f ${wrapperDir}/* # */
 
           ${concatMapStrings makeSetuidWrapper setuidPrograms}
         '';
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index 215a8ecd6018..956856c64995 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/amqp/activemq/default.nix b/nixos/modules/services/amqp/activemq/default.nix
index 915d179e6999..f1f3d005f30f 100644
--- a/nixos/modules/services/amqp/activemq/default.nix
+++ b/nixos/modules/services/amqp/activemq/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 with pkgs;
 
 let
diff --git a/nixos/modules/services/amqp/rabbitmq.nix b/nixos/modules/services/amqp/rabbitmq.nix
index 696b5ad43797..bef15fb64b7f 100644
--- a/nixos/modules/services/amqp/rabbitmq.nix
+++ b/nixos/modules/services/amqp/rabbitmq.nix
@@ -1,22 +1,13 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
-
   cfg = config.services.rabbitmq;
 
-  run = cmd: "${pkgs.sudo}/bin/sudo -E -u rabbitmq ${cmd}";
-
-in
-
-{
-
-
+in {
   ###### interface
-
   options = {
-
     services.rabbitmq = {
 
       enable = mkOption {
@@ -40,55 +31,59 @@ in
         '';
       };
 
-    };
 
+      dataDir = mkOption {
+        type = types.path;
+        default = "/var/lib/rabbitmq";
+        description = ''
+          Data directory for rabbitmq.
+        '';
+      };
+
+    };
   };
 
 
   ###### implementation
-
   config = mkIf cfg.enable {
 
     environment.systemPackages = [ pkgs.rabbitmq_server ];
 
     users.extraUsers.rabbitmq = {
       description = "RabbitMQ server user";
-      home = "/var/empty";
+      home = "${cfg.dataDir}";
       group = "rabbitmq";
       uid = config.ids.uids.rabbitmq;
     };
 
     users.extraGroups.rabbitmq.gid = config.ids.gids.rabbitmq;
 
-    jobs.rabbitmq = {
-        description = "RabbitMQ server";
-
-        startOn = "started network-interfaces";
+    systemd.services.rabbitmq = {
+      description = "RabbitMQ Server";
 
-        preStart =
-          ''
-            mkdir -m 0700 -p /var/lib/rabbitmq
-            chown rabbitmq /var/lib/rabbitmq
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
 
-            mkdir -m 0700 -p /var/log/rabbitmq
-            chown rabbitmq /var/log/rabbitmq
-          '';
-
-        environment.HOME = "/var/lib/rabbitmq";
-        environment.RABBITMQ_NODE_IP_ADDRESS = cfg.listenAddress;
-        environment.SYS_PREFIX = "";
-
-        exec =
-          ''
-            ${run "${pkgs.rabbitmq_server}/sbin/rabbitmq-server"}
-          '';
+      environment = {
+        RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia";
+        RABBITMQ_NODE_IP_ADDRESS = cfg.listenAddress;
+        RABBITMQ_SERVER_START_ARGS = "-rabbit error_logger tty -rabbit sasl_error_logger false";
+        SYS_PREFIX = "";
+      };
 
-        preStop =
-          ''
-            ${run "${pkgs.rabbitmq_server}/sbin/rabbitmqctl stop"}
-          '';
+      serviceConfig = {
+        ExecStart = "${pkgs.rabbitmq_server}/sbin/rabbitmq-server";
+        User = "rabbitmq";
+        Group = "rabbitmq";
+        PermissionsStartOnly = true;
       };
 
+      preStart = ''
+        mkdir -p ${cfg.dataDir} && chmod 0700 ${cfg.dataDir}
+        if [ "$(id -u)" = 0 ]; then chown rabbitmq:rabbitmq ${cfg.dataDir}; fi
+      '';
+    };
+
   };
 
 }
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
index c9a2ef4589bd..9a70d9edfa72 100644
--- a/nixos/modules/services/audio/alsa.nix
+++ b/nixos/modules/services/audio/alsa.nix
@@ -1,7 +1,7 @@
 # ALSA sound support.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/audio/fuppes.nix b/nixos/modules/services/audio/fuppes.nix
index 3eb0732bae2e..4a975ed5f538 100644
--- a/nixos/modules/services/audio/fuppes.nix
+++ b/nixos/modules/services/audio/fuppes.nix
@@ -1,10 +1,10 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
 let
   cfg = config.services.fuppesd;
 in
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index a9880dee20c4..1b50f06bf30d 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix
index d5bc932c6b96..5ce215c5c4b5 100644
--- a/nixos/modules/services/backup/almir.nix
+++ b/nixos/modules/services/backup/almir.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.almir;
@@ -154,7 +154,8 @@ in {
       description = "Almir web app";
       wantedBy = [ "multi-user.target" ];
       path = [ pkgs.pythonPackages.almir ];
-      serviceConfig.ExecStart = "${pkgs.pythonPackages.almir}/bin/pserve ${productionini}";
+      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 ];
diff --git a/nixos/modules/services/backup/bacula.nix b/nixos/modules/services/backup/bacula.nix
index 272903c99e33..c2255f688181 100644
--- a/nixos/modules/services/backup/bacula.nix
+++ b/nixos/modules/services/backup/bacula.nix
@@ -1,9 +1,9 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
 # TODO: test configuration when building nixexpr (use -t parameter)
 # TODO: support sqlite3 (it's deprecate?) and mysql
 
-with pkgs.lib;
+with lib;
 
 let
   libDir = "/var/lib/bacula";
diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix
index 3ff9978fbb96..28f607861f77 100644
--- a/nixos/modules/services/backup/mysql-backup.nix
+++ b/nixos/modules/services/backup/mysql-backup.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
index e68ad794a96b..1f7b123eca37 100644
--- a/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   inherit (pkgs) postgresql gzip;
diff --git a/nixos/modules/services/backup/rsnapshot.nix b/nixos/modules/services/backup/rsnapshot.nix
index 178ba3ec7207..48ad7582b7e6 100644
--- a/nixos/modules/services/backup/rsnapshot.nix
+++ b/nixos/modules/services/backup/rsnapshot.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.services.rsnapshot;
 in
diff --git a/nixos/modules/services/backup/sitecopy-backup.nix b/nixos/modules/services/backup/sitecopy-backup.nix
index 5c7f7ffae5b3..5d3675fa3e9d 100644
--- a/nixos/modules/services/backup/sitecopy-backup.nix
+++ b/nixos/modules/services/backup/sitecopy-backup.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   inherit (pkgs) sitecopy;
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
new file mode 100644
index 000000000000..1966242e3dcb
--- /dev/null
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -0,0 +1,204 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.tarsnap;
+
+  optionalNullStr = e: v: if e == null then "" else v;
+
+  configFile = pkgs.writeText "tarsnap.conf" ''
+    cachedir ${cfg.cachedir}
+    keyfile  ${cfg.keyfile}
+    ${optionalString cfg.nodump "nodump"}
+    ${optionalString cfg.printStats "print-stats"}
+    ${optionalNullStr cfg.checkpointBytes "checkpoint-bytes "+cfg.checkpointBytes}
+    ${optionalString cfg.aggressiveNetworking "aggressive-networking"}
+    ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
+    ${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)}
+    ${optionalString cfg.lowmem "lowmem"}
+    ${optionalString cfg.verylowmem "verylowmem"}
+  '';
+in
+{
+  options = {
+    services.tarsnap = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, NixOS will periodically create backups of the
+          specified directories using the <literal>tarsnap</literal>
+          backup service. This installs a <literal>systemd</literal>
+          service called <literal>tarsnap-backup</literal> which is
+          periodically run by cron, or you may run it on-demand.
+
+          See the Tarsnap <link
+          xlink:href='http://www.tarsnap.com/gettingstarted.html'>Getting
+          Started</link> page.
+        '';
+      };
+
+      label = mkOption {
+        type = types.str;
+        default = "nixos";
+        description = ''
+          Specifies the label for archives created by Tarsnap. The
+          full name will be
+          <literal>label-$(date+"%Y%m%d%H%M%S")</literal>. For
+          example, by default your backups will look similar to
+          <literal>nixos-20140301011501</literal>.
+        '';
+      };
+
+      cachedir = mkOption {
+        type    = types.path;
+        default = "/var/cache/tarsnap";
+        description = ''
+          Tarsnap operations use a "cache directory" which allows
+          Tarsnap to identify which blocks of data have been
+          previously stored; this directory is specified via the
+          <literal>cachedir</literal> option. If the cache directory
+          is lost or out of date, tarsnap creation/deletion operations
+          will exit with an error message instructing you to run
+          <literal>tarsnap --fsck</literal> to regenerate the cache
+          directory.
+        '';
+      };
+
+      keyfile = mkOption {
+        type = types.path;
+        default = "/root/tarsnap.key";
+        description = ''
+          Path to the keyfile which identifies the machine associated
+          with your Tarsnap account. This file can be created using
+          the <literal>tarsnap-keygen</literal> utility, and providing
+          your Tarsnap login credentials.
+        '';
+      };
+
+      nodump = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          If set to <literal>true</literal>, then don't archive files
+          which have the <literal>nodump</literal> flag set.
+        '';
+      };
+
+      printStats = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Print statistics when creating archives.";
+      };
+
+      checkpointBytes = mkOption {
+        type = types.nullOr types.str;
+        default = "1G";
+        description = ''
+          Create a checkpoint per a particular amount of uploaded
+          data. By default, Tarsnap will create checkpoints once per
+          GB of data uploaded. At minimum,
+          <literal>checkpointBytes</literal> must be 1GB.
+
+          Can also be set to <literal>null</literal> to disable
+          checkpointing.
+        '';
+      };
+
+      period = mkOption {
+        type = types.str;
+        default = "15 01 * * *";
+        description = ''
+          This option defines (in the format used by cron) when
+          tarsnap is run for backups. The default is to backup the
+          specified paths at 01:15 at night every day.
+        '';
+      };
+
+      aggressiveNetworking = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Aggressive network behaviour: Use multiple TCP connections
+          when writing archives.  Use of this option is recommended
+          only in cases where TCP congestion control is known to be
+          the limiting factor in upload performance.
+        '';
+      };
+
+      directories = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = "List of filesystem paths to archive.";
+      };
+
+      excludes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Exclude files and directories matching the specified patterns.
+        '';
+      };
+
+      includes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Include only files and directories matching the specified patterns.
+
+          Note that exclusions specified via
+          <literal>excludes</literal> take precedence over inclusions.
+        '';
+      };
+
+      lowmem = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Attempt to reduce tarsnap memory consumption.  This option
+          will slow down the process of creating archives, but may
+          help on systems where the average size of files being backed
+          up is less than 1 MB.
+        '';
+      };
+
+      verylowmem = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Try even harder to reduce tarsnap memory consumption.  This
+          can significantly slow down tarsnap, but reduces its memory
+          usage by an additional factor of 2 beyond what the
+          <literal>lowmem</literal> option does.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.directories != [];
+          message = "Must specify directories for Tarsnap to back up";
+        }
+        { assertion = cfg.lowmem -> !cfg.verylowmem && (cfg.verylowmem -> !cfg.lowmem);
+          message = "You cannot set both lowmem and verylowmem";
+        }
+      ];
+
+    systemd.services.tarsnap-backup = {
+      description = "Tarsnap Backup process";
+      path = [ pkgs.tarsnap pkgs.coreutils ];
+      script = ''
+        mkdir -p -m 0755 $(dirname ${cfg.cachedir})
+        mkdir -p -m 0600 ${cfg.cachedir}
+        exec tarsnap --configfile ${configFile} -c -f ${cfg.label}-$(date +"%Y%m%d%H%M%S") ${concatStringsSep " " cfg.directories}
+      '';
+    };
+
+    services.cron.systemCronJobs = optional cfg.enable
+      "${cfg.period} root ${config.systemd.package}/bin/systemctl start tarsnap-backup.service";
+
+    environment.systemPackages = [ pkgs.tarsnap ];
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
new file mode 100644
index 000000000000..b01b5c3245a4
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -0,0 +1,119 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkins = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the jenkins continuous integration server.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins server should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          If the default user "jenkins" is configured then this is the primary
+          group of that user.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+
+      port = mkOption {
+        default = 8080;
+        type = types.uniq types.int;
+        description = ''
+          Specifies port number on which the jenkins HTTP interface listens. The default is 8080.
+        '';
+      };
+
+      packages = mkOption {
+        default = [ pkgs.stdenv pkgs.git pkgs.jdk pkgs.openssh pkgs.nix ];
+        type = types.listOf types.package;
+        description = ''
+          Packages to add to PATH for the jenkins process.
+        '';
+      };
+
+      environment = mkOption {
+        default = { NIX_REMOTE = "daemon"; };
+        type = with types; attrsOf string;
+        description = ''
+          Additional environment variables to be passed to the jenkins process.
+          The environment will always include JENKINS_HOME.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+
+    systemd.services.jenkins = {
+      description = "Jenkins Continuous Integration Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        JENKINS_HOME = cfg.home;
+      } // cfg.environment;
+
+      path = cfg.packages;
+
+      script = ''
+        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpPort=${toString cfg.port}
+      '';
+
+      postStart = ''
+        until ${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port} ; do
+          sleep 10
+        done
+        while true ; do
+          index=`${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port}`
+          if [[ !("$index" =~ 'Please wait while Jenkins is restarting' ||
+                  "$index" =~ 'Please wait while Jenkins is getting ready to work') ]]; then
+            exit 0
+          fi
+          sleep 30
+        done
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/slave.nix b/nixos/modules/services/continuous-integration/jenkins/slave.nix
new file mode 100644
index 000000000000..5836d92a4fc0
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/slave.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.jenkinsSlave;
+  masterCfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkinsSlave = {
+      # todo:
+      # * assure the profile of the jenkins user has a JRE and any specified packages. This would
+      # enable ssh slaves.
+      # * Optionally configure the node as a jenkins ad-hoc slave. This would imply configuration
+      # properties for the master node.
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If true the system will be configured to work as a jenkins slave.
+          If the system is also configured to work as a jenkins master then this has no effect.
+          In progress: Currently only assures the jenkins user is configured.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins slave agent should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          If the default slave agent user "jenkins" is configured then this is
+          the primary group of that user.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.enable && !masterCfg.enable) {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
index 7872ea2dc6a3..68913f15f953 100644
--- a/nixos/modules/services/databases/4store-endpoint.nix
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -1,10 +1,10 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 let
   cfg = config.services.fourStoreEndpoint;
   endpointUser = "fourstorehttp";
   run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
 in
-with pkgs.lib;
+with lib;
 {
 
   ###### interface
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
index 412d14b050cb..1725672a6594 100644
--- a/nixos/modules/services/databases/4store.nix
+++ b/nixos/modules/services/databases/4store.nix
@@ -1,11 +1,11 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 let
   cfg = config.services.fourStore;
   stateDir = "/var/lib/4store";
   fourStoreUser = "fourstore";
   run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}";
 in
-with pkgs.lib;
+with lib;
 {
 
   ###### interface
diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix
new file mode 100644
index 000000000000..5088c7416810
--- /dev/null
+++ b/nixos/modules/services/databases/couchdb.nix
@@ -0,0 +1,174 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.couchdb;
+  configFile = pkgs.writeText "couchdb.ini"
+    ''
+      [couchdb]
+      database_dir = ${cfg.databaseDir}
+      uri_file = ${cfg.uriFile}
+      view_index_dir = ${cfg.viewIndexDir}
+
+      [httpd]
+      port = ${toString cfg.port}
+      bind_address = ${cfg.bindAddress}
+
+      [log]
+      file = ${cfg.logFile}
+    '';
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.couchdb = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run CouchDB Server.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.couchdb;
+        example = literalExample "pkgs.couchdb";
+        description = ''
+          CouchDB package to use.
+        '';
+      };
+
+
+      user = mkOption {
+        type = types.string;
+        default = "couchdb";
+        description = ''
+          User account under which couchdb runs.
+        '';
+      };
+
+      group = mkOption {
+        type = types.string;
+        default = "couchdb";
+        description = ''
+          Group account under which couchdb runs.
+        '';
+      };
+
+      # couchdb options: http://docs.couchdb.org/en/latest/config/index.html
+
+      databaseDir = mkOption {
+        type = types.path;
+        default = "/var/lib/couchdb";
+        description = ''
+          Specifies location of CouchDB database files (*.couch named). This
+          location should be writable and readable for the user the CouchDB
+          service runs as (couchdb by default).
+        '';
+      };
+
+      uriFile = mkOption {
+        type = types.path;
+        default = "/var/run/couchdb/couchdb.uri";
+        description = ''
+          This file contains the full URI that can be used to access this
+          instance of CouchDB. It is used to help discover the port CouchDB is
+          running on (if it was set to 0 (e.g. automatically assigned any free
+          one). This file should be writable and readable for the user that
+          runs the CouchDB service (couchdb by default).
+        '';
+      };
+
+      viewIndexDir = mkOption {
+        type = types.path;
+        default = "/var/lib/couchdb";
+        description = ''
+          Specifies location of CouchDB view index files. This location should
+          be writable and readable for the user that runs the CouchDB service
+          (couchdb by default).
+        '';
+      };
+
+      bindAddress = mkOption {
+        type = types.string;
+        default = "127.0.0.1";
+        description = ''
+          Defines the IP address by which CouchDB will be accessible.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 5984;
+        description = ''
+          Defined the port number to listen.
+        '';
+      };
+
+      logFile = mkOption {
+        type = types.path;
+        default = "/var/log/couchdb.log";
+        description = ''
+          Specifies the location of file for logging output.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration. Overrides any other cofiguration.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.couchdb.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.couchdb = {
+      description = "CouchDB Server";
+      wantedBy = [ "multi-user.target" ];
+
+      preStart =
+        ''
+        mkdir -p `dirname ${cfg.uriFile}`;
+        mkdir -p `dirname ${cfg.logFile}`;
+        mkdir -p ${cfg.databaseDir};
+        mkdir -p ${cfg.viewIndexDir};
+
+        if [ "$(id -u)" = 0 ]; then
+          chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`
+          chown ${cfg.user}:${cfg.group} ${cfg.databaseDir}
+          chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir}
+        fi
+        '';
+
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${cfg.package}/bin/couchdb -a ${configFile} -a ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig}";
+      };
+    };
+
+    users.extraUsers.couchdb = {
+      description = "CouchDB Server user";
+      group = "couchdb";
+      uid = config.ids.uids.couchdb;
+    };
+
+    users.extraGroups.couchdb.gid = config.ids.gids.couchdb;
+
+  };
+}
diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix
index 75c225bdb67b..83dd4951170a 100644
--- a/nixos/modules/services/databases/firebird.nix
+++ b/nixos/modules/services/databases/firebird.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
 # TODO: This may file may need additional review, eg which configuartions to
 # expose to the user.
@@ -19,7 +19,7 @@
 # Eg superserver is said to be most efficiently using resources according to
 # http://www.firebirdsql.org/manual/qsg25-classic-or-super.html
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -49,6 +49,7 @@ in
 
       package = mkOption {
         default = pkgs.firebirdSuper;
+        type = types.package;
         /*
           Example: <code>package = pkgs.firebirdSuper.override { icu =
             pkgs.icu; };</code> which is not recommended for compatibility
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
index 505253229c9c..c6875af506d3 100644
--- a/nixos/modules/services/databases/memcached.nix
+++ b/nixos/modules/services/databases/memcached.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/databases/monetdb.nix b/nixos/modules/services/databases/monetdb.nix
new file mode 100644
index 000000000000..9f09c71e005a
--- /dev/null
+++ b/nixos/modules/services/databases/monetdb.nix
@@ -0,0 +1,88 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.monetdb;
+  monetdbUser = "monetdb";
+in
+with lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.monetdb = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable MonetDB database server.";
+      };
+
+      package = mkOption {
+        type = types.path;
+        description = "MonetDB package to use.";
+      };
+
+      dbfarmDir = mkOption {
+        type = types.path;
+        default = "/var/lib/monetdb";
+        description = ''
+          Specifies location of Monetdb dbfarm (keeps database and auxiliary files).
+        '';
+      };
+
+      port = mkOption {
+        default = "50000";
+        example = "50000";
+        description = "Port to listen on.";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers.monetdb = 
+      { name = monetdbUser;
+        uid = config.ids.uids.monetdb;
+        description = "monetdb user";
+        home = cfg.dbfarmDir;
+      };
+
+    users.extraGroups.monetdb.gid = config.ids.gids.monetdb;
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.monetdb =
+      { description = "MonetDB Server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        after = [ "network.target" ];
+
+        path = [ cfg.package ];
+
+        preStart =
+          ''
+            # Initialise the database.
+            if ! test -e ${cfg.dbfarmDir}/.merovingian_properties; then
+                mkdir -m 0700 -p ${cfg.dbfarmDir}
+                chown -R ${monetdbUser} ${cfg.dbfarmDir}
+                ${cfg.package}/bin/monetdbd create ${cfg.dbfarmDir}
+                ${cfg.package}/bin/monetdbd set port=${cfg.port} ${cfg.dbfarmDir}
+            fi
+          '';
+
+        serviceConfig.ExecStart = "${cfg.package}/bin/monetdbd start -n ${cfg.dbfarmDir}";
+
+        serviceConfig.ExecStop = "${cfg.package}/bin/monetdbd stop ${cfg.dbfarmDir}";
+
+        unitConfig.RequiresMountsFor = "${cfg.dbfarmDir}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
index 213a60687b23..fe82ca430e13 100644
--- a/nixos/modules/services/databases/mongodb.nix
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -39,6 +39,7 @@ in
 
       package = mkOption {
         default = pkgs.mongodb;
+        type = types.package;
         description = "
           Which MongoDB derivation to use.
         ";
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 8be05a27cdcb..1ca45d90f891 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -8,10 +8,14 @@ let
 
   mysql = cfg.package;
 
+  is55 = mysql.mysqlVersion == "5.5";
+
+  mysqldDir = if is55 then "${mysql}/bin" else "${mysql}/libexec";
+
   pidFile = "${cfg.pidDir}/mysqld.pid";
 
   mysqldOptions =
-    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
+    "--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${mysql} " +
     "--pid-file=${pidFile}";
 
   myCnf = pkgs.writeText "my.cnf"
@@ -19,7 +23,7 @@ let
     [mysqld]
     ${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")
+    ${optionalString (cfg.replication.role == "slave" && !is55)
     ''
       master-host = ${cfg.replication.masterHost}
       master-user = ${cfg.replication.masterUser}
@@ -47,7 +51,8 @@ in
       };
 
       package = mkOption {
-        default = pkgs.mysql;
+        type = types.package;
+        example = literalExample "pkgs.mysql";
         description = "
           Which MySQL derivation to use.
         ";
@@ -176,7 +181,7 @@ in
             chown -R ${cfg.user} ${cfg.pidDir}
           '';
 
-        serviceConfig.ExecStart = "${mysql}/libexec/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
+        serviceConfig.ExecStart = "${mysqldDir}/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
 
         postStart =
           ''
@@ -216,6 +221,16 @@ in
                     fi
                   '') cfg.initialDatabases}
 
+                ${optionalString (cfg.replication.role == "slave" && is55)
+                  ''
+                    # Set up the replication master
+
+                    ( echo "stop slave;"
+                      echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
+                      echo "start slave;"
+                    ) | ${mysql}/bin/mysql -u root -N
+                  ''}
+
                 ${optionalString (cfg.initialScript != null)
                   ''
                     # Execute initial script
@@ -235,9 +250,6 @@ in
               rm /tmp/mysql_init
             fi
           ''; # */
-
-        serviceConfig.ExecStop =
-          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
       };
 
   };
diff --git a/nixos/modules/services/databases/mysql55.nix b/nixos/modules/services/databases/mysql55.nix
deleted file mode 100644
index fe8b29e3c6b7..000000000000
--- a/nixos/modules/services/databases/mysql55.nix
+++ /dev/null
@@ -1,248 +0,0 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
-
-let
-
-  cfg = config.services.mysql55;
-
-  mysql = cfg.package;
-
-  pidFile = "${cfg.pidDir}/mysqld.pid";
-
-  mysqldOptions =
-    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
-    "--pid-file=${pidFile}";
-
-  myCnf = pkgs.writeText "my.cnf"
-  ''
-    [mysqld]
-    ${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}"}
-    ${cfg.extraOptions}
-  '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.mysql55 = {
-
-      enable = mkOption {
-        default = false;
-        description = "
-          Whether to enable the MySQL server.
-        ";
-      };
-
-      package = mkOption {
-        default = pkgs.mysql55;
-        description = "
-          Which MySQL derivation to use.
-        ";
-      };
-
-      port = mkOption {
-        default = "3306";
-        description = "Port of MySQL";
-      };
-
-      user = mkOption {
-        default = "mysql";
-        description = "User account under which MySQL runs";
-      };
-
-      dataDir = mkOption {
-        default = "/var/mysql"; # !!! should be /var/db/mysql
-        description = "Location where MySQL stores its table files";
-      };
-
-      pidDir = mkOption {
-        default = "/var/run/mysql";
-        description = "Location of the file which stores the PID of the MySQL server";
-      };
-
-      extraOptions = mkOption {
-        default = "";
-        example = ''
-          key_buffer_size = 6G
-          table_cache = 1600
-          log-error = /var/log/mysql_err.log
-        '';
-        description = ''
-          Provide extra options to the MySQL configuration file.
-
-          Please note, that these options are added to the
-          <literal>[mysqld]</literal> section so you don't need to explicitly
-          state it again.
-        '';
-      };
-
-      initialDatabases = mkOption {
-        default = [];
-        description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL";
-        example = [
-          { name = "foodatabase"; schema = literalExample "./foodatabase.sql"; }
-          { name = "bardatabase"; schema = literalExample "./bardatabase.sql"; }
-        ];
-      };
-
-      initialScript = mkOption {
-        default = null;
-        description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database";
-      };
-
-      # FIXME: remove this option; it's a really bad idea.
-      rootPassword = mkOption {
-        default = null;
-        description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty.";
-      };
-
-      replication = {
-        role = mkOption {
-          default = "none";
-          description = "Role of the MySQL server instance. Can be either: master, slave or none";
-        };
-
-        serverId = mkOption {
-          default = 1;
-          description = "Id of the MySQL server instance. This number must be unique for each instance";
-        };
-
-        masterHost = mkOption {
-          description = "Hostname of the MySQL master server";
-        };
-
-        masterUser = mkOption {
-          description = "Username of the MySQL replication user";
-        };
-
-        masterPassword = mkOption {
-          description = "Password of the MySQL replication user";
-        };
-
-        masterPort = mkOption {
-          default = 3306;
-          description = "Port number on which the MySQL master server runs";
-        };
-      };
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf config.services.mysql55.enable {
-
-    users.extraUsers.mysql = {
-      description = "MySQL server user";
-      group = "mysql";
-      uid = config.ids.uids.mysql;
-    };
-
-    users.extraGroups.mysql.gid = config.ids.gids.mysql;
-
-    environment.systemPackages = [mysql];
-
-    systemd.services.mysql =
-      { description = "MySQL Server";
-
-        wantedBy = [ "multi-user.target" ];
-
-        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
-
-        preStart =
-          ''
-            if ! test -e ${cfg.dataDir}/mysql; then
-                mkdir -m 0700 -p ${cfg.dataDir}
-                chown -R ${cfg.user} ${cfg.dataDir}
-                ${mysql}/bin/mysql_install_db ${mysqldOptions}
-                touch /tmp/mysql_init
-            fi
-
-            mkdir -m 0700 -p ${cfg.pidDir}
-            chown -R ${cfg.user} ${cfg.pidDir}
-          '';
-
-        serviceConfig.ExecStart = "${mysql}/bin/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
-
-        postStart =
-          ''
-            # Wait until the MySQL server is available for use
-            count=0
-            while [ ! -e /tmp/mysql.sock ]
-            do
-                if [ $count -eq 30 ]
-                then
-                    echo "Tried 30 times, giving up..."
-                    exit 1
-                fi
-
-                echo "MySQL daemon not yet started. Waiting for 1 second..."
-                count=$((count++))
-                sleep 1
-            done
-
-            if [ -f /tmp/mysql_init ]
-            then
-                ${concatMapStrings (database:
-                  ''
-                    # Create initial databases
-                    if ! test -e "${cfg.dataDir}/${database.name}"; then
-                        echo "Creating initial database: ${database.name}"
-                        ( echo "create database ${database.name};"
-                          echo "use ${database.name};"
-
-                          if [ -f "${database.schema}" ]
-                          then
-                              cat ${database.schema}
-                          elif [ -d "${database.schema}" ]
-                          then
-                              cat ${database.schema}/mysql-databases/*.sql
-                          fi
-                        ) | ${mysql}/bin/mysql -u root -N
-                    fi
-                  '') cfg.initialDatabases}
-                
-                ${optionalString (cfg.replication.role == "slave")
-                  ''
-                    # Set up the replication master
-                    
-                    ( echo "stop slave;"
-                      echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
-                      echo "start slave;"
-                    ) | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-                ${optionalString (cfg.initialScript != null)
-                  ''
-                    # Execute initial script
-                    cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-                ${optionalString (cfg.rootPassword != null)
-                  ''
-                    # Change root password
-
-                    ( echo "use mysql;"
-                      echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';"
-                      echo "flush privileges;"
-                    ) | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-              rm /tmp/mysql_init
-            fi
-          ''; # */
-
-        serviceConfig.ExecStop =
-          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index 0fc8b88c6526..c95238b34515 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 265d26e8ce98..ad83cb553e1d 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -55,7 +55,7 @@ in
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         example = literalExample "pkgs.postgresql92";
         description = ''
           PostgreSQL package to use.
@@ -215,7 +215,7 @@ in
             # Shut down Postgres using SIGINT ("Fast Shutdown mode").  See
             # http://www.postgresql.org/docs/current/static/server-shutdown.html
             KillSignal = "SIGINT";
-            KillMode = "process"; # FIXME: this may cause processes to be left behind in the cgroup even after the final SIGKILL
+            KillMode = "mixed";
 
             # Give Postgres a decent amount of time to clean up after
             # receiving systemd's SIGINT.
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index ea6399ba4f44..4ef48df9831c 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.redis;
@@ -45,6 +45,7 @@ in
       package = mkOption {
         default = pkgs.redis;
         description = "Which Redis derivation to use.";
+        type = types.package;
       };
 
       user = mkOption {
diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix
index 6a29fc132114..f955cb74b6ba 100644
--- a/nixos/modules/services/databases/virtuoso.nix
+++ b/nixos/modules/services/databases/virtuoso.nix
@@ -1,10 +1,10 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 let
   cfg = config.services.virtuoso;
   virtuosoUser = "virtuoso";
   stateDir = "/var/lib/virtuoso";
 in
-with pkgs.lib;
+with lib;
 {
 
   ###### interface
diff --git a/nixos/modules/services/desktops/accountservice.nix b/nixos/modules/services/desktops/accountservice.nix
new file mode 100644
index 000000000000..386dfe98bd23
--- /dev/null
+++ b/nixos/modules/services/desktops/accountservice.nix
@@ -0,0 +1,40 @@
+# AccountsService daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.accounts-daemon = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable AccountsService, a DBus service for accessing
+          the list of user accounts and information attached to those accounts.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.accounts-daemon.enable {
+
+    environment.systemPackages = [ pkgs.accountservice ];
+
+    services.dbus.packages = [ pkgs.accountservice ];
+
+    systemd.packages = [ pkgs.accountservice ];
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
new file mode 100644
index 000000000000..22a54f511d1c
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix
@@ -0,0 +1,39 @@
+# at-spi2-core daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.at-spi2-core = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable at-spi2-core, a service for the Assistive Technologies
+          available on the GNOME platform.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.at-spi2-core.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.at_spi2_core ];
+
+    services.dbus.packages = [ pkgs.gnome3.at_spi2_core ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/evolution-data-server.nix b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
new file mode 100644
index 000000000000..a49b5b477689
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/evolution-data-server.nix
@@ -0,0 +1,39 @@
+# Evolution Data Server daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.evolution-data-server = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Evolution Data Server, a collection of services for 
+          storing addressbooks and calendars.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.evolution-data-server.enable {
+
+    environment.systemPackages = [ pkgs.evolution_data_server ];
+
+    services.dbus.packages = [ pkgs.evolution_data_server ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/gnome-keyring.nix b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
new file mode 100644
index 000000000000..447fd783f148
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/gnome-keyring.nix
@@ -0,0 +1,40 @@
+# GNOME Keyring daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.gnome-keyring = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME Keyring daemon, a service designed to
+          take care of the user's security credentials,
+          such as user names and passwordsa search engine.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.gnome-keyring.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.gnome_keyring ];
+
+    services.dbus.packages = [ pkgs.gnome3.gnome_keyring ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
new file mode 100644
index 000000000000..365e19c15bb1
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/gnome-online-accounts.nix
@@ -0,0 +1,39 @@
+# GNOME Online Accounts daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.gnome-online-accounts = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME Online Accounts daemon, a service that provides
+          a single sign-on framework for the GNOME desktop.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.gnome-online-accounts.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.gnome_online_accounts ];
+
+    services.dbus.packages = [ pkgs.gnome3.gnome_online_accounts ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/gnome-user-share.nix b/nixos/modules/services/desktops/gnome3/gnome-user-share.nix
new file mode 100644
index 000000000000..df796ed77ff4
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/gnome-user-share.nix
@@ -0,0 +1,42 @@
+# GNOME User Share daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.gnome-user-share = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME User Share, a service that exports the
+          contents of the Public folder in your home directory on the local network.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.gnome-user-share.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.gnome-user-share ];
+
+    services.xserver.displayManager.sessionCommands = with pkgs.gnome3; ''
+      # Don't let gnome-control-center depend upon gnome-user-share
+      export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${gnome-user-share}/share/gsettings-schemas/${gnome-user-share.name}
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/sushi.nix b/nixos/modules/services/desktops/gnome3/sushi.nix
new file mode 100644
index 000000000000..7a4389038b22
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/sushi.nix
@@ -0,0 +1,38 @@
+# GNOME Sushi daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.sushi = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Sushi, a quick previewer for nautilus.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.sushi.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.sushi ];
+
+    services.dbus.packages = [ pkgs.gnome3.sushi ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome3/tracker.nix b/nixos/modules/services/desktops/gnome3/tracker.nix
new file mode 100644
index 000000000000..94a22d0c8819
--- /dev/null
+++ b/nixos/modules/services/desktops/gnome3/tracker.nix
@@ -0,0 +1,39 @@
+# Tracker daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnome3.tracker = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Tracker services, a search engine,
+          search tool and metadata storage system.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnome3.tracker.enable {
+
+    environment.systemPackages = [ pkgs.gnome3.tracker ];
+
+    services.dbus.packages = [ pkgs.gnome3.tracker ];
+
+  };
+
+}
diff --git a/nixos/modules/services/desktops/telepathy.nix b/nixos/modules/services/desktops/telepathy.nix
new file mode 100644
index 000000000000..2554f3a1666f
--- /dev/null
+++ b/nixos/modules/services/desktops/telepathy.nix
@@ -0,0 +1,39 @@
+# Telepathy daemon.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.telepathy = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Telepathy service, a communications framework
+          that enables real-time communication via pluggable protocol backends.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.telepathy.enable {
+
+    environment.systemPackages = [ pkgs.telepathy_mission_control ];
+
+    services.dbus.packages = [ pkgs.telepathy_mission_control ];
+
+  };
+
+}
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
index 92c9112eeb6c..7a3ecebec39c 100644
--- a/nixos/modules/services/games/ghost-one.nix
+++ b/nixos/modules/services/games/ghost-one.nix
@@ -1,5 +1,5 @@
-{pkgs, config, ...}:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
 
   cfg = config.services.ghostOne;
diff --git a/nixos/modules/services/games/minecraft-server.nix b/nixos/modules/services/games/minecraft-server.nix
new file mode 100644
index 000000000000..4c734aefa469
--- /dev/null
+++ b/nixos/modules/services/games/minecraft-server.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.minecraft-server;
+in
+{
+  options = {
+    services.minecraft-server = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, start a Minecraft Server. The listening port for
+          the server is always <literal>25565</literal>. The server
+          data will be loaded from and saved to
+          <literal>/var/lib/minecraft</literal>.
+        '';
+      };
+
+      jvmOpts = mkOption {
+        type = types.str;
+        default = "-Xmx2048M -Xms2048M";
+        description = "JVM options for the Minecraft Service.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.minecraft = {
+      description     = "Minecraft Server Service user";
+      home            = "/var/lib/minecraft";
+      createHome      = true;
+      uid             = config.ids.uids.minecraft;
+    };
+
+    systemd.services.minecraft-server = {
+      description   = "Minecraft Server Service";
+      wantedBy      = [ "multi-user.target" ];
+      after         = [ "network.target" ];
+
+      serviceConfig.Restart = "always";
+      serviceConfig.User    = "minecraft";
+      script = ''
+        cd /var/lib/minecraft
+        exec ${pkgs.minecraft-server}/bin/minecraft-server ${cfg.jvmOpts}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/80-net-name-slot.rules b/nixos/modules/services/hardware/80-net-name-slot.rules
new file mode 100644
index 000000000000..18547f170a3f
--- /dev/null
+++ b/nixos/modules/services/hardware/80-net-name-slot.rules
@@ -0,0 +1,13 @@
+# Copied from systemd 203.
+ACTION=="remove", GOTO="net_name_slot_end"
+SUBSYSTEM!="net", GOTO="net_name_slot_end"
+NAME!="", GOTO="net_name_slot_end"
+
+IMPORT{cmdline}="net.ifnames"
+ENV{net.ifnames}=="0", GOTO="net_name_slot_end"
+
+NAME=="", ENV{ID_NET_NAME_ONBOARD}!="", NAME="$env{ID_NET_NAME_ONBOARD}"
+NAME=="", ENV{ID_NET_NAME_SLOT}!="", NAME="$env{ID_NET_NAME_SLOT}"
+NAME=="", ENV{ID_NET_NAME_PATH}!="", NAME="$env{ID_NET_NAME_PATH}"
+
+LABEL="net_name_slot_end"
diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix
index 2329f38dfc25..a710636c1400 100644
--- a/nixos/modules/services/hardware/acpid.nix
+++ b/nixos/modules/services/hardware/acpid.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index a70a66c2ba0b..68d0406e63bd 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix
index 9f389efc06d4..6e30dfb752d2 100644
--- a/nixos/modules/services/hardware/pcscd.nix
+++ b/nixos/modules/services/hardware/pcscd.nix
@@ -1,6 +1,10 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+let
+  cfgFile = pkgs.writeText "reader.conf" "";
+in
+
+with lib;
 
 {
 
@@ -24,22 +28,26 @@ with pkgs.lib;
 
   config = mkIf config.services.pcscd.enable {
 
-    jobs.pcscd =
-      { description = "PCSC-Lite daemon";
-
-        startOn = "started udev";
-
-        daemonType = "fork";
-
-        # Add to the drivers directory the only drivers we have by now: ccid
-        preStart = ''
-            mkdir -p /var/lib/pcsc
-            rm -Rf /var/lib/pcsc/drivers
-            ln -s ${pkgs.ccid}/pcsc/drivers /var/lib/pcsc/
-        '';
+    systemd.sockets.pcscd = {
+      description = "PCSC-Lite Socket";
+      wantedBy = [ "sockets.target" ];
+      before = [ "multi-user.target" ];
+      socketConfig.ListenStream = "/run/pcscd/pcscd.comm";
+    };
 
-        exec = "${pkgs.pcsclite}/sbin/pcscd";
+    systemd.services.pcscd = {
+      description = "PCSC-Lite daemon";
+      preStart = ''
+          mkdir -p /var/lib/pcsc
+          rm -Rf /var/lib/pcsc/drivers
+          ln -s ${pkgs.ccid}/pcsc/drivers /var/lib/pcsc/
+      '';
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.pcsclite}/sbin/pcscd --auto-exit -c ${cfgFile}";
+        ExecReload = "${pkgs.pcsclite}/sbin/pcscd --hotplug";
       };
+    };
 
   };
 
diff --git a/nixos/modules/services/hardware/pommed.nix b/nixos/modules/services/hardware/pommed.nix
index 32599554fc12..04db46999b0a 100644
--- a/nixos/modules/services/hardware/pommed.nix
+++ b/nixos/modules/services/hardware/pommed.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
index 5979feb82409..01d910575bb5 100644
--- a/nixos/modules/services/hardware/sane.nix
+++ b/nixos/modules/services/hardware/sane.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/hardware/thinkfan.nix b/nixos/modules/services/hardware/thinkfan.nix
index b39c9cb1d9bb..16c31aab2d50 100644
--- a/nixos/modules/services/hardware/thinkfan.nix
+++ b/nixos/modules/services/hardware/thinkfan.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 52b3ad435797..507752dabcf7 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -83,8 +83,8 @@ let
         grep -l '\(RUN+\|IMPORT{program}\)="\(/usr\)\?/s\?bin' $i/*/udev/rules.d/* || true
       done
 
-      ${optionalString (!config.networking.usePredictableInterfaceNames) ''
-        ln -s /dev/null $out/80-net-name-slot.rules
+      ${optionalString config.networking.usePredictableInterfaceNames ''
+        cp ${./80-net-name-slot.rules} $out/80-net-name-slot.rules
       ''}
 
       # If auto-configuration is disabled, then remove
@@ -243,5 +243,9 @@ in
         fi
       '';
 
+    systemd.services.systemd-udevd =
+      { environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
+      };
+
   };
 }
diff --git a/nixos/modules/services/hardware/udisks.nix b/nixos/modules/services/hardware/udisks.nix
index 531ee192573a..c9d11bcfc687 100644
--- a/nixos/modules/services/hardware/udisks.nix
+++ b/nixos/modules/services/hardware/udisks.nix
@@ -1,8 +1,8 @@
 # Udisks daemon.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/hardware/udisks2.nix b/nixos/modules/services/hardware/udisks2.nix
index 178ec379ff1b..f5b641c7378b 100644
--- a/nixos/modules/services/hardware/udisks2.nix
+++ b/nixos/modules/services/hardware/udisks2.nix
@@ -1,8 +1,8 @@
 # Udisks daemon.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
@@ -14,7 +14,7 @@ with pkgs.lib;
 
       enable = mkOption {
         type = types.bool;
-        default = false;
+        default = true;
         description = ''
           Whether to enable Udisks, a DBus service that allows
           applications to query and manipulate storage devices.
diff --git a/nixos/modules/services/hardware/upower.nix b/nixos/modules/services/hardware/upower.nix
index 4a9b13d4aa09..a202d82f646a 100644
--- a/nixos/modules/services/hardware/upower.nix
+++ b/nixos/modules/services/hardware/upower.nix
@@ -1,8 +1,8 @@
 # Upower daemon.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/logging/klogd.nix b/nixos/modules/services/logging/klogd.nix
index 36b530d0077e..f69e08152b55 100644
--- a/nixos/modules/services/logging/klogd.nix
+++ b/nixos/modules/services/logging/klogd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   ###### interface
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 2a6a6516f488..1cd032ffa76b 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.logcheck;
@@ -208,12 +208,13 @@ in
         mapAttrsToList writeIgnoreRule cfg.ignore
         ++ mapAttrsToList writeIgnoreCronRule cfg.ignoreCron;
 
-    users.extraUsers = singleton
-      { name = cfg.user;
+    users.extraUsers = optionalAttrs (cfg.user == "logcheck") (singleton
+      { name = "logcheck";
+        uid = config.ids.uids.logcheck;
         shell = "/bin/sh";
         description = "Logcheck user account";
         extraGroups = cfg.extraGroups;
-      };
+      });
 
     system.activationScripts.logcheck = ''
       mkdir -m 700 -p /var/{lib,lock}/logcheck
diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix
index c6c0d2ea2382..804f9a0847ff 100644
--- a/nixos/modules/services/logging/logrotate.nix
+++ b/nixos/modules/services/logging/logrotate.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.logrotate;
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index 21128779e8f6..480e35a1156d 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.logstash;
diff --git a/nixos/modules/services/logging/rsyslogd.nix b/nixos/modules/services/logging/rsyslogd.nix
index 680c7a912c1f..d4b7aa809f00 100644
--- a/nixos/modules/services/logging/rsyslogd.nix
+++ b/nixos/modules/services/logging/rsyslogd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/logging/syslogd.nix b/nixos/modules/services/logging/syslogd.nix
index 36a0ace927ae..325868079e22 100644
--- a/nixos/modules/services/logging/syslogd.nix
+++ b/nixos/modules/services/logging/syslogd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 92682d644f41..1fb7102e7f3e 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/mail/freepops.nix b/nixos/modules/services/mail/freepops.nix
index 79f211ad86e3..2dd27a2033a7 100644
--- a/nixos/modules/services/mail/freepops.nix
+++ b/nixos/modules/services/mail/freepops.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.mail.freepopsd;
diff --git a/nixos/modules/services/mail/mail.nix b/nixos/modules/services/mail/mail.nix
index bad0b22625dc..b7e1d295f2c5 100644
--- a/nixos/modules/services/mail/mail.nix
+++ b/nixos/modules/services/mail/mail.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index 2732fd602004..fbc4b1d7d8a8 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -1,7 +1,7 @@
-{ pkgs, config, ... }:
+{ config, lib, pkgs, ... }:
 
 with pkgs;
-with pkgs.lib;
+with lib;
 
 let
 
@@ -79,5 +79,10 @@ in {
       preStart = "mkdir -p /var/spool";
       serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
     };
+
+    environment.systemPackages = [ (pkgs.runCommand "opensmtpd-sendmail" {} ''
+      mkdir -p $out/bin
+      ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail
+    '') ];
   };
 }
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 2633289b46d5..8f75bd8ab5d0 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix
index aaf1dfcc210c..a3ac9e372422 100644
--- a/nixos/modules/services/mail/spamassassin.nix
+++ b/nixos/modules/services/mail/spamassassin.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix
index 50491c556e8a..e645bd25a66d 100644
--- a/nixos/modules/services/misc/autofs.nix
+++ b/nixos/modules/services/misc/autofs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/cgminer.nix b/nixos/modules/services/misc/cgminer.nix
index f715013b51f3..8f25df809cd6 100644
--- a/nixos/modules/services/misc/cgminer.nix
+++ b/nixos/modules/services/misc/cgminer.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.cgminer;
@@ -42,6 +42,7 @@ in
       package = mkOption {
         default = pkgs.cgminer;
         description = "Which cgminer derivation to use.";
+        type = types.package;
       };
 
       user = mkOption {
diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix
new file mode 100644
index 000000000000..552e0a435efe
--- /dev/null
+++ b/nixos/modules/services/misc/dictd.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dictd = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the DICT.org dictionary server.
+        '';
+      };
+
+      DBs = mkOption {
+        default = [];
+        # example = [ pkgs.dictDBs.nld2eng ];
+        description = ''List of databases to make available.'';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = let dictdb = pkgs.dictDBCollector { dictlist = map (x: {
+               name = x.name;
+               filename = x; } ) config.services.dictd.DBs; };
+  in mkIf config.services.dictd.enable {
+
+    # get the command line client on system path to make some use of the service
+    environment.systemPackages = [ pkgs.dict ];
+
+    users.extraUsers = singleton
+      { name = "dictd";
+        group = "dictd";
+        description = "DICT.org dictd server";
+        home = "${dictdb}/share/dictd";
+        uid = config.ids.uids.dictd;
+      };
+
+    users.extraGroups = singleton
+      { name = "dictd";
+        gid = config.ids.gids.dictd;
+      };
+
+    jobs.dictd =
+      { description = "DICT.org Dictionary Server";
+        startOn = "startup";
+        environment = { LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; };
+        daemonType = "fork";
+        exec = "${pkgs.dict}/sbin/dictd -s -c ${dictdb}/share/dictd/dictd.conf --locale en_US.UTF-8";
+      };
+  };
+
+}
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index 82526b154e7a..94d0caaa76b7 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -1,7 +1,7 @@
 # Disnix server
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/felix.nix b/nixos/modules/services/misc/felix.nix
index 2da50fc85957..a01c7f08b914 100644
--- a/nixos/modules/services/misc/felix.nix
+++ b/nixos/modules/services/misc/felix.nix
@@ -1,7 +1,7 @@
 # Felix server
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
index 9f4c4645279f..0093e3c0c33a 100644
--- a/nixos/modules/services/misc/folding-at-home.nix
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
   stateDir = "/var/lib/foldingathome";
   cfg = config.services.foldingAtHome;
diff --git a/nixos/modules/services/misc/gpsd.nix b/nixos/modules/services/misc/gpsd.nix
index bc1d1f4575a8..4a677f33fa0c 100644
--- a/nixos/modules/services/misc/gpsd.nix
+++ b/nixos/modules/services/misc/gpsd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -19,6 +19,7 @@ in
     services.gpsd = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = ''
           Whether to enable `gpsd', a GPS service daemon.
@@ -26,6 +27,7 @@ in
       };
 
       device = mkOption {
+        type = types.str;
         default = "/dev/ttyUSB0";
         description = ''
           A device may be a local serial device for GPS input, or a URL of the form:
@@ -35,6 +37,7 @@ in
       };
 
       readonly = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to enable the broken-device-safety, otherwise
@@ -51,6 +54,7 @@ in
       };
 
       port = mkOption {
+        type = types.uniq types.int;
         default = 2947;
         description = ''
           The port where to listen for TCP connections.
@@ -58,6 +62,7 @@ in
       };
 
       debugLevel = mkOption {
+        type = types.uniq types.int;
         default = 0;
         description = ''
           The debugging level.
@@ -85,19 +90,20 @@ in
         inherit gid;
       };
 
-    jobs.gpsd =
-      { description = "GPSD daemon";
-
-        startOn = "ip-up";
-
-        exec =
-          ''
-            ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}"  \
-              -S "${toString cfg.port}"                             \
-              ${if cfg.readonly then "-b" else ""}                  \
-              "${cfg.device}"
-          '';
+    systemd.services.gpsd = {
+      description = "GPSD daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = ''
+          ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}"  \
+            -S "${toString cfg.port}"                             \
+            ${if cfg.readonly then "-b" else ""}                  \
+            "${cfg.device}"
+        '';
       };
+    };
 
   };
 
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index cca42aa11009..4bfd6268234d 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -58,7 +58,7 @@ in
     nix = {
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pkgs.nix;
         description = ''
           This option specifies the Nix package instance to use throughout the system.
@@ -253,7 +253,7 @@ in
 
   config = {
 
-    nix.chrootDirs = [ "/dev" "/dev/pts" "/proc" "/bin" ];
+    nix.chrootDirs = [ "/bin" ];
 
     environment.etc."nix/nix.conf".source = nixConf;
 
@@ -275,28 +275,18 @@ in
           ) cfg.buildMachines;
       };
 
-    systemd.sockets."nix-daemon" =
-      { description = "Nix Daemon Socket";
-        wantedBy = [ "sockets.target" ];
-        before = [ "multi-user.target" ];
-        unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket/";
-        socketConfig.ListenStream = "/nix/var/nix/daemon-socket/socket";
-      };
+    systemd.packages = [ nix ];
 
-    systemd.services."nix-daemon" =
-      { description = "Nix Daemon";
+    systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
 
-        path = [ nix pkgs.openssl pkgs.utillinux ]
-          ++ optionals cfg.distributedBuilds [ pkgs.openssh pkgs.gzip ];
+    systemd.services.nix-daemon =
+      { path = [ nix pkgs.openssl pkgs.utillinux pkgs.openssh ]
+          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
 
         environment = cfg.envVars // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; };
 
-        unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket/";
-
         serviceConfig =
-          { ExecStart = "@${nix}/bin/nix-daemon nix-daemon --daemon";
-            KillMode = "process";
-            Nice = cfg.daemonNiceLevel;
+          { Nice = cfg.daemonNiceLevel;
             IOSchedulingPriority = cfg.daemonIONiceLevel;
             LimitNOFILE = 4096;
           };
@@ -352,8 +342,7 @@ in
           /nix/var/nix/profiles \
           /nix/var/nix/db \
           /nix/var/log/nix/drvs \
-          /nix/var/nix/channel-cache \
-          /nix/var/nix/chroots
+          /nix/var/nix/channel-cache
         mkdir -m 1777 -p \
           /nix/var/nix/gcroots/per-user \
           /nix/var/nix/profiles/per-user \
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
index fa20e0956f59..6a7a7f4cee72 100644
--- a/nixos/modules/services/misc/nix-gc.nix
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.nix.gc;
diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix
new file mode 100644
index 000000000000..80e7961b1f82
--- /dev/null
+++ b/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+let
+  serveOnly = pkgs.writeScript "nix-store-serve" ''
+    #!${pkgs.stdenv.shell}
+    if [ "$SSH_ORIGINAL_COMMAND" != "nix-store --serve" ]; then
+      echo 'Error: You are only allowed to run `nix-store --serve'\'''!' >&2
+      exit 1
+    fi
+    exec /run/current-system/sw/bin/nix-store --serve
+  '';
+
+  inherit (lib) mkIf mkOption types;
+in {
+  options = {
+    nix.sshServe = {
+      enable = mkOption {
+        description = "Whether to enable serving the nix store over ssh.";
+        default = false;
+        type = types.bool;
+      };
+    };
+  };
+
+  config = mkIf config.nix.sshServe.enable {
+    users.extraUsers.nix-ssh = {
+      description = "User for running nix-store --serve.";
+      uid = config.ids.uids.nix-ssh;
+      shell = pkgs.stdenv.shell;
+    };
+
+    services.openssh.enable = true;
+
+    services.openssh.extraConfig = ''
+      Match User nix-ssh
+        AllowAgentForwarding no
+        AllowTcpForwarding no
+        PermitTTY no
+        PermitTunnel no
+        X11Forwarding no
+        ForceCommand ${serveOnly}
+      Match All
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 1a8b85db3290..808c5dcbdc6f 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -3,9 +3,9 @@
 # of the virtual consoles.  The latter is useful for the installation
 # CD.
 
-{ config, pkgs, baseModules, ... } @ extraArgs:
+{ config, lib, pkgs, baseModules, ... } @ extraArgs:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix
new file mode 100644
index 000000000000..2c1fec9f6d7c
--- /dev/null
+++ b/nixos/modules/services/misc/rippled.nix
@@ -0,0 +1,314 @@
+# configuration building is commented out until better tested.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.rippled;
+
+  rippledStateCfgFile = "/var/lib/rippled/rippled.cfg";
+
+  rippledCfg = ''
+    [node_db]
+    type=HyperLevelDB
+    path=/var/lib/rippled/db/hyperldb
+
+    [debug_logfile]
+    /var/log/rippled/debug.log
+
+  ''
+  + optionalString (cfg.peerIp != null) ''
+    [peer_ip]
+    ${cfg.peerIp}
+
+    [peer_port]
+    ${toString cfg.peerPort}
+
+  ''
+  + cfg.extraConfig;
+
+  rippledCfgFile = pkgs.writeText "rippled.cfg" rippledCfg;
+    
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.rippled = {
+
+      enable = mkOption {
+        default = false;
+	description = "Whether to enable rippled";
+      };
+
+      #
+      # Rippled has a simple configuration file layout that is easy to 
+      # build with nix. Many of the options are defined here but are 
+      # commented out until the code to append them to the config above
+      # is written and they are tested.
+      #
+      # If you find a yourself implementing more options, please submit a 
+      # pull request.
+      #
+
+      /*
+      ips = mkOption {
+        default = [ "r.ripple.com 51235" ];
+	example = [ "192.168.0.1" "192.168.0.1 3939" "r.ripple.com 51235" ];
+	description = ''
+	  List of hostnames or ips where the Ripple protocol is served.
+	  For a starter list, you can either copy entries from: 
+	  https://ripple.com/ripple.txt or if you prefer you can let it
+	   default to r.ripple.com 51235
+
+	  A port may optionally be specified after adding a space to the 
+	  address. By convention, if known, IPs are listed in from most 
+	  to least trusted.
+	'';
+      };
+
+      ipsFixed = mkOption {
+        default = null;
+	example = [ "192.168.0.1" "192.168.0.1 3939" "r.ripple.com 51235" ];
+	description = ''
+	  List of IP addresses or hostnames to which rippled should always 
+	  attempt to maintain peer connections with. This is useful for 
+	  manually forming private networks, for example to configure a 
+	  validation server that connects to the Ripple network through a 
+	  public-facing server, or for building a set of cluster peers.
+
+	  A port may optionally be specified after adding a space to the address
+	'';
+      };
+      */
+
+      peerIp = mkOption {
+        default = null;
+	example = "0.0.0.0";
+	description = ''
+	  IP address or domain to bind to allow external connections from peers.
+	  Defaults to not binding, which disallows external connections from peers.
+        '';
+      };
+
+      peerPort = mkOption {
+	default = 51235;
+	description = ''
+	  If peerIp is supplied, corresponding port to bind to for peer connections.
+	'';
+      };
+
+      /*
+      peerPortProxy = mkOption {
+        type = types.int;
+	example = 51236;
+	description = ''
+	  An optional, additional listening port number for peers. Incoming
+	  connections on this port will be required to provide a PROXY Protocol
+	  handshake, described in this document (external link):
+
+	    http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
+
+	  The PROXY Protocol is a popular method used by elastic load balancing
+	  service providers such as Amazon, to identify the true IP address and
+	  port number of external incoming connections.
+
+	  In addition to enabling this setting, it will also be required to
+	  use your provider-specific control panel or administrative web page
+	  to configure your server instance to receive PROXY Protocol handshakes,
+	  and also to restrict access to your instance to the Elastic Load Balancer.
+	'';
+      };
+
+      peerPrivate = mkOption {
+        default = null;
+	example = 0;
+	description = ''
+	 0: Request peers to broadcast your address. Normal outbound peer connections [default]
+	 1: Request peers not broadcast your address. Only connect to configured peers.
+       '';
+     };
+
+     peerSslCipherList = mkOption {
+       default = null;
+       example = "ALL:!LOW:!EXP:!MD5:@STRENGTH";
+       description = ''
+         A colon delimited string with the allowed SSL cipher modes for peer. The
+	 choices for for ciphers are defined by the OpenSSL API function
+	 SSL_CTX_set_cipher_list, documented here (external link):
+
+	  http://pic.dhe.ibm.com/infocenter/tpfhelp/current/index.jsp?topic=%2Fcom.ibm.ztpf-ztpfdf.doc_put.cur%2Fgtpc2%2Fcpp_ssl_ctx_set_cipher_list.html
+
+	The default setting of "ALL:!LOW:!EXP:!MD5:@STRENGTH", which allows
+	non-authenticated peer connections (they are, however, secure).
+      '';
+    };
+
+    nodeSeed = mkOption {
+      default = null;
+      example = "RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE";
+      description = ''
+        This is used for clustering. To force a particular node seed or key, the
+	key can be set here.  The format is the same as the validation_seed field.
+	To obtain a validation seed, use the rippled validation_create command.
+      '';
+    };
+
+    clusterNodes = mkOption {
+      default = null;
+      example = [ "n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5" ];
+      description = ''
+        To extend full trust to other nodes, place their node public keys here.
+	Generally, you should only do this for nodes under common administration.
+	Node public keys start with an 'n'. To give a node a name for identification
+	place a space after the public key and then the name.
+      '';
+    };
+
+    sntpServers = mkOption {
+      default = null;
+      example = [ "time.nist.gov" "pool.ntp.org" ];
+      description = ''
+        IP address or domain of NTP servers to use for time synchronization.
+      '';
+    };
+
+    # TODO: websocket options
+
+    rpcAllowRemote = mkOption {
+      default = false;
+      description = ''
+        false: Allow RPC connections only from 127.0.0.1. [default]
+	true:  Allow RPC connections from any IP.
+      '';
+    };
+
+    rpcAdminAllow = mkOption {
+      example = [ "10.0.0.4" ];
+      description = ''
+        List of IP addresses allowed to have admin access.
+      '';
+    };
+
+    rpcAdminUser = mkOption {
+      type = types.str;
+      description = ''
+        As a server, require this as the admin user to be specified.  Also, require
+	rpc_admin_user and rpc_admin_password to be checked for RPC admin functions.
+	The request must specify these as the admin_user and admin_password in the
+	request object.
+      '';
+    };
+
+    rpcAdminPassword = mkOption {
+      type = types.str;
+      description = ''
+        As a server, require this as the admin pasword to be specified.  Also,
+	require rpc_admin_user and rpc_admin_password to be checked for RPC admin
+	functions.  The request must specify these as the admin_user and
+	admin_password in the request object.
+      '';
+    };
+
+      rpcIp = mkOption {
+        type = types.str;
+	description = ''
+	  IP address or domain to bind to allow insecure RPC connections.
+	  Defaults to not binding, which disallows RPC connections.
+	'';
+      };
+
+      rpcPort = mkOption {
+        type = types.int;
+        description = ''
+          If rpcIp is supplied, corresponding port to bind to for peer connections.
+        '';
+      };
+
+      rpcUser = mkOption {
+        type = types.str;
+	description = ''
+	  Require a this user to specified and require rpcPassword to
+	  be checked for RPC access via the rpcIp and rpcPort. The user and password
+	  must be specified via HTTP's basic authentication method.
+	  As a client, supply this to the server via HTTP's basic authentication
+	  method.
+	'';
+      };
+
+      rpcPassword = mkOption {
+        type = types.str;
+	description = ''
+	  Require a this password to specified and require rpc_user to
+	  be checked for RPC access via the rpcIp and rpcPort. The user and password
+	  must be specified via HTTP's basic authentication method.
+	  As a client, supply this to the server via HTTP's basic authentication
+	  method.
+	'';
+      };
+
+      rpcStartup = mkOption {
+        example = [ ''"command" : "log_level"'' ''"partition" : "ripplecalc"'' ''"severity" : "trace"'' ];
+	description = "List of RPC commands to run at startup.";
+      };
+
+      rpcSecure = mkOption {
+        default = false;
+	description = ''
+	  false: Server certificates are not provided for RPC clients using SSL [default]
+	  true:  Client RPC connections wil be provided with SSL certificates.
+
+	  Note that if rpc_secure is enabled, it will also be necessasry to configure the
+	  certificate file settings located in rpcSslCert, rpcSslChain, and rpcSslKey
+	'';
+      };
+      */
+
+      extraConfig = mkOption {
+        default = "";
+	description = ''
+	  Extra lines to be added verbatim to the rippled.cfg configuration file.
+	'';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = "rippled";
+        description = "Ripple server user";
+        uid = config.ids.uids.rippled;
+	home = "/var/lib/rippled";
+      };
+
+    systemd.services.rippled = {
+      path = [ pkgs.rippled ];
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.rippled}/bin/rippled --fg -q --conf ${rippledStateCfgFile}";
+	WorkingDirectory = "/var/lib/rippled";
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = mkIf (cfg.peerIp != null) [ cfg.peerPort ];
+
+    system.activationScripts.rippled = ''
+      mkdir -p /var/{lib,log}/rippled
+      chown -R rippled /var/{lib,log}/rippled
+      ln -sf ${rippledCfgFile} ${rippledStateCfgFile}
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/rogue.nix b/nixos/modules/services/misc/rogue.nix
index de25cc0fb982..ed8da8a518ff 100644
--- a/nixos/modules/services/misc/rogue.nix
+++ b/nixos/modules/services/misc/rogue.nix
@@ -1,9 +1,9 @@
 # Execute the game `rogue' on tty 9.  Mostly used by the NixOS
 # installation CD.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/svnserve.nix b/nixos/modules/services/misc/svnserve.nix
index b0806d14738b..848905ca457f 100644
--- a/nixos/modules/services/misc/svnserve.nix
+++ b/nixos/modules/services/misc/svnserve.nix
@@ -1,7 +1,7 @@
 # SVN server
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/misc/synergy.nix b/nixos/modules/services/misc/synergy.nix
index 63e7c7667e57..ec8ff426f0a6 100644
--- a/nixos/modules/services/misc/synergy.nix
+++ b/nixos/modules/services/misc/synergy.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 58ec8a49694b..6cc0c122ec62 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.apcupsd;
@@ -168,11 +168,11 @@ in
     # shuts off power.) Copied from here:
     # http://forums.opensuse.org/english/get-technical-help-here/applications/479499-apcupsd-systemd-killpower-issues.html
     systemd.services.apcupsd-killpower = {
+      description = "APC UPS Kill Power";
       after = [ "shutdown.target" ]; # append umount.target?
       before = [ "final.target" ];
       wantedBy = [ "shutdown.target" ];
       unitConfig = {
-        Description = "APC UPS Kill Power";
         ConditionPathExists = "/run/apcupsd/powerfail";
         DefaultDependencies = "no";
       };
diff --git a/nixos/modules/services/monitoring/dd-agent.nix b/nixos/modules/services/monitoring/dd-agent.nix
index 37ce1c099df9..bddf102ee517 100644
--- a/nixos/modules/services/monitoring/dd-agent.nix
+++ b/nixos/modules/services/monitoring/dd-agent.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.dd-agent;
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index be57b8c5c03f..cb67b9d4fcbb 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -1,20 +1,34 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.graphite;
-  writeTextOrNull = f: t: if t == null then null else pkgs.writeText f t;
+  writeTextOrNull = f: t: if t == null then null else pkgs.writeTextDir f t;
+
+  dataDir = cfg.dataDir;
+
+  configDir = pkgs.buildEnv {
+    name = "graphite-config";
+    paths = lists.filter (el: el != null) [
+      (writeTextOrNull "carbon.conf" cfg.carbon.config)
+      (writeTextOrNull "storage-agregation.conf" cfg.carbon.storageAggregation)
+      (writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas)
+      (writeTextOrNull "blacklist.conf" cfg.carbon.blacklist)
+      (writeTextOrNull "whitelist.conf" cfg.carbon.whitelist)
+      (writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules)
+      (writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules)
+      (writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules)
+    ];
+  };
 
-  dataDir = "/var/db/graphite";
   carbonOpts = name: with config.ids; ''
-    --nodaemon --syslog --prefix=${name} --pidfile /var/run/${name}.pid \
-    --uid ${toString uids.graphite} --gid ${toString uids.graphite} ${name}
+    --nodaemon --syslog --prefix=${name} --pidfile ${dataDir}/${name}.pid ${name}
   '';
   carbonEnv = {
     PYTHONPATH = "${pkgs.python27Packages.carbon}/lib/python2.7/site-packages";
     GRAPHITE_ROOT = dataDir;
-    GRAPHITE_CONF_DIR = "/etc/graphite/";
+    GRAPHITE_CONF_DIR = configDir;
     GRAPHITE_STORAGE_DIR = dataDir;
   };
 
@@ -23,6 +37,14 @@ in {
   ###### interface
 
   options.services.graphite = {
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/db/graphite";
+      description = ''
+        Data directory for graphite.
+      '';
+    };
+
     web = {
       enable = mkOption {
         description = "Whether to enable graphite web frontend";
@@ -38,8 +60,8 @@ in {
 
       port = mkOption {
         description = "Graphite web frontend port";
-        default = "8080";
-        type = types.str;
+        default = 8080;
+        type = types.int;
       };
     };
 
@@ -152,31 +174,17 @@ in {
   ###### implementation
 
   config = mkIf (cfg.carbon.enableAggregator || cfg.carbon.enableCache || cfg.carbon.enableRelay || cfg.web.enable) {
-    environment.etc = lists.filter (el: el.source != null) [
-      { source = writeTextOrNull "carbon.conf" cfg.carbon.config;
-        target = "graphite/carbon.conf"; }
-      { source = writeTextOrNull "storage-agregation.conf" cfg.carbon.storageAggregation;
-        target = "graphite/storage-agregation.conf"; }
-      { source = writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas;
-        target = "graphite/storage-schemas.conf"; }
-      { source = writeTextOrNull "blacklist.conf" cfg.carbon.blacklist;
-        target = "graphite/blacklist.conf"; }
-      { source = writeTextOrNull "whitelist.conf" cfg.carbon.whitelist;
-        target = "graphite/whitelist.conf"; }
-      { source = writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules;
-        target = "graphite/rewrite-rules.conf"; }
-      { source = writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules;
-        target = "graphite/relay-rules.conf"; }
-      { source = writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules;
-        target = "graphite/aggregation-rules.conf"; }
-    ];
-
-    systemd.services.carbonCache = mkIf cfg.carbon.enableCache {
+    systemd.services.carbonCache = {
+      enable = cfg.carbon.enableCache;
       description = "Graphite Data Storage Backend";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-cache"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-cache"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon
         cfg.carbon.config
@@ -185,33 +193,45 @@ in {
         cfg.carbon.rewriteRules
       ];
       preStart = ''
-        mkdir -p ${dataDir}/whisper
+        mkdir -m 0700 -p ${cfg.dataDir}/whisper
+        if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
       '';
     };
 
-    systemd.services.carbonAggregator = mkIf cfg.carbon.enableAggregator {
+    systemd.services.carbonAggregator = {
+      enable = cfg.carbon.enableAggregator;
       description = "Carbon Data Aggregator";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-aggregator"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-aggregator"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.aggregationRules
       ];
     };
 
-    systemd.services.carbonRelay = mkIf cfg.carbon.enableRelay {
+    systemd.services.carbonRelay = {
+      enable = cfg.carbon.enableRelay;
       description = "Carbon Data Relay";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-relay"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-relay"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.relayRules
       ];
     };
 
-    systemd.services.graphiteWeb = mkIf cfg.web.enable {
+    systemd.services.graphiteWeb = {
+      enable = cfg.web.enable;
       description = "Graphite Web Interface";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
@@ -224,14 +244,15 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.python27Packages.waitress}/bin/waitress-serve \
-          --host=${cfg.web.host} --port=${cfg.web.port} \
+          --host=${cfg.web.host} --port=${toString cfg.web.port} \
           --call django.core.handlers.wsgi:WSGIHandler'';
         User = "graphite";
         Group = "graphite";
       };
       preStart = ''
         if ! test -e ${dataDir}/db-created; then
-          mkdir -p ${dataDir}/{whisper/,log/webapp/}
+          mkdir -m 0700 -p ${dataDir}/{whisper/,log/webapp/}
+          if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
 
           # populate database
           ${pkgs.python27Packages.graphite_web}/bin/manage-graphite.py syncdb --noinput
@@ -259,7 +280,6 @@ in {
       uid = config.ids.uids.graphite;
       description = "Graphite daemon user";
       home = dataDir;
-      createHome = true;
     };
     users.extraGroups.graphite.gid = config.ids.gids.graphite;
   };
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 153f49429029..966c2eca282a 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
 # TODO: support munin-async
 # TODO: LWP/Pg perl libs aren't recognized
@@ -11,7 +11,7 @@
 # nginx http://munin.readthedocs.org/en/latest/example/webserver/nginx.html
 
 
-with pkgs.lib;
+with lib;
 
 let
   nodeCfg = config.services.munin-node;
@@ -173,10 +173,12 @@ in
       name = "munin";
       description = "Munin monitoring user";
       group = "munin";
+      uid = config.ids.uids.munin;
     }];
 
     users.extraGroups = [{
       name = "munin";
+      gid = config.ids.gids.munin;
     }];
 
   }) (mkIf nodeCfg.enable {
diff --git a/nixos/modules/services/monitoring/nagios/default.nix b/nixos/modules/services/monitoring/nagios/default.nix
index c809a3b84573..2eeddf782501 100644
--- a/nixos/modules/services/monitoring/nagios/default.nix
+++ b/nixos/modules/services/monitoring/nagios/default.nix
@@ -1,7 +1,7 @@
 # Nagios system/network monitoring daemon.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
index 512e639721e4..250035fe447f 100644
--- a/nixos/modules/services/monitoring/smartd.nix
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
index 979debefdd9f..05950639c1e0 100644
--- a/nixos/modules/services/monitoring/statsd.nix
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -64,13 +64,13 @@ in
 
     graphiteHost = mkOption {
       description = "Hostname or IP of Graphite server";
-      default = "127.0.0.1";
+      default = config.services.graphite.web.host;
       type = types.str;
     };
 
     graphitePort = mkOption {
       description = "Port of Graphite server";
-      default = 2003;
+      default = config.services.graphite.web.port;
       type = types.uniq types.int;
     };
 
diff --git a/nixos/modules/services/monitoring/systemhealth.nix b/nixos/modules/services/monitoring/systemhealth.nix
index 0a3e666ad4e7..b0e59595e133 100644
--- a/nixos/modules/services/monitoring/systemhealth.nix
+++ b/nixos/modules/services/monitoring/systemhealth.nix
@@ -1,6 +1,6 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.systemhealth;
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index c00f4bad9351..cc9026f768a8 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -1,8 +1,8 @@
-{config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
 # TODO: This is not secure, have a look at the file docs/security.txt inside
 # the project sources.
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.power.ups;
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
index 229236c1bbd4..481298f763a4 100644
--- a/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -1,7 +1,7 @@
 # Zabbix agent daemon.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index 6735b4ca3279..ca283ea2a99f 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -1,7 +1,7 @@
 # Zabbix server daemon.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/network-filesystems/drbd.nix b/nixos/modules/services/network-filesystems/drbd.nix
index 1a00ccab0a61..b914724abfe2 100644
--- a/nixos/modules/services/network-filesystems/drbd.nix
+++ b/nixos/modules/services/network-filesystems/drbd.nix
@@ -1,8 +1,8 @@
 # Support for DRBD, the Distributed Replicated Block Device.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.services.drbd; in
 
diff --git a/nixos/modules/services/network-filesystems/nfsd.nix b/nixos/modules/services/network-filesystems/nfsd.nix
index 4daa5e9d0639..2217fec3b0f7 100644
--- a/nixos/modules/services/network-filesystems/nfsd.nix
+++ b/nixos/modules/services/network-filesystems/nfsd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
index 4a888b64bd34..b34ebc3663e3 100644
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix
@@ -46,6 +46,16 @@ in
         description = "Cache directory.";
       };
 
+      crypt = mkOption {
+        default = false;
+        description = "Whether to enable (weak) protocol encryption.";
+      };
+
+      sparse = mkOption {
+        default = false;
+        description = "Minimal cell list in /afs.";
+      };
+
     };
   };
 
@@ -70,18 +80,23 @@ in
         startOn = "started network-interfaces";
         stopOn = "stopping network-interfaces";
 
-	preStart = ''
-	  mkdir -m 0755 /afs || true
-	  mkdir -m 0755 -p ${cfg.cacheDirectory} || true
+        preStart = ''
+          mkdir -p -m 0755 /afs
+          mkdir -m 0700 -p ${cfg.cacheDirectory}
           ${pkgs.module_init_tools}/sbin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true
-          ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} -dynroot -fakestat
-	'';
-
-	postStop = ''
-	  umount /afs
+          ${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
-	  rmmod libafs
-	'';
+          ${pkgs.module_init_tools}/sbin/rmmod libafs
+        '';
 
       };
 
diff --git a/nixos/modules/services/network-filesystems/rsyncd.nix b/nixos/modules/services/network-filesystems/rsyncd.nix
new file mode 100644
index 000000000000..bc17add809b2
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/rsyncd.nix
@@ -0,0 +1,139 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.rsyncd;
+
+  motdFile = pkgs.writeText "rsyncd-motd" cfg.motd;
+
+  rsyncdCfg = ""
+    + optionalString (cfg.motd != "") "motd file = ${motdFile}\n"
+    + optionalString (cfg.address != "") "address = ${cfg.address}\n"
+    + optionalString (cfg.port != 873) "port = ${toString cfg.port}\n"
+    + cfg.extraConfig
+    + "\n"
+    + flip concatMapStrings cfg.modules (m: "[${m.name}]\n\tpath = ${m.path}\n"
+      + optionalString (m.comment != "") "\tcomment = ${m.comment}\n"
+      + m.extraConfig
+      + "\n"
+    );
+
+  rsyncdCfgFile = pkgs.writeText "rsyncd.conf" rsyncdCfg;
+
+in
+
+{
+  options = {
+
+    services.rsyncd = {
+
+      enable = mkOption {
+        default = false;
+	description = "Whether to enable the rsync daemon.";
+      };
+
+      motd = mkOption {
+        type = types.string;
+        default = "";
+	description = ''
+	  Message of the day to display to clients on each connect.
+	  This usually contains site information and any legal notices.
+	'';
+      };
+
+      port = mkOption {
+        default = 873;
+	type = types.int;
+	description = "TCP port the daemon will listen on.";
+      };
+
+      address = mkOption {
+        default = "";
+	example = "192.168.1.2";
+	description = ''
+	  IP address the daemon will listen on; rsyncd will listen on
+	  all addresses if this is not specified.
+	'';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+	default = "";
+	description = ''
+	  Lines of configuration to add to rsyncd globally.
+	  See <literal>man rsyncd.conf</literal> for more options.
+	'';
+      };
+
+      modules = mkOption {
+        default = [ ];
+	example = [ 
+	  { name = "ftp"; 
+	    path = "/home/ftp"; 
+	    comment = "ftp export area";
+	    extraConfig = ''
+	      secrets file = /etc/rsyncd.secrets
+	    '';
+	  }
+	];
+	description = "The list of file paths to export.";
+	type = types.listOf types.optionSet;
+
+	options = {
+
+	  name = mkOption {
+	    example = "ftp";
+	    type = types.string;
+	    description = "Name of export module.";
+	  };
+
+	  comment = mkOption {
+	    default = "";
+	    description = ''
+	      Description string that is displayed next to the module name
+	      when clients obtain a list of available modules.
+	    '';
+	  };
+
+	  path = mkOption {
+	    example = "/home/ftp";
+	    type = types.string;
+	    description = "Directory to make available in this module.";
+   	  };
+
+          extraConfig = mkOption {
+            type = types.lines;
+	    default = "";
+            description = ''
+	      Lines of configuration to add to this module.
+	      See <literal>man rsyncd.conf</literal> for more options.
+	    '';
+          };
+	};
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc = singleton
+    { source = rsyncdCfgFile;
+      target = "rsyncd.conf";
+    };
+
+    systemd.services.rsyncd = {
+      description = "Rsync daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ pkgs.rsync ];
+
+      serviceConfig.ExecStart = "${pkgs.rsync}/bin/rsync --daemon --no-detach";
+    };
+
+    networking.firewall.allowedTCPPorts = [ cfg.port ];
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 4f6fce6cd52e..51a4d193d504 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
index 8652d0daf4c8..516238fdddf6 100644
--- a/nixos/modules/services/networking/amuled.nix
+++ b/nixos/modules/services/networking/amuled.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.amule;
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 2256de89630d..284b2b84e6c7 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -1,7 +1,7 @@
 # Avahi daemon.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -142,6 +142,10 @@ in
     services.dbus.enable = true;
     services.dbus.packages = [avahi];
 
+    # Enabling Avahi without exposing it in the firewall doesn't make
+    # sense.
+    networking.firewall.allowedUDPPorts = [ 5353 ];
+
   };
 
 }
diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix
index 765dc014dcb7..57547da10067 100644
--- a/nixos/modules/services/networking/bind.nix
+++ b/nixos/modules/services/networking/bind.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index fe37e8ea0120..7276603da16f 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/btsync.nix b/nixos/modules/services/networking/btsync.nix
new file mode 100644
index 000000000000..8b288a713c60
--- /dev/null
+++ b/nixos/modules/services/networking/btsync.nix
@@ -0,0 +1,280 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.btsync;
+  listenAddr = cfg.httpListenAddr + ":" + (toString cfg.httpListenPort);
+
+  boolStr = x: if x then "true" else "false";
+  optionalEmptyStr = b: v: optionalString (b != "") v;
+
+  webUIConfig = optionalString cfg.enableWebUI
+    ''
+      "webui":
+      {
+        ${optionalEmptyStr cfg.httpLogin "\"login\":    \"${cfg.httpLogin}\","}
+        ${optionalEmptyStr cfg.httpPass  "\"password\": \"${cfg.httpPass}\","}
+        ${optionalEmptyStr cfg.apiKey    "\"api_key\":  \"${cfg.apiKey}\","}
+        "listen": "${listenAddr}"
+      }
+    '';
+
+  knownHosts = e:
+    optionalString (e ? "knownHosts")
+      (concatStringsSep "," (map (v: "\"${v}\"") e."knownHosts"));
+
+  sharedFoldersRecord =
+    concatStringsSep "," (map (entry:
+      let helper = attr: v:
+        if (entry ? attr) then boolStr entry.attr else boolStr v;
+      in
+      ''
+        {
+          "secret": "${entry.secret}",
+          "dir":    "${entry.directory}",
+
+          "use_relay_server": ${helper "useRelayServer" true},
+          "use_tracker":      ${helper "useTracker"     true},
+          "use_dht":          ${helper "useDHT"        false},
+
+          "search_lan":       ${helper "searchLAN"      true},
+          "use_sync_trash":   ${helper "useSyncTrash"   true},
+
+          "known_hosts": [${knownHosts entry}]
+        }
+      '') cfg.sharedFolders);
+
+  sharedFoldersConfig = optionalString (cfg.sharedFolders != [])
+    ''
+      "shared_folders":
+        [
+        ${sharedFoldersRecord}
+        ]
+    '';
+
+  configFile = pkgs.writeText "btsync.config"
+    ''
+      {
+        "device_name":     "${cfg.deviceName}",
+        "storage_path":    "/var/lib/btsync",
+        "listening_port":  ${toString cfg.listeningPort},
+        "use_gui":         false,
+
+        "check_for_updates": ${boolStr cfg.checkForUpdates},
+        "use_upnp":          ${boolStr cfg.useUpnp},
+        "download_limit":    ${toString cfg.downloadLimit},
+        "upload_limit":      ${toString cfg.uploadLimit},
+        "lan_encrypt_data":  ${boolStr cfg.encryptLAN},
+
+        ${webUIConfig}
+        ${sharedFoldersConfig}
+      }
+    '';
+in
+{
+  options = {
+    services.btsync = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, start the Bittorrent Sync daemon. Once enabled,
+          you can interact with the service through the Web UI, or
+          configure it in your NixOS configuration. Enabling the
+          <literal>btsync</literal> service also installs a
+          multi-instance systemd unit which can be used to start
+          user-specific copies of the daemon. Once installed, you can
+          use <literal>systemctl start btsync@user</literal> to start
+          the daemon only for user <literal>user</literal>, using the
+          configuration file located at
+          <literal>$HOME/.config/btsync.conf</literal>
+        '';
+      };
+
+      deviceName = mkOption {
+        type = types.str;
+        example = "Voltron";
+        description = ''
+          Name of the Bittorrent Sync device.
+        '';
+      };
+
+      listeningPort = mkOption {
+        type = types.int;
+        default = 0;
+        example = 44444;
+        description = ''
+          Listening port. Defaults to 0 which randomizes the port.
+        '';
+      };
+
+      checkForUpdates = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Determines whether to check for updates and alert the user
+          about them in the UI.
+        '';
+      };
+
+      useUpnp = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Use Universal Plug-n-Play (UPnP)
+        '';
+      };
+
+      downloadLimit = mkOption {
+        type = types.int;
+        default = 0;
+        example = 1024;
+        description = ''
+          Download speed limit. 0 is unlimited (default).
+        '';
+      };
+
+      uploadLimit = mkOption {
+        type = types.int;
+        default = 0;
+        example = 1024;
+        description = ''
+          Upload speed limit. 0 is unlimited (default).
+        '';
+      };
+
+      httpListenAddr = mkOption {
+        type = types.str;
+        default = "0.0.0.0";
+        example = "1.2.3.4";
+        description = ''
+          HTTP address to bind to.
+        '';
+      };
+
+      httpListenPort = mkOption {
+        type = types.int;
+        default = 9000;
+        description = ''
+          HTTP port to bind on.
+        '';
+      };
+
+      httpLogin = mkOption {
+        type = types.str;
+        example = "allyourbase";
+        description = ''
+          HTTP web login username.
+        '';
+      };
+
+      httpPass = mkOption {
+        type = types.str;
+        example = "arebelongtous";
+        description = ''
+          HTTP web login password.
+        '';
+      };
+
+      encryptLAN = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Encrypt LAN data.";
+      };
+
+      enableWebUI = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Web UI for administration. Bound to the specified
+          <literal>httpListenAddress</literal> and
+          <literal>httpListenPort</literal>.
+          '';
+      };
+
+      apiKey = mkOption {
+        type = types.str;
+        default = "";
+        description = "API key, which enables the developer API.";
+      };
+
+      sharedFolders = mkOption {
+        default = [];
+        example =
+          [ { secret         = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y";
+              directory      = "/home/user/sync_test";
+              useRelayServer = true;
+              useTracker     = true;
+              useDHT         = false;
+              searchLAN      = true;
+              useSyncTrash   = true;
+              knownHosts     =
+                [ "192.168.1.2:4444"
+                  "192.168.1.3:4444"
+                ];
+            }
+          ];
+        description = ''
+          Shared folder list. If enabled, web UI must be
+          disabled. Secrets can be generated using <literal>btsync
+          --generate-secret</literal>. Note that this secret will be
+          put inside the Nix store, so it is realistically not very
+          secret.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.deviceName != "";
+          message   = "Device name cannot be empty.";
+        }
+        { assertion = cfg.enableWebUI -> cfg.sharedFolders == [];
+          message   = "If using shared folders, the web UI cannot be enabled.";
+        }
+        { assertion = cfg.apiKey != "" -> cfg.enableWebUI;
+          message   = "If you're using an API key, you must enable the web server.";
+        }
+        # TODO FIXME: the README says not specifying the login/pass means it
+        # should disable authentication, but apparently it doesn't?
+        { assertion = cfg.enableWebUI -> cfg.httpLogin != "" && cfg.httpPass != "";
+          message   = "If using the web UI, you must configure a login/password.";
+        }
+        # TODO FIXME: assert the existence of sharedFolder directories?
+      ];
+
+    users.extraUsers.btsync = {
+      description     = "Bittorrent Sync Service user";
+      home            = "/var/lib/btsync";
+      createHome      = true;
+      uid             = config.ids.uids.btsync;
+    };
+
+    systemd.services.btsync = with pkgs; {
+      description = "Bittorrent Sync Service";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network.target" ];
+      serviceConfig = {
+        Restart   = "on-abort";
+        User      = "btsync";
+        ExecStart =
+          "${bittorrentSync}/bin/btsync --nodaemon --config ${configFile}";
+      };
+    };
+
+    systemd.services."btsync@" = with pkgs; {
+      description = "Bittorrent Sync Service for %i";
+      after       = [ "network.target" ];
+      serviceConfig = {
+        Restart   = "on-abort";
+        User      = "%i";
+        ExecStart =
+          "${bittorrentSync}/bin/btsync --nodaemon --config %h/.config/btsync.conf";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.bittorrentSync ];
+  };
+}
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
index 5e9818858e0c..d1684dd9f05d 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/chrony.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
new file mode 100644
index 000000000000..9306ffd5a18a
--- /dev/null
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -0,0 +1,207 @@
+# You may notice the commented out sections in this file,
+# it would be great to configure cjdns from nix, but cjdns 
+# reads its configuration from stdin, including the private
+# key and admin password, all nested in a JSON structure.
+#
+# Until a good method of storing the keys outside the nix 
+# store and mixing them back into a string is devised
+# (without too much shell hackery), a skeleton of the
+# configuration building lies commented out.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.cjdns;
+
+  /*
+  # can't keep keys and passwords in the nix store,
+  # but don't want to deal with this stdin quagmire.
+
+  cjdrouteConf = '' {
+    "admin": {"bind": "${cfg.admin.bind}", "password": "\${CJDNS_ADMIN}" },
+    "privateKey": "\${CJDNS_KEY}",
+
+    "interfaces": {
+    ''
+
+    + optionalString (cfg.interfaces.udp.bind.address != null) ''
+      "UDPInterface": [ {
+        "bind": "${cfg.interfaces.udp.bind.address}:"''
+	   ${if cfg.interfaces.upd.bind.port != null
+             then ${toString cfg.interfaces.udp.bind.port}
+	     else ${RANDOM}
+	   fi)
+      + '' } ]''
+
+    + (if cfg.interfaces.eth.bind != null then ''
+      "ETHInterface": [ {
+        "bind": "${cfg.interfaces.eth.bind}",
+        "beacon": ${toString cfg.interfaces.eth.beacon}
+      } ]
+    '' fi )
+    + ''
+    },
+    "router": { "interface": { "type": "TUNInterface" }, },
+    "security": [ { "setuser": "nobody" } ]
+    }
+    '';   
+
+    cjdrouteConfFile = pkgs.writeText "cjdroute.conf" cjdrouteConf
+    */
+in
+
+{
+  options = {
+
+    services.cjdns = {
+
+      enable = mkOption {
+        type = types.bool;
+	default = false;
+        description = ''
+          Enable this option to start a instance of the 
+          cjdns network encryption and and routing engine.
+          Configuration will be read from <literal>confFile</literal>.
+        '';
+      };
+
+      confFile = mkOption {
+	default = "/etc/cjdroute.conf";
+        description = ''
+          Configuration file to pipe to cjdroute.
+        '';
+      };
+
+      /*
+      admin = {
+        bind = mkOption {
+	  default = "127.0.0.1:11234";
+	  description = ''
+            Bind the administration port to this address and port.
+	  '';
+        };
+
+	passwordFile = mkOption {
+	  example = "/root/cjdns.adminPassword";
+	  description = ''
+	    File containing a password to the administration port.
+	  '';
+	};
+      };
+
+      keyFile = mkOption {
+        type = types.str;
+	example = "/root/cjdns.key";
+	description = ''
+	  Path to a file containing a cjdns private key on a single line.
+	'';
+      };
+      
+      passwordsFile = mkOption {
+        type = types.str;
+	default = null;
+	example = "/root/cjdns.authorizedPasswords";
+	description = ''
+	  A file containing a list of json dictionaries with passwords.
+	  For example:
+	    {"password": "s8xf5z7znl4jt05g922n3wpk75wkypk"},
+	    { "name": "nice guy",
+	      "password": "xhthk1mglz8tpjrbbvdlhyc092rhpx5"},
+	    {"password": "3qfxyhmrht7uwzq29pmhbdm9w4bnc8w"}
+	  '';
+	};
+
+      interfaces = {
+        udp = {
+	  bind = { 
+            address = mkOption {
+	      default = "0.0.0.0";
+	      description = ''
+	        Address to bind UDP tunnels to; disable by setting to null;
+	      '';
+ 	    };
+	    port = mkOption {
+	      type = types.int;
+	      default = null;
+	      description = ''
+	        Port to bind UDP tunnels to.
+	        A port will be choosen at random if this is not set.
+	        This option is required to act as the server end of 
+	        a tunnel.
+	      '';
+ 	    };
+	  };
+	};
+
+	eth = {
+	  bind = mkOption {
+	    default = null;
+	    example = "eth0";
+	    description = ''
+	      Bind to this device and operate with native wire format.
+	    '';
+	  };
+
+	  beacon = mkOption {
+	    default = 2;
+	    description = ''
+	      Auto-connect to other cjdns nodes on the same network.
+	      Options:
+	        0 -- Disabled.
+
+                1 -- Accept beacons, this will cause cjdns to accept incoming
+		     beacon messages and try connecting to the sender.
+
+		2 -- Accept and send beacons, this will cause cjdns to broadcast
+		     messages on the local network which contain a randomly
+		     generated per-session password, other nodes which have this
+                     set to 1 or 2 will hear the beacon messages and connect
+                     automatically.
+            '';
+	  };
+	  
+	  connectTo = mkOption {
+	    type = types.listOf types.str;
+	    default = [];
+	    description = ''
+	      Credentials for connecting look similar to UDP credientials
+              except they begin with the mac address, for example:
+              "01:02:03:04:05:06":{"password":"a","publicKey":"b"}
+	    '';
+	  };
+        };
+      };
+      */
+    };
+  };
+
+  config = mkIf config.services.cjdns.enable {
+
+    boot.kernelModules = [ "tun" ];
+
+    /*
+    networking.firewall.allowedUDPPorts = mkIf (cfg.udp.bind.port != null) [
+      cfg.udp.bind.port
+    ];
+    */
+
+    systemd.services.cjdns = {
+      description = "encrypted networking for everybody";
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      before = [ "network.target" ];
+      path = [ pkgs.cjdns ];
+
+      serviceConfig = {
+        Type = "forking";
+	ExecStart = ''
+          ${pkgs.stdenv.shell} -c "${pkgs.cjdns}/sbin/cjdroute < ${cfg.confFile}"
+	'';
+	Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/cntlm.nix b/nixos/modules/services/networking/cntlm.nix
index 96396878afc9..a50aa4d0636b 100644
--- a/nixos/modules/services/networking/cntlm.nix
+++ b/nixos/modules/services/networking/cntlm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -34,7 +34,7 @@ in
 
       password = mkOption {
         default = "/etc/cntlm.password";
-        type = with pkgs.lib.types; string;
+        type = types.str;
         description = ''Proxy account password. Note: use chmod 0600 on /etc/cntlm.password for security.'';
       };
 
diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix
index 2b26fe88129b..7ed1e4805d8f 100644
--- a/nixos/modules/services/networking/connman.nix
+++ b/nixos/modules/services/networking/connman.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 with pkgs;
 
 let
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 08a5d6de6a2b..35a3cfff8406 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -1,10 +1,10 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
-  inherit (pkgs) dhcpcd;
+  dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; };
 
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge.
@@ -34,8 +34,9 @@ let
 
       # Ignore peth* devices; on Xen, they're renamed physical
       # Ethernet cards used for bridging.  Likewise for vif* and tap*
-      # (Xen) and virbr* and vnet* (libvirt).
-      denyinterfaces ${toString ignoredInterfaces} peth* vif* tap* tun* virbr* vnet* vboxnet*
+      # (Xen) and virbr* and vnet* (libvirt) and c-* and ctmp-* (NixOS
+      # containers).
+      denyinterfaces ${toString ignoredInterfaces} peth* vif* tap* tun* virbr* vnet* vboxnet* c-* ctmp-*
 
       ${config.networking.dhcpcd.extraConfig}
     '';
@@ -79,6 +80,7 @@ in
   options = {
 
     networking.dhcpcd.denyInterfaces = mkOption {
+      type = types.listOf types.str;
       default = [];
       description = ''
          Disable the DHCP client for any interface whose name matches
@@ -89,6 +91,7 @@ in
     };
 
     networking.dhcpcd.extraConfig = mkOption {
+      type = types.lines;
       default = "";
       description = ''
          Literal string to append to the config file generated for dhcpcd.
@@ -106,7 +109,7 @@ in
       { description = "DHCP Client";
 
         wantedBy = [ "network.target" ];
-        after = [ "systemd-udev-settle.service" ];
+        after = [ "systemd-udev-settle.service" ]; # FIXME
 
         # Stopping dhcpcd during a reconfiguration is undesirable
         # because it brings down the network interfaces configured by
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
index 5b2058e4e129..89f686c28703 100644
--- a/nixos/modules/services/networking/dhcpd.nix
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix
index b726493d421f..8e38b9d017a3 100644
--- a/nixos/modules/services/networking/dnsmasq.nix
+++ b/nixos/modules/services/networking/dnsmasq.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.dnsmasq;
diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
index 8a01c217eb5c..28b8e234a5cf 100644
--- a/nixos/modules/services/networking/ejabberd.nix
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 3c0c51e6ec8a..42914bfe5d62 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -18,11 +18,9 @@
 
 */
 
+{ config, lib, pkgs, ... }:
 
-
-{ config, pkgs, ... }:
-
-with pkgs.lib;
+with lib;
 
 let
 
@@ -32,9 +30,9 @@ let
     ''
       # Helper command to manipulate both the IPv4 and IPv6 tables.
       ip46tables() {
-        iptables "$@"
+        iptables -w "$@"
         ${optionalString config.networking.enableIPv6 ''
-          ip6tables "$@"
+          ip6tables -w "$@"
         ''}
       }
     '';
@@ -54,7 +52,7 @@ in
 
     networking.firewall.enable = mkOption {
       type = types.bool;
-      default = false;
+      default = true;
       description =
         ''
           Whether to enable the firewall.  This is a simple stateful
@@ -128,6 +126,17 @@ in
         '';
     };
 
+    networking.firewall.allowedTCPPortRanges = mkOption {
+      default = [];
+      example = [ { from = 8999; to = 9003; } ];
+      type = types.listOf (types.attrsOf types.int);
+      description =
+        ''
+          A range of TCP ports on which incoming connections are
+          accepted.
+        '';
+    };
+
     networking.firewall.allowedUDPPorts = mkOption {
       default = [];
       example = [ 53 ];
@@ -138,6 +147,16 @@ in
         '';
     };
 
+    networking.firewall.allowedUDPPortRanges = mkOption {
+      default = [];
+      example = [ { from = 60000; to = 61000; } ];
+      type = types.listOf (types.attrsOf types.int);
+      description =
+        ''
+          Range of open UDP ports.
+        '';
+    };
+
     networking.firewall.allowPing = mkOption {
       default = false;
       type = types.bool;
@@ -150,6 +169,17 @@ in
         '';
     };
 
+    networking.firewall.pingLimit = mkOption {
+      default = null;
+      type = types.nullOr (types.separatedString " ");
+      description =
+        ''
+          If pings are allowed, this allows setting rate limits
+          on them. If non-null, this option should be in the form
+          of flags like "-limit 1/minute -limit-burst 5"
+        '';
+    };
+
     networking.firewall.checkReversePath = mkOption {
       default = kernelHasRPFilter;
       type = types.bool;
@@ -234,14 +264,23 @@ in
                      message = "This kernel does not support disabling conntrack helpers"; }
                  ];
 
-    jobs.firewall =
+    systemd.services.firewall =
       { description = "Firewall";
 
-        startOn = "started network-interfaces";
+        wantedBy = [ "network.target" ];
+        after = [ "network-interfaces.target" "systemd-modules-load.service" ];
 
         path = [ pkgs.iptables ];
 
-        preStart =
+        # FIXME: this module may also try to load kernel modules, but
+        # containers don't have CAP_SYS_MODULE. So the host system had
+        # better have all necessary modules already loaded.
+        unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+
+        script =
           ''
             ${helpers}
 
@@ -322,6 +361,15 @@ in
               ) cfg.allowedTCPPorts
             }
 
+            # Accept connections to the allowed TCP port ranges.
+            ${concatMapStrings (rangeAttr:
+                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+                ''
+                  ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept
+                ''
+              ) cfg.allowedTCPPortRanges
+            }
+
             # Accept packets on the allowed UDP ports.
             ${concatMapStrings (port:
                 ''
@@ -330,13 +378,24 @@ in
               ) cfg.allowedUDPPorts
             }
 
+            # Accept packets on the allowed UDP port ranges.
+            ${concatMapStrings (rangeAttr:
+                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+                ''
+                  ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept
+                ''
+              ) cfg.allowedUDPPortRanges
+            }
+
             # Accept IPv4 multicast.  Not a big security risk since
             # probably nobody is listening anyway.
             #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept
 
             # Optionally respond to ICMPv4 pings.
             ${optionalString cfg.allowPing ''
-              iptables -A nixos-fw -p icmp --icmp-type echo-request -j nixos-fw-accept
+              iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
+                "-m limit ${cfg.pingLimit} "
+              }-j nixos-fw-accept
             ''}
 
             # Accept all ICMPv6 messages except redirects and node
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
index f5bc550ab5f8..5ba85178179b 100644
--- a/nixos/modules/services/networking/flashpolicyd.nix
+++ b/nixos/modules/services/networking/flashpolicyd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/freenet.nix b/nixos/modules/services/networking/freenet.nix
index a4bd2098986d..e9cacf4a16e8 100644
--- a/nixos/modules/services/networking/freenet.nix
+++ b/nixos/modules/services/networking/freenet.nix
@@ -1,8 +1,8 @@
 # NixOS module for Freenet daemon
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
index a7c7c206198f..5864efaca51f 100644
--- a/nixos/modules/services/networking/git-daemon.nix
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -1,5 +1,5 @@
-{pkgs, config, ...}:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
 
   cfg = config.services.gitDaemon;
@@ -101,7 +101,7 @@ in
       name = "git-daemon";
       startOn = "ip-up";
       exec = "${pkgs.git}/bin/git daemon --reuseaddr "
-        + (optionalString (cfg.basePath != "") "--basepath=${cfg.basePath} ")
+        + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ")
         + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
         + "--port=${toString cfg.port} --user=${gitUser} --group=${gitUser} ${cfg.options} "
         + "--verbose " + (optionalString cfg.exportAll "--export-all")  + concatStringsSep " " cfg.repositories;
diff --git a/nixos/modules/services/networking/gnunet.nix b/nixos/modules/services/networking/gnunet.nix
index 421c0d9bb697..03ee54af4334 100644
--- a/nixos/modules/services/networking/gnunet.nix
+++ b/nixos/modules/services/networking/gnunet.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/gogoclient.nix b/nixos/modules/services/networking/gogoclient.nix
index 3b92eb8b06bd..416007941976 100644
--- a/nixos/modules/services/networking/gogoclient.nix
+++ b/nixos/modules/services/networking/gogoclient.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.services.gogoclient;
 in
diff --git a/nixos/modules/services/networking/haproxy.nix b/nixos/modules/services/networking/haproxy.nix
index c8345a528a72..c2e2c2d7a418 100644
--- a/nixos/modules/services/networking/haproxy.nix
+++ b/nixos/modules/services/networking/haproxy.nix
@@ -1,9 +1,9 @@
-{ config, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 let
   cfg = config.services.haproxy;
   haproxyCfg = pkgs.writeText "haproxy.conf" cfg.config;
 in
-with pkgs.lib;
+with lib;
 {
   options = {
     services.haproxy = {
diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix
index 4edea12b6be8..2adbb0a5c4e3 100644
--- a/nixos/modules/services/networking/hostapd.nix
+++ b/nixos/modules/services/networking/hostapd.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
 # TODO:
 #
@@ -9,7 +9,7 @@
 #   wpa_supplicant and hostapd on the same wireless interface doesn't make any sense
 #   perhaps an assertion that there is a dhcp server and a dns server on the IP address serviced by the hostapd?
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/ifplugd.nix b/nixos/modules/services/networking/ifplugd.nix
index 4e939d603542..20bfca8f8723 100644
--- a/nixos/modules/services/networking/ifplugd.nix
+++ b/nixos/modules/services/networking/ifplugd.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/iodined.nix b/nixos/modules/services/networking/iodined.nix
index cd150fe63fd3..bc0fbb42c99d 100644
--- a/nixos/modules/services/networking/iodined.nix
+++ b/nixos/modules/services/networking/iodined.nix
@@ -1,8 +1,8 @@
 # NixOS module for iodine, ip over dns daemon
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.iodined;
diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix
index cd82a41ef7af..a3d5b71740f6 100644
--- a/nixos/modules/services/networking/ircd-hybrid/default.nix
+++ b/nixos/modules/services/networking/ircd-hybrid/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix
index 76dd66013ba7..d2045c9efc58 100644
--- a/nixos/modules/services/networking/kippo.nix
+++ b/nixos/modules/services/networking/kippo.nix
@@ -6,8 +6,8 @@
 #      iptables -t nat -A PREROUTING -i IN_IFACE -p tcp --dport 22 -j REDIRECT --to-port 2222'';
 #
 # Lastly: use this service at your own risk. I am working on a way to run this inside a VM.
-{ pkgs, config, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
   cfg = config.services.kippo;
 in
@@ -76,8 +76,9 @@ rec {
     users.extraUsers = singleton {
       name = "kippo";
       description = "kippo web server privilege separation user";
+      uid = 108; # why does config.ids.uids.kippo give an error?
     };
-    users.extraGroups = singleton { name = "kippo"; };
+    users.extraGroups = singleton { name = "kippo";gid=108; };
 
     systemd.services.kippo = with pkgs; {
       description = "Kippo Web Server";
diff --git a/nixos/modules/services/networking/minidlna.nix b/nixos/modules/services/networking/minidlna.nix
index 73fcb1eeea8e..a519857d6a0b 100644
--- a/nixos/modules/services/networking/minidlna.nix
+++ b/nixos/modules/services/networking/minidlna.nix
@@ -1,8 +1,8 @@
 # Module for MiniDLNA, a simple DLNA server.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
new file mode 100644
index 000000000000..4f91a4947479
--- /dev/null
+++ b/nixos/modules/services/networking/murmur.nix
@@ -0,0 +1,253 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.murmur;
+  configFile = pkgs.writeText "murmurd.ini" ''
+    database=/var/lib/murmur/murmur.sqlite
+    dbDriver=QSQLITE
+
+    autobanAttempts=${toString cfg.autobanAttempts}
+    autobanTimeframe=${toString cfg.autobanTimeframe}
+    autobanTime=${toString cfg.autobanTime}
+
+    logfile=/var/log/murmur/murmurd.log
+    pidfile=${cfg.pidfile}
+
+    welcome="${cfg.welcome}"
+    port=${toString cfg.port}
+
+    ${if cfg.hostName == "" then "" else "host="+cfg.hostName}
+    ${if cfg.password == "" then "" else "serverpassword="+cfg.password}
+
+    bandwidth=${toString cfg.bandwidth}
+    users=${toString cfg.users}
+
+    textmessagelength=${toString cfg.textMsgLength}
+    imagemessagelength=${toString cfg.imgMsgLength}
+    allowhtml=${if cfg.allowHtml then "true" else "false"}
+    logdays=${toString cfg.logDays}
+    bonjour=${if cfg.bonjour then "true" else "false"}
+    sendversion=${if cfg.sendVersion then "true" else "false"}
+
+    ${if cfg.registerName     == "" then "" else "registerName="+cfg.registerName}
+    ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword}
+    ${if cfg.registerUrl      == "" then "" else "registerUrl="+cfg.registerUrl}
+    ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname}
+
+    certrequired=${if cfg.clientCertRequired then "true" else "false"}
+    ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert}
+    ${if cfg.sslKey  == "" then "" else "sslKey="+cfg.sslKey}
+  '';
+in
+{
+  options = {
+    services.murmur = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, start the Murmur Service.";
+      };
+
+      autobanAttempts = mkOption {
+        type = types.int;
+        default = 10;
+        description = ''
+          Number of attempts a client is allowed to make in
+          <literal>autobanTimeframe</literal> seconds, before being
+          banned for <literal>autobanTime</literal>.
+        '';
+      };
+
+      autobanTimeframe = mkOption {
+        type = types.int;
+        default = 120;
+        description = ''
+          Timeframe in which a client can connect without being banned
+          for repeated attempts (in seconds).
+        '';
+      };
+
+      autobanTime = mkOption {
+        type = types.int;
+        default = 300;
+        description = "The amount of time an IP ban lasts (in seconds).";
+      };
+
+      pidfile = mkOption {
+        type = types.path;
+        default = "/tmp/murmurd.pid";
+        description = "Path to PID file for Murmur daemon.";
+      };
+
+      welcome = mkOption {
+        type = types.str;
+        default = "";
+        description = "Welcome message for connected clients.";
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 64738;
+        description = "Ports to bind to (UDP and TCP).";
+      };
+
+      hostName = mkOption {
+        type = types.str;
+        default = "";
+        description = "Host to bind to. Defaults binding on all addresses.";
+      };
+
+      password = mkOption {
+        type = types.str;
+        default = "";
+        description = "Required password to join server, if specified.";
+      };
+
+      bandwidth = mkOption {
+        type = types.int;
+        default = 72000;
+        description = ''
+          Maximum bandwidth (in bits per second) that clients may send
+          speech at.
+        '';
+      };
+
+      users = mkOption {
+        type = types.int;
+        default = 100;
+        description = "Maximum number of concurrent clients allowed.";
+      };
+
+      textMsgLength = mkOption {
+        type = types.int;
+        default = 5000;
+        description = "Max length of text messages. Set 0 for no limit.";
+      };
+
+      imgMsgLength = mkOption {
+        type = types.int;
+        default = 131072;
+        description = "Max length of image messages. Set 0 for no limit.";
+      };
+
+      allowHtml = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Allow HTML in client messages, comments, and channel
+          descriptions.
+        '';
+      };
+
+      logDays = mkOption {
+        type = types.int;
+        default = 31;
+        description = ''
+          How long to store RPC logs for in the database. Set 0 to
+          keep logs forever, or -1 to disable DB logging.
+        '';
+      };
+
+      bonjour = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Bonjour auto-discovery, which allows clients over
+          your LAN to automatically discover Murmur servers.
+        '';
+      };
+
+      sendVersion = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Send Murmur version in UDP response.";
+      };
+
+      registerName = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Public server registration name, and also the name of the
+          Root channel. Even if you don't publicly register your
+          server, you probably still want to set this.
+        '';
+      };
+
+      registerPassword = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Public server registry password, used authenticate your
+          server to the registry to prevent impersonation; required for
+          subsequent registry updates.
+        '';
+      };
+
+      registerUrl = mkOption {
+        type = types.str;
+        default = "";
+        description = "URL website for your server.";
+      };
+
+      registerHostname = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          DNS hostname where your server can be reached. This is only
+          needed if you want your server to be accessed by its
+          hostname and not IP - but the name *must* resolve on the
+          internet properly.
+        '';
+      };
+
+      clientCertRequired = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Require clients to authenticate via certificates.";
+      };
+
+      sslCert = mkOption {
+        type = types.str;
+        default = "";
+        description = "Path to your SSL certificate.";
+      };
+
+      sslKey = mkOption {
+        type = types.str;
+        default = "";
+        description = "Path to your SSL key.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.murmur = {
+      description     = "Murmur Service user";
+      home            = "/var/lib/murmur";
+      createHome      = true;
+      uid             = config.ids.uids.murmur;
+    };
+
+    systemd.services.murmur = {
+      description = "Murmur Chat Service";
+      wantedBy    = [ "multi-user.target" ];
+      after       = [ "network.target "];
+
+      serviceConfig = {
+        Type      = "forking";
+        PIDFile   = cfg.pidfile;
+        Restart   = "always";
+        User      = "murmur";
+        ExecStart = "${pkgs.murmur}/bin/murmurd -ini ${configFile}";
+        PermissionsStartOnly = true;
+      };
+
+      preStart = ''
+        mkdir -p /var/log/murmur
+        chown -R murmur /var/log/murmur
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index ce28f0188284..e8d9d00cc0a2 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -2,14 +2,16 @@
 # XXX: todo: support multiple upstream links
 # see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   cfg = config.networking.nat;
 
+  dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}";
+
 in
 
 {
@@ -27,14 +29,27 @@ in
         '';
     };
 
+    networking.nat.internalInterfaces = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "eth0" ];
+      description =
+        ''
+          The interfaces for which to perform NAT. Packets coming from
+          these interface and destined for the external interface will
+          be rewritten.
+        '';
+    };
+
     networking.nat.internalIPs = mkOption {
       type = types.listOf types.str;
-      example = [ "192.168.1.0/24" ] ;
+      default = [];
+      example = [ "192.168.1.0/24" ];
       description =
         ''
           The IP address ranges for which to perform NAT.  Packets
-          coming from these networks and destined for the external
-          interface will be rewritten.
+          coming from these addresses (on any interface) and destined
+          for the external interface will be rewritten.
         '';
     };
 
@@ -80,25 +95,37 @@ in
 
         preStart =
           ''
-            iptables -t nat -F POSTROUTING
-            iptables -t nat -X
-          ''
-          + (concatMapStrings (network:
-            ''
-            iptables -t nat -A POSTROUTING \
-              -s ${network} -o ${cfg.externalInterface} \
-              ${if cfg.externalIP == null
-                then "-j MASQUERADE"
-                else "-j SNAT --to-source ${cfg.externalIP}"}
-            ''
-          ) cfg.internalIPs) +
-          ''
+            iptables -w -t nat -F PREROUTING
+            iptables -w -t nat -F POSTROUTING
+            iptables -w -t nat -X
+
+            # We can't match on incoming interface in POSTROUTING, so
+            # mark packets coming from the external interfaces.
+            ${concatMapStrings (iface: ''
+              iptables -w -t nat -A PREROUTING \
+                -i '${iface}' -j MARK --set-mark 1
+            '') cfg.internalInterfaces}
+
+            # NAT the marked packets.
+            ${optionalString (cfg.internalInterfaces != []) ''
+              iptables -w -t nat -A POSTROUTING -m mark --mark 1 \
+                -o ${cfg.externalInterface} ${dest}
+            ''}
+
+            # NAT packets coming from the internal IPs.
+            ${concatMapStrings (range: ''
+              iptables -w -t nat -A POSTROUTING \
+                -s '${range}' -o ${cfg.externalInterface} ${dest}
+            '') cfg.internalIPs}
+
             echo 1 > /proc/sys/net/ipv4/ip_forward
           '';
 
         postStop =
           ''
-            iptables -t nat -F POSTROUTING
+            iptables -w -t nat -F PREROUTING
+            iptables -w -t nat -F POSTROUTING
+            iptables -w -t nat -X
           '';
       };
   };
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 40c20aae4ef0..54bdf19373d5 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 with pkgs;
 
 let
@@ -31,7 +31,7 @@ let
 
     [modem-manager]
     Identity=unix-group:networkmanager
-    Action=org.freedesktop.ModemManager.*
+    Action=org.freedesktop.ModemManager*
     ResultAny=yes
     ResultInactive=no
     ResultActive=yes
@@ -42,7 +42,7 @@ let
         subject.isInGroup("networkmanager")
         && subject.active
         && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
-            || action.id.indexOf("org.freedesktop.ModemManager.")  == 0
+            || action.id.indexOf("org.freedesktop.ModemManager")  == 0
         ))
           { return polkit.Result.YES; }
     });
@@ -89,7 +89,7 @@ in {
           to change network settings to this group.
         '';
       };
-  
+
       packages = mkOption {
         type = types.listOf types.path;
         default = [ ];
@@ -130,6 +130,8 @@ in {
       message = "You can not use networking.networkmanager with services.networking.wireless";
     }];
 
+    boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
+
     environment.etc = [
       { source = ipUpScript;
         target = "NetworkManager/dispatcher.d/01nixos-ip-up";
@@ -159,6 +161,7 @@ in {
         networkmanager_vpnc
         networkmanager_openconnect
         networkmanager_pptp
+        modemmanager
         ];
 
     users.extraGroups = singleton {
@@ -174,16 +177,13 @@ in {
     systemd.services."networkmanager-init" = {
       description = "NetworkManager initialisation";
       wantedBy = [ "network.target" ];
-      partOf = [ "NetworkManager.service" ];
       wants = [ "NetworkManager.service" ];
       before = [ "NetworkManager.service" ];
       script = ''
         mkdir -m 700 -p /etc/NetworkManager/system-connections
         mkdir -m 755 -p ${stateDirs}
       '';
-      serviceConfig = {
-        Type = "oneshot";
-      };
+      serviceConfig.Type = "oneshot";
     };
 
     # Turn off NixOS' network management
@@ -204,6 +204,7 @@ in {
         networkmanager_vpnc
         networkmanager_openconnect
         networkmanager_pptp
+        modemmanager
         ];
 
     services.udev.packages = cfg.packages;
diff --git a/nixos/modules/services/networking/ngircd.nix b/nixos/modules/services/networking/ngircd.nix
new file mode 100644
index 000000000000..49e5f3559803
--- /dev/null
+++ b/nixos/modules/services/networking/ngircd.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ngircd;
+
+  configFile = pkgs.stdenv.mkDerivation {
+    name = "ngircd.conf";
+
+    text = cfg.config;
+
+    preferLocalBuild = true;
+
+    buildCommand = ''
+      echo -n "$text" > $out
+      ${cfg.package}/sbin/ngircd --config $out --configtest
+    '';
+  };
+in {
+  options = {
+    services.ngircd = {
+      enable = mkEnableOption "the ngircd IRC server";
+
+      config = mkOption {
+        description = "The ngircd configuration (see ngircd.conf(5)).";
+
+        type = types.lines;
+      };
+
+      package = mkOption {
+        description = "The ngircd package.";
+
+        type = types.package;
+
+        default = pkgs.ngircd;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    #!!! TODO: Use ExecReload (see https://github.com/NixOS/nixpkgs/issues/1988)
+    systemd.services.ngircd = {
+      description = "The ngircd IRC server";
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig.ExecStart = "${cfg.package}/sbin/ngircd --config ${configFile} --nodaemon";
+
+      serviceConfig.User = "ngircd";
+    };
+
+    users.extraUsers.ngircd = {
+      uid = config.ids.uids.ngircd;
+      description = "ngircd user.";
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/notbit.nix b/nixos/modules/services/networking/notbit.nix
new file mode 100644
index 000000000000..b97435042395
--- /dev/null
+++ b/nixos/modules/services/networking/notbit.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.notbit;
+  varDir = "/var/lib/notbit";
+  
+  sendmail = pkgs.stdenv.mkDerivation {
+    name = "notbit-wrapper";
+    buildInputs = [ pkgs.makeWrapper ];
+    propagatedBuildInputs = [ pkgs.notbit ];
+    buildCommand = ''
+      mkdir -p $out/bin
+      makeWrapper ${pkgs.notbit}/bin/notbit-sendmail $out/bin/notbit-system-sendmail \
+        --set XDG_RUNTIME_DIR ${varDir}
+    '';
+  };
+in
+
+with lib;
+{
+
+  ### configuration
+
+  options = {
+
+    services.notbit = {
+
+      enable = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = ''
+          Enables the notbit daemon and provides a sendmail binary named `notbit-system-sendmail` for sending mail over the system instance of notbit. Users must be in the notbit group in order to send mail over the system notbit instance. Currently mail recipt is not supported.
+        '';
+      };
+
+      port = mkOption {
+        type = types.uniq types.int;
+        default = 8443;
+        description = "The port which the daemon listens for other bitmessage clients";
+      };
+
+      nice = mkOption {
+        type = types.uniq types.int;
+        default = 10;
+        description = "Set the nice level for the notbit daemon";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.notbit sendmail ];
+
+    systemd.services.notbit = {
+      description = "Notbit daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.notbit ];
+      environment = { XDG_RUNTIME_DIR = varDir; };
+
+      postStart = ''
+        [ ! -f "${varDir}/addr" ] && notbit-keygen > ${varDir}/addr
+        chmod 0640 ${varDir}/{addr,notbit/notbit-ipc.lock}
+        chmod 0750 ${varDir}/notbit/{,notbit-ipc}
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.notbit}/bin/notbit -d -p ${toString cfg.port}";
+        User = "notbit";
+        Group = "notbit";
+        UMask = "0077";
+        WorkingDirectory = varDir;
+        Nice = cfg.nice;
+      };
+    };
+
+    users.extraUsers.notbit = {
+      group = "notbit";
+      description = "Notbit daemon user";
+      home = varDir;
+      createHome = true;
+      uid = config.ids.uids.notbit;
+    };
+
+    users.extraGroups.notbit.gid = config.ids.gids.notbit;
+  };
+
+}
diff --git a/nixos/modules/services/networking/ntopng.nix b/nixos/modules/services/networking/ntopng.nix
index 156a6b32a6ab..ab86f1a5b2b4 100644
--- a/nixos/modules/services/networking/ntopng.nix
+++ b/nixos/modules/services/networking/ntopng.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix
index e5e164021d3a..2f638904406b 100644
--- a/nixos/modules/services/networking/ntpd.nix
+++ b/nixos/modules/services/networking/ntpd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -15,6 +15,11 @@ let
     # chroot to ${stateDir}, we have to specify it as /ntp.drift.
     driftfile /ntp.drift
 
+    restrict default kod nomodify notrap nopeer noquery
+    restrict -6 default kod nomodify notrap nopeer noquery
+    restrict 127.0.0.1
+    restrict -6 ::1
+
     ${toString (map (server: "server " + server + " iburst\n") config.services.ntp.servers)}
   '';
 
@@ -31,7 +36,7 @@ in
     services.ntp = {
 
       enable = mkOption {
-        default = true;
+        default = !config.boot.isContainer;
         description = ''
           Whether to synchronise your machine's time using the NTP
           protocol.
diff --git a/nixos/modules/services/networking/oidentd.nix b/nixos/modules/services/networking/oidentd.nix
index a2a555a8ad1b..923e7cd0986e 100644
--- a/nixos/modules/services/networking/oidentd.nix
+++ b/nixos/modules/services/networking/oidentd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/networking/openfire.nix b/nixos/modules/services/networking/openfire.nix
index b2efb5e9c123..c3b4ba90b4e7 100644
--- a/nixos/modules/services/networking/openfire.nix
+++ b/nixos/modules/services/networking/openfire.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 292d45f43475..e3998b1e5b28 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
index fb541bf101ae..ad0fb0af01cb 100644
--- a/nixos/modules/services/networking/prayer.nix
+++ b/nixos/modules/services/networking/prayer.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
index 89c40c531579..950112b2dabe 100644
--- a/nixos/modules/services/networking/privoxy.nix
+++ b/nixos/modules/services/networking/privoxy.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
index f3a4e457ec84..749e5dcebb61 100644
--- a/nixos/modules/services/networking/quassel.nix
+++ b/nixos/modules/services/networking/quassel.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   quassel = pkgs.kde4.quasselDaemon;
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index 8d586ce6e46b..08762c9c8372 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -1,8 +1,8 @@
 # Module for the IPv6 Router Advertisement Daemon.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix
index f797206ad5c7..4c1891816e3e 100644
--- a/nixos/modules/services/networking/rdnssd.nix
+++ b/nixos/modules/services/networking/rdnssd.nix
@@ -1,9 +1,9 @@
 # Module for rdnssd, a daemon that configures DNS servers in
 # /etc/resolv/conf from IPv6 RDNSS advertisements.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/networking/rpcbind.nix b/nixos/modules/services/networking/rpcbind.nix
index c966f85e260d..eef1e8e8cd88 100644
--- a/nixos/modules/services/networking/rpcbind.nix
+++ b/nixos/modules/services/networking/rpcbind.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix
index 8816ac0d2f83..83db0841b346 100644
--- a/nixos/modules/services/networking/sabnzbd.nix
+++ b/nixos/modules/services/networking/sabnzbd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
new file mode 100644
index 000000000000..b29db58af99b
--- /dev/null
+++ b/nixos/modules/services/networking/searx.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.searx;
+
+  configFile = cfg.configFile;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.searx = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the Searx server. See https://github.com/asciimoo/searx
+        ";
+      };
+
+      configFile = mkOption {
+        default = "";
+        description = "
+          The path of the Searx server configuration file. If no file
+          is specified, a default file is used (default config file has
+          debug mode enabled).
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.searx.enable {
+
+    users.extraUsers.searx =
+      { uid = config.ids.uids.searx;
+        description = "Searx user";
+        createHome = true;
+        home = "/var/lib/searx";
+      };
+
+    users.extraGroups.searx =
+      { gid = config.ids.gids.searx;
+      };
+
+    systemd.services.searx =
+      {
+        description = "Searx server, the meta search engine.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "searx";
+          ExecStart = "${pkgs.pythonPackages.searx}/bin/searx-run";
+        };
+      } // (optionalAttrs (configFile != "") {
+        environment.SEARX_SETTINGS_PATH = configFile;
+      });
+        
+
+    environment.systemPackages = [ pkgs.pythonPackages.searx ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/spiped.nix b/nixos/modules/services/networking/spiped.nix
new file mode 100644
index 000000000000..ec5908b182fb
--- /dev/null
+++ b/nixos/modules/services/networking/spiped.nix
@@ -0,0 +1,212 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.spiped;
+in
+{
+  options = {
+    services.spiped = mkOption {
+      type = types.attrsOf (types.submodule (
+        {
+          options = {
+            encrypt = mkOption {
+              type    = types.bool;
+              default = false;
+              description = ''
+                Take unencrypted connections from the
+                <literal>source</literal> socket and send encrypted
+                connections to the <literal>target</literal> socket.
+              '';
+            };
+
+            decrypt = mkOption {
+              type    = types.bool;
+              default = false;
+              description = ''
+                Take encrypted connections from the
+                <literal>source</literal> socket and send unencrypted
+                connections to the <literal>target</literal> socket.
+              '';
+            };
+
+            source = mkOption {
+              type    = types.str;
+              description = ''
+                Address on which spiped should listen for incoming
+                connections.  Must be in one of the following formats:
+                <literal>/absolute/path/to/unix/socket</literal>,
+                <literal>host.name:port</literal>,
+                <literal>[ip.v4.ad.dr]:port</literal> or
+                <literal>[ipv6::addr]:port</literal> - note that
+                hostnames are resolved when spiped is launched and are
+                not re-resolved later; thus if DNS entries change
+                spiped will continue to connect to the expired
+                address.
+              '';
+            };
+
+            target = mkOption {
+              type    = types.str;
+              description = "Address to which spiped should connect.";
+            };
+
+            keyfile = mkOption {
+              type    = types.path;
+              description = ''
+                Name of a file containing the spiped key. As the
+                daemon runs as the <literal>spiped</literal> user, the
+                key file must be somewhere owned by that user. By
+                default, we recommend putting the keys for any spipe
+                services in <literal>/var/lib/spiped</literal>.
+              '';
+            };
+
+            timeout = mkOption {
+              type = types.int;
+              default = 5;
+              description = ''
+                Timeout, in seconds, after which an attempt to connect to
+                the target or a protocol handshake will be aborted (and the
+                connection dropped) if not completed
+              '';
+            };
+
+            maxConns = mkOption {
+              type = types.int;
+              default = 100;
+              description = ''
+                Limit on the number of simultaneous connections allowed.
+              '';
+            };
+
+            waitForDNS = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Wait for DNS. Normally when <literal>spiped</literal> is
+                launched it resolves addresses and binds to its source
+                socket before the parent process returns; with this option
+                it will daemonize first and retry failed DNS lookups until
+                they succeed. This allows <literal>spiped</literal> to
+                launch even if DNS isn't set up yet, but at the expense of
+                losing the guarantee that once <literal>spiped</literal> has
+                finished launching it will be ready to create pipes.
+              '';
+            };
+
+            disableKeepalives = mkOption {
+              type = types.bool;
+              default = false;
+              description = "Disable transport layer keep-alives.";
+            };
+
+            weakHandshake = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Use fast/weak handshaking: This reduces the CPU time spent
+                in the initial connection setup, at the expense of losing
+                perfect forward secrecy.
+              '';
+            };
+
+            resolveRefresh = mkOption {
+              type = types.int;
+              default = 60;
+              description = ''
+                Resolution refresh time for the target socket, in seconds.
+              '';
+            };
+
+            disableReresolution = mkOption {
+              type = types.bool;
+              default = false;
+              description = "Disable target address re-resolution.";
+            };
+          };
+        }
+      ));
+
+      default = {};
+
+      example = literalExample ''
+        {
+          pipe1 =
+            { keyfile = "/var/lib/spiped/pipe1.key";
+              encrypt = true;
+              source  = "localhost:6000";
+              target  = "endpoint.example.com:7000";
+            };
+          pipe2 =
+            { keyfile = "/var/lib/spiped/pipe2.key";
+              decrypt = true;
+              source  = "0.0.0.0:7000";
+              target  = "localhost:3000";
+            };
+        }
+      '';
+
+      description = ''
+        Configuration for a secure pipe daemon. The daemon can be
+        started, stopped, or examined using
+        <literal>systemctl</literal>, under the name
+        <literal>spiped@foo</literal>.
+      '';
+    };
+  };
+
+  config = {
+    assertions = mapAttrsToList (name: c: {
+      assertion = (c.encrypt -> !c.decrypt) || (c.decrypt -> c.encrypt);
+      message   = "A pipe must either encrypt or decrypt";
+    }) cfg;
+
+    users.extraGroups.spiped.gid = config.ids.gids.spiped;
+    users.extraUsers.spiped = {
+      description = "Secure Pipe Service user";
+      group       = "spiped";
+      uid         = config.ids.uids.spiped;
+    };
+
+    systemd.services."spiped@" = {
+      description = "Secure pipe '%i'";
+      after       = [ "network.target" ];
+
+      serviceConfig = {
+        Restart   = "always";
+        User      = "spiped";
+        PermissionsStartOnly = true;
+      };
+
+      preStart  = ''
+        cd /var/lib/spiped
+        chmod -R 0660 *
+        chown -R spiped:spiped *
+      '';
+      scriptArgs = "%i";
+      script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`";
+    };
+
+    system.activationScripts.spiped = optionalString (cfg != {})
+      "mkdir -p /var/lib/spiped";
+
+    # Setup spiped config files
+    environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec"
+      { text = concatStringsSep " "
+          [ (if cfg.encrypt then "-e" else "-d")        # Mode
+            "-s ${cfg.source}"                          # Source
+            "-t ${cfg.target}"                          # Target
+            "-k ${cfg.keyfile}"                         # Keyfile
+            "-n ${toString cfg.maxConns}"               # Max number of conns
+            "-o ${toString cfg.timeout}"                # Timeout
+            (optionalString cfg.waitForDNS "-D")        # Wait for DNS
+            (optionalString cfg.weakHandshake "-f")     # No PFS
+            (optionalString cfg.disableKeepalives "-j") # Keepalives
+            (if cfg.disableReresolution then "-R"
+              else "-r ${toString cfg.resolveRefresh}")
+          ];
+      }) cfg;
+  };
+}
diff --git a/nixos/modules/services/networking/ssh/lshd.nix b/nixos/modules/services/networking/ssh/lshd.nix
index 04ef76f1e4d4..fca30a1fe49c 100644
--- a/nixos/modules/services/networking/ssh/lshd.nix
+++ b/nixos/modules/services/networking/ssh/lshd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 85b6ab1efecf..b66ccb87120c 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -258,7 +258,6 @@ in
         path = [ pkgs.openssh pkgs.gawk ];
 
         environment.LD_LIBRARY_PATH = nssModulesPath;
-        environment.LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
 
         preStart =
           ''
@@ -285,7 +284,7 @@ in
     networking.firewall.allowedTCPPorts = cfg.ports;
 
     security.pam.services.sshd =
-      { startSession = true;
+      { startSession = !config.boot.isContainer;
         showMotd = true;
         unixAuth = cfg.passwordAuthentication;
       };
diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix
index fa8b7556de5d..2cfb9fc9b923 100644
--- a/nixos/modules/services/networking/supybot.nix
+++ b/nixos/modules/services/networking/supybot.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
new file mode 100644
index 000000000000..02572c1e27d2
--- /dev/null
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -0,0 +1,78 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.syncthing;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.syncthing = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Syncthing, self-hosted open-source alternative
+          to Dropbox and BittorrentSync. Initial interface will be
+          available on http://127.0.0.1:8080/.
+        '';
+      };
+
+      user = mkOption {
+        default = "syncthing";
+        description = ''
+          Syncthing will be run under this user (user must exist,
+          this can be your user name).
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/lib/syncthing";
+        description = ''
+          Path where the `.syncthing` (settings and keys) and `Sync`
+          (your synced files) directories will exist. This can be your home
+          directory.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.syncthing =
+      {
+        description = "Syncthing service";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        environment.STNORESTART = "placeholder";  # do not self-restart
+        environment.HOME = "${cfg.dataDir}";
+        serviceConfig = {
+          User = "${cfg.user}";
+          PermissionsStartOnly = true;
+          Restart = "always";
+          ExecStart = "${pkgs.syncthing}/bin/syncthing -home=${cfg.dataDir}/.syncthing";
+        };
+        preStart = ''
+          mkdir -p ${cfg.dataDir}
+          chown ${cfg.user} ${cfg.dataDir}
+        '';
+
+      };
+
+    environment.systemPackages = [ pkgs.syncthing ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix
index 48cb884f2466..1359006aef4e 100644
--- a/nixos/modules/services/networking/tcpcrypt.nix
+++ b/nixos/modules/services/networking/tcpcrypt.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/tftpd.nix b/nixos/modules/services/networking/tftpd.nix
index 37935496c597..a2f7ff06ea61 100644
--- a/nixos/modules/services/networking/tftpd.nix
+++ b/nixos/modules/services/networking/tftpd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index fb75b4ed069a..30ce4b49fa8d 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 1c77cc6df4ea..62b6027c0120 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -91,6 +91,7 @@ let
       ${optionalString (pkgs.stdenv.system == "x86_64-linux") ''
         seccomp_sandbox=NO
       ''}
+      anon_umask=${cfg.anonymousUmask}
     '';
 
 in
@@ -139,6 +140,13 @@ in
         description = "RSA certificate file.";
       };
 
+      anonymousUmask = mkOption {
+        type = types.string;
+        default = "077";
+        example = "002";
+        description = "Anonymous write umask.";
+      };
+
     } // (listToAttrs (catAttrs "nixosOption" optionDescription));
 
   };
diff --git a/nixos/modules/services/networking/wakeonlan.nix b/nixos/modules/services/networking/wakeonlan.nix
index 1fc54986b16f..11bb7e925255 100644
--- a/nixos/modules/services/networking/wakeonlan.nix
+++ b/nixos/modules/services/networking/wakeonlan.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   interfaces = config.services.wakeonlan.interfaces;
diff --git a/nixos/modules/services/networking/websockify.nix b/nixos/modules/services/networking/websockify.nix
index 12042bbad6c9..4b76350ecf8a 100644
--- a/nixos/modules/services/networking/websockify.nix
+++ b/nixos/modules/services/networking/websockify.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.services.networking.websockify; in {
   options = {
diff --git a/nixos/modules/services/networking/wicd.nix b/nixos/modules/services/networking/wicd.nix
index 8e0122732164..18258084fc2c 100644
--- a/nixos/modules/services/networking/wicd.nix
+++ b/nixos/modules/services/networking/wicd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 5e5f81ed5a0b..eb721ec34559 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/networking/xinetd.nix b/nixos/modules/services/networking/xinetd.nix
index 626183b810f1..14ee52ae52e6 100644
--- a/nixos/modules/services/networking/xinetd.nix
+++ b/nixos/modules/services/networking/xinetd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 1be3587c3bb9..4b758608cb64 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -143,7 +143,9 @@ in
       };
 
     services.printing.drivers =
-      [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends pkgs.perl pkgs.coreutils pkgs.gnused ];
+      [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends
+        pkgs.perl pkgs.coreutils pkgs.gnused pkgs.bc pkgs.gawk pkgs.gnugrep
+	];
 
     services.printing.cupsdConf =
       ''
diff --git a/nixos/modules/services/scheduling/atd.nix b/nixos/modules/services/scheduling/atd.nix
index c516c5889f12..c6f128ec4026 100644
--- a/nixos/modules/services/scheduling/atd.nix
+++ b/nixos/modules/services/scheduling/atd.nix
@@ -1,6 +1,6 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/scheduling/cron.nix b/nixos/modules/services/scheduling/cron.nix
index 44ed1ba5a078..5da71b12dd76 100644
--- a/nixos/modules/services/scheduling/cron.nix
+++ b/nixos/modules/services/scheduling/cron.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
index fda29ca0482b..346a64f2c3cb 100644
--- a/nixos/modules/services/scheduling/fcron.nix
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index b3d934862abf..b7a3566f95d9 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -1,20 +1,26 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.elasticsearch;
 
-  es_home = "/var/lib/elasticsearch";
-
-  configFile = pkgs.writeText "elasticsearch.yml" ''
+  esConfig = ''
     network.host: ${cfg.host}
-    network.port: ${cfg.port}
-    network.tcp.port: ${cfg.tcp_port}
+    network.port: ${toString cfg.port}
+    network.tcp.port: ${toString cfg.tcp_port}
     cluster.name: ${cfg.cluster_name}
     ${cfg.extraConf}
   '';
 
+  configDir = pkgs.buildEnv {
+    name = "elasticsearch-config";
+    paths = [
+      (pkgs.writeTextDir "elasticsearch.yml" esConfig)
+      (pkgs.writeTextDir "logging.yml" cfg.logging)
+    ];
+  };
+
 in {
 
   ###### interface
@@ -34,14 +40,14 @@ in {
 
     port = mkOption {
       description = "Elasticsearch port to listen for HTTP traffic";
-      default = "9200";
-      type = types.str;
+      default = 9200;
+      type = types.int;
     };
 
     tcp_port = mkOption {
       description = "Elasticsearch port for the node to node communication";
-      default = "9300";
-      type = types.str;
+      default = 9300;
+      type = types.int;
     };
 
     cluster_name = mkOption {
@@ -79,27 +85,32 @@ in {
       '';
       type = types.str;
     };
+
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/lib/elasticsearch";
+      description = ''
+        Data directory for elasticsearch.
+      '';
+    };
   };
 
   ###### implementation
 
   config = mkIf cfg.enable {
-    environment.etc = [
-      { source = configFile;
-        target = "elasticsearch/elasticsearch.yml"; }
-      { source = pkgs.writeText "logging.yml" cfg.logging;
-        target = "elasticsearch/logging.yml"; }
-    ];
-
     systemd.services.elasticsearch = {
       description = "Elasticsearch daemon";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
-      environment = { ES_HOME = es_home; };
+      environment = { ES_HOME = cfg.dataDir; };
       serviceConfig = {
-        ExecStart = "${pkgs.elasticsearch}/bin/elasticsearch -f -Des.path.conf=/etc/elasticsearch";
+        ExecStart = "${pkgs.elasticsearch}/bin/elasticsearch -f -Des.path.conf=${configDir}";
         User = "elasticsearch";
       };
+      preStart = ''
+        mkdir -m 0700 -p ${cfg.dataDir}
+        if [ "$(id -u)" = 0 ]; then chown -R elasticsearch ${cfg.dataDir}; fi
+      '';
     };
 
     environment.systemPackages = [ pkgs.elasticsearch ];
@@ -108,8 +119,7 @@ in {
       name = "elasticsearch";
       uid = config.ids.uids.elasticsearch;
       description = "Elasticsearch daemon user";
-      home = es_home;
-      createHome = true;
+      home = cfg.dataDir;
     };
   };
 }
diff --git a/nixos/modules/services/search/solr.nix b/nixos/modules/services/search/solr.nix
new file mode 100644
index 000000000000..38d9dedbe3cf
--- /dev/null
+++ b/nixos/modules/services/search/solr.nix
@@ -0,0 +1,147 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.solr;
+
+  # Assemble all jars needed for solr
+  solrJars = pkgs.stdenv.mkDerivation {
+    name = "solr-jars";
+
+    src = pkgs.fetchurl {
+      url = http://archive.apache.org/dist/tomcat/tomcat-5/v5.5.36/bin/apache-tomcat-5.5.36.tar.gz;
+      sha256 = "01mzvh53wrs1p2ym765jwd00gl6kn8f9k3nhdrnhdqr8dhimfb2p";
+    };
+
+    buildPhases = [ "unpackPhase" "installPhase" ];
+
+    installPhase = ''
+      mkdir -p $out/lib
+      cp common/lib/*.jar $out/lib/
+      ln -s ${pkgs.ant}/lib/ant/lib/ant.jar $out/lib/
+      ln -s ${cfg.solrPackage}/lib/ext/* $out/lib/
+      ln -s ${pkgs.openjdk}/lib/openjdk/lib/tools.jar $out/lib/
+    '' + optionalString (cfg.extraJars != []) ''
+      for f in ${concatStringsSep " " cfg.extraJars}; do
+         cp $f $out/lib
+      done
+    '';
+  };
+
+in {
+
+  options = {
+    services.solr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables the solr service.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running solr.
+        '';
+      };
+
+      solrPackage = mkOption {
+        type = types.package;
+        default = pkgs.solr;
+        description = ''
+          Which solr derivation to use for running solr.
+        '';
+      };
+
+      extraJars = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          List of paths pointing to jars. Jars are copied to commonLibFolder to be available to java/solr.
+        '';
+      };
+
+      log4jConfiguration = mkOption {
+        type = types.lines;
+        default = ''
+          log4j.rootLogger=INFO, stdout
+          log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+          log4j.appender.stdout.Target=System.out
+          log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+          log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+        '';
+        description = ''
+          Contents of the <literal>log4j.properties</literal> used. By default,
+          everything is logged to stdout (picked up by systemd) with level INFO.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run the solr process and.
+          the working directories.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      solrHome = mkOption {
+        type = types.str;
+        description = ''
+          The solr home directory. It is your own responsibility to
+          make sure this directory contains a working solr configuration,
+          and is writeable by the the user running the solr service.
+          Failing to do so, the solr will not start properly.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          solr.
+        '';
+      };
+
+      extraWinstoneOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone, which is
+          the servlet container hosting solr.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.winstone.solr = {
+      serviceName = "solr";
+      inherit (cfg) user group javaPackage;
+      warFile = "${cfg.solrPackage}/lib/solr.war";
+      extraOptions = [
+        "--commonLibFolder=${solrJars}/lib"
+        "--useJasper"
+      ] ++ cfg.extraWinstoneOptions;
+      extraJavaOptions = [
+        "-Dsolr.solr.home=${cfg.solrHome}"
+        "-Dlog4j.configuration=file://${pkgs.writeText "log4j.properties" cfg.log4jConfiguration}"
+      ] ++ cfg.extraJavaOptions;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
index 5ccb4927fcb7..057891a60475 100644
--- a/nixos/modules/services/security/clamav.nix
+++ b/nixos/modules/services/security/clamav.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
   clamavUser = "clamav";
   stateDir = "/var/lib/clamav";
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index ae1fd22d23ee..af5450166379 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/security/fprot.nix b/nixos/modules/services/security/fprot.nix
index 9f1fc4ed6d8b..7270a9f98145 100644
--- a/nixos/modules/services/security/fprot.nix
+++ b/nixos/modules/services/security/fprot.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
   fprotUser = "fprot";
   stateDir = "/var/lib/fprot";
@@ -10,33 +10,31 @@ in {
 
     services.fprot = {
       updater = {
-	enable = mkOption {
-	  default = false;
-	  description = ''
-	    Whether to enable automatic F-Prot virus definitions database updates.
-	  '';
-	};
-
-	productData = mkOption {
-	  default = "${pkgs.fprot}/opt/f-prot/product.data";
-	  description = ''
-	    product.data file. Defaults to the one supplied with installation package.
-	  '';
-	};
-
-	frequency = mkOption {
-	  default = 30;
-	  description = ''
-	    Update virus definitions every X minutes.
-	  '';
-	};
-
-	licenseKeyfile = mkOption {
-	  default = "${pkgs.fprot}/opt/f-prot/license.key";
-	  description = ''
-	    License keyfile. Defaults to the one supplied with installation package.
-	  '';
-	};
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable automatic F-Prot virus definitions database updates.
+          '';
+        };
+
+        productData = mkOption {
+          description = ''
+            product.data file. Defaults to the one supplied with installation package.
+          '';
+        };
+
+        frequency = mkOption {
+          default = 30;
+          description = ''
+            Update virus definitions every X minutes.
+          '';
+        };
+
+        licenseKeyfile = mkOption {
+          description = ''
+            License keyfile. Defaults to the one supplied with installation package.
+          '';
+        };
 
       };
     };
@@ -45,6 +43,10 @@ in {
   ###### implementation
 
   config = mkIf cfg.updater.enable {
+
+    services.fprot.updater.productData = mkDefault "${pkgs.fprot}/opt/f-prot/product.data";
+    services.fprot.updater.licenseKeyfile = mkDefault "${pkgs.fprot}/opt/f-prot/license.key";
+
     environment.systemPackages = [ pkgs.fprot ];
     environment.etc = singleton {
       source = "${pkgs.fprot}/opt/f-prot/f-prot.conf";
@@ -67,22 +69,22 @@ in {
 
     jobs = {
       fprot_updater = {
-	name = "fprot-updater";
-	  task = true;
+        name = "fprot-updater";
+          task = true;
 
-	  # have to copy fpupdate executable because it insists on storing the virus database in the same dir
+          # have to copy fpupdate executable because it insists on storing the virus database in the same dir
           preStart = ''
             mkdir -m 0755 -p ${stateDir}
             chown ${fprotUser}:${fprotGroup} ${stateDir}
-	    cp ${pkgs.fprot}/opt/f-prot/fpupdate ${stateDir}
-	    ln -sf ${cfg.updater.productData} ${stateDir}/product.data
+            cp ${pkgs.fprot}/opt/f-prot/fpupdate ${stateDir}
+            ln -sf ${cfg.updater.productData} ${stateDir}/product.data
           '';
-	  #setuid = fprotUser;
-	  #setgid = fprotGroup;
+          #setuid = fprotUser;
+          #setgid = fprotGroup;
           exec = "/var/lib/fprot/fpupdate --keyfile ${cfg.updater.licenseKeyfile}";
-      }; 
+      };
     };
 
  };
 
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/services/security/haveged.nix b/nixos/modules/services/security/haveged.nix
index c3ea3fb03ed9..1d52ed55dbda 100644
--- a/nixos/modules/services/security/haveged.nix
+++ b/nixos/modules/services/security/haveged.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index e70eb8511a63..582dd124c290 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
index 1c158906a911..53f48a714b4b 100644
--- a/nixos/modules/services/security/torify.nix
+++ b/nixos/modules/services/security/torify.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
 
   cfg = config.services.tor;
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
index d6974282a6b5..ede6c983677a 100644
--- a/nixos/modules/services/security/torsocks.nix
+++ b/nixos/modules/services/security/torsocks.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
 
   cfg = config.services.tor;
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index cb5110f6feb3..8cc655803c59 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -1,8 +1,8 @@
 # D-Bus configuration and system bus daemon.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/system/nscd.nix b/nixos/modules/services/system/nscd.nix
index b817b1df779f..f357eb4e1fc8 100644
--- a/nixos/modules/services/system/nscd.nix
+++ b/nixos/modules/services/system/nscd.nix
@@ -1,10 +1,11 @@
-{pkgs, config, ...}:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   nssModulesPath = config.system.nssModules.path;
+  cfg = config.services.nscd;
 
   inherit (pkgs.lib) singleton;
 
@@ -24,6 +25,12 @@ in
         description = "Whether to enable the Name Service Cache Daemon.";
       };
 
+      config = mkOption {
+        type = types.lines;
+        default = builtins.readFile ./nscd.conf;
+        description = "Configuration to use for Name Service Cache Daemon.";
+      };
+
     };
 
   };
@@ -31,7 +38,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.nscd.enable {
+  config = mkIf cfg.enable {
 
     users.extraUsers = singleton
       { name = "nscd";
@@ -56,7 +63,7 @@ in
         restartTriggers = [ config.environment.etc.hosts.source ];
 
         serviceConfig =
-          { ExecStart = "@${pkgs.glibc}/sbin/nscd nscd -f ${./nscd.conf}";
+          { ExecStart = "@${pkgs.glibc}/sbin/nscd nscd -f ${pkgs.writeText "nscd.conf" cfg.config}";
             Type = "forking";
             PIDFile = "/run/nscd/nscd.pid";
             Restart = "always";
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index e0c212e5661f..00df4042d890 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.deluge;
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 68f9b0647c0e..5cdecd1eb577 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -1,13 +1,14 @@
-# NixOS module for Transmission BitTorrent daemon
+{ config, lib, pkgs, ... }:
 
-{ config, pkgs, ... }:
-
-with pkgs.lib;
+with lib;
 
 let
-
   cfg = config.services.transmission;
+  apparmor = config.security.apparmor.enable;
+
   homeDir = "/var/lib/transmission";
+  downloadDir = "${homeDir}/Downloads";
+  incompleteDir = "${homeDir}/.incomplete";
   settingsDir = "${homeDir}/.config/transmission-daemon";
   settingsFile = "${settingsDir}/settings.json";
 
@@ -31,16 +32,12 @@ let
         (if isList value then value else [value]))
         as));
 
+  # for users in group "transmission" to have access to torrents
+  fullSettings = cfg.settings // { umask = 2; };
 in
-
 {
-
-  ### configuration
-
   options = {
-
     services.transmission = {
-
       enable = mkOption {
         type = types.uniq types.bool;
         default = false;
@@ -59,65 +56,48 @@ in
         type = types.attrs;
         default =
           {
-            # for users in group "transmission" to have access to torrents
-            umask = 2;
-          }
-        ;
+            download-dir = downloadDir;
+            incomplete-dir = incompleteDir;
+            incomplete-dir-enabled = true;
+          };
         example =
           {
             download-dir = "/srv/torrents/";
             incomplete-dir = "/srv/torrents/.incomplete/";
             incomplete-dir-enabled = true;
             rpc-whitelist = "127.0.0.1,192.168.*.*";
-            # for users in group "transmission" to have access to torrents
-            umask = 2;
-          }
-        ;
+          };
         description = ''
           Attribute set whos fields overwrites fields in settings.json (each
           time the service starts). String values must be quoted, integer and
           boolean values must not.
 
-          See https://trac.transmissionbt.com/wiki/EditConfigFiles for documentation
-          and/or look at ${settingsFile}."
+          See https://trac.transmissionbt.com/wiki/EditConfigFiles for
+          documentation and/or look at ${settingsFile}.
         '';
       };
 
-      rpc_port = mkOption {
+      port = mkOption {
         type = types.uniq types.int;
         default = 9091;
         description = "TCP port number to run the RPC/web interface.";
       };
-
-      apparmor = mkOption {
-        type = types.uniq types.bool;
-        default = true;
-        description = "Generate apparmor profile for transmission-daemon.";
-      };
     };
-
   };
 
-  ### implementation
-
   config = mkIf cfg.enable {
-
     systemd.services.transmission = {
-      description = "Transmission BitTorrent Daemon";
-      after = [ "network.target" ] ++ optional (config.security.apparmor.enable && cfg.apparmor) "apparmor.service";
-      requires = mkIf (config.security.apparmor.enable && cfg.apparmor) [ "apparmor.service" ];
+      description = "Transmission BitTorrent Service";
+      after = [ "network.target" ] ++ optional apparmor "apparmor.service";
+      requires = mkIf apparmor [ "apparmor.service" ];
       wantedBy = [ "multi-user.target" ];
 
       # 1) Only the "transmission" user and group have access to torrents.
       # 2) Optionally update/force specific fields into the configuration file.
-      serviceConfig.ExecStartPre =
-        if cfg.settings != {} then ''
-          ${pkgs.stdenv.shell} -c "chmod 770 ${homeDir} && mkdir -p ${settingsDir} && ${pkgs.transmission}/bin/transmission-daemon -d |& sed ${attrsToSedArgs cfg.settings} > ${settingsFile}.tmp && mv ${settingsFile}.tmp ${settingsFile}"
-        ''
-        else ''
-          ${pkgs.stdenv.shell} -c "chmod 770 ${homeDir}"
-        '';
-      serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.rpc_port}";
+      serviceConfig.ExecStartPre = ''
+          ${pkgs.stdenv.shell} -c "chmod 770 ${homeDir} && mkdir -p ${settingsDir} ${downloadDir} ${incompleteDir} && ${pkgs.transmission}/bin/transmission-daemon -d |& sed ${attrsToSedArgs fullSettings} > ${settingsFile}.tmp && mv ${settingsFile}.tmp ${settingsFile}"
+      '';
+      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";
       # NOTE: transmission has an internal umask that also must be set (in settings.json)
@@ -127,6 +107,7 @@ in
     # It's useful to have transmission in path, e.g. for remote control
     environment.systemPackages = [ pkgs.transmission ];
 
+    users.extraGroups.transmission.gid = config.ids.gids.transmission;
     users.extraUsers.transmission = {
       group = "transmission";
       uid = config.ids.uids.transmission;
@@ -135,10 +116,8 @@ in
       createHome = true;
     };
 
-    users.extraGroups.transmission.gid = config.ids.gids.transmission;
-
     # AppArmor profile
-    security.apparmor.profiles = mkIf (config.security.apparmor.enable && cfg.apparmor) [
+    security.apparmor.profiles = mkIf apparmor [
       (pkgs.writeText "apparmor-transmission-daemon" ''
         #include <tunables/global>
 
@@ -161,9 +140,9 @@ in
 
           owner ${settingsDir}/** rw,
 
-          ${cfg.settings.download-dir}/** rw,
-          ${optionalString cfg.settings.incomplete-dir-enabled ''
-            ${cfg.settings.incomplete-dir}/** rw,
+          ${fullSettings.download-dir}/** rw,
+          ${optionalString fullSettings.incomplete-dir-enabled ''
+            ${fullSettings.incomplete-dir}/** rw,
           ''}
         }
       '')
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index ae4fa87d4b7b..df21ebbd9743 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
@@ -28,6 +28,17 @@ with pkgs.lib;
         '';
       };
 
+      serialSpeed = mkOption {
+        type = types.listOf types.int;
+        default = [ 115200 57600 38400 9600 ];
+        example = [ 38400 9600 ];
+        description = ''
+            Bitrates to allow for agetty's listening on serial ports. Listing more
+            bitrates gives more interoperability but at the cost of long delays
+            for getting a sync on the line.
+        '';
+      };
+
     };
 
   };
@@ -37,81 +48,23 @@ with pkgs.lib;
 
   config = {
 
-    # FIXME: these are mostly copy/pasted from the systemd sources,
-    # which some small modifications, which is annoying.
-
-    # Generate a separate job for each tty.
-    systemd.units."getty@.service".text =
-      ''
-        [Unit]
-        Description=Getty on %I
-        Documentation=man:agetty(8)
-        After=systemd-user-sessions.service plymouth-quit-wait.service
-
-        # If additional gettys are spawned during boot then we should make
-        # sure that this is synchronized before getty.target, even though
-        # getty.target didn't actually pull it in.
-        Before=getty.target
-        IgnoreOnIsolate=yes
-
-        ConditionPathExists=/dev/tty0
-
-        [Service]
-        Environment=TERM=linux
-        Environment=LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive
-        ExecStart=@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        TTYVTDisallocate=yes # set to no to prevent clearing the screen
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-
-        X-RestartIfChanged=false
-      '';
-    
-    systemd.units."serial-getty@.service".text =
-      ''
-        [Unit]
-        Description=Serial Getty on %I
-        Documentation=man:agetty(8) man:systemd-getty-generator(8)
-        BindsTo=dev-%i.device
-        After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
-
-        # If additional gettys are spawned during boot then we should make
-        # sure that this is synchronized before getty.target, even though
-        # getty.target didn't actually pull it in.
-        Before=getty.target
-        IgnoreOnIsolate=yes
-
-        [Service]
-        Environment=TERM=linux
-        Environment=LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive
-        ExecStart=@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I 115200,57600,38400,9600
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-        
-        X-RestartIfChanged=false
-      '';
+    systemd.services."getty@" =
+      { serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud %I 115200,38400,9600 $TERM";
+        restartIfChanged = false;
+      };
+
+    systemd.services."serial-getty@" =
+      { serviceConfig.ExecStart =
+          let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed);
+          in "@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I ${speeds} $TERM";
+        restartIfChanged = false;
+      };
+
+    systemd.services."container-getty@" =
+      { unitConfig.ConditionPathExists = "/dev/pts/%I"; # Work around being respawned when "machinectl login" exits.
+        serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud pts/%I 115200,38400,9600 $TERM";
+        restartIfChanged = false;
+      };
 
     environment.etc = singleton
       { # Friendly greeting on the virtual consoles.
diff --git a/nixos/modules/services/ttys/gpm.nix b/nixos/modules/services/ttys/gpm.nix
index 74cee67aeae9..03b0f39824d0 100644
--- a/nixos/modules/services/ttys/gpm.nix
+++ b/nixos/modules/services/ttys/gpm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -40,12 +40,15 @@ in
 
   config = mkIf cfg.enable {
 
-    jobs.gpm =
-      { description = "General purpose mouse";
+    systemd.services.gpm =
+      { description = "Console Mouse Daemon";
 
-        startOn = "started udev";
+        wantedBy = [ "multi-user.target" ];
+        requires = [ "getty.target" ];
 
-        exec = "${pkgs.gpm}/sbin/gpm -m /dev/input/mice -t ${cfg.protocol} -D &>/dev/null";
+        serviceConfig.ExecStart = "@${pkgs.gpm}/sbin/gpm gpm -m /dev/input/mice -t ${cfg.protocol}";
+        serviceConfig.Type = "forking";
+        serviceConfig.PIDFile = "/run/gpm.pid";
       };
 
   };
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index 302e660a7bff..70555e5d8825 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -44,6 +44,7 @@ in {
       After=systemd-user-sessions.service
       After=plymouth-quit-wait.service
       After=systemd-logind.service
+      After=systemd-vconsole-setup.service
       Requires=systemd-logind.service
       Before=getty.target
       Conflicts=getty@%i.service
@@ -62,17 +63,19 @@ in {
       X-RestartIfChanged=false
     '';
 
-    systemd.units."autovt@.service".linkTarget = "${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service";
-
-    systemd.services."systemd-vconsole-setup".restartIfChanged = false;
+    systemd.units."autovt@.service".unit = pkgs.runCommand "unit" { }
+        ''
+          mkdir -p $out
+          ln -s ${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service $out/autovt@.service
+        '';
 
-    systemd.units."kmsconvt@tty1.service".extraConfig.wait-for-vconsole-setup = "After=systemd-vconsole-setup.service";
+    systemd.services.systemd-vconsole-setup.restartIfChanged = false;
 
     services.kmscon.extraConfig = mkIf cfg.hwRender ''
       drm
       hwaccel
     '';
 
-    services.mesa.enable = mkIf cfg.hwRender true;
+    hardware.opengl.enable = mkIf cfg.hwRender true;
   };
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index cdb42fa7308a..75ec6671d156 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -63,8 +63,9 @@ let
           enablePHP = false;
           phpOptions = "";
           options = {};
+          documentRoot = null;
         };
-        res = defaults // svcFunction { inherit config pkgs serverInfo php; };
+        res = defaults // svcFunction { inherit config lib pkgs serverInfo php; };
       in res;
     in map f defs;
 
@@ -188,7 +189,11 @@ let
 
     subservices = callSubservices serverInfo cfg.extraSubservices;
 
-    documentRoot = if cfg.documentRoot != null then cfg.documentRoot else
+    maybeDocumentRoot = fold (svc: acc:
+      if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
+    ) null ([ cfg ] ++ subservices);
+
+    documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
       pkgs.runCommand "empty" {} "ensureDir $out";
 
     documentRootConf = ''
@@ -240,7 +245,7 @@ let
 
     ${robotsConf}
 
-    ${if isMainServer || cfg.documentRoot != null then documentRootConf else ""}
+    ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
 
     ${if cfg.enableUserDir then ''
 
@@ -260,7 +265,7 @@ let
 
     '' else ""}
 
-    ${if cfg.globalRedirect != null then ''
+    ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
       RedirectPermanent / ${cfg.globalRedirect}
     '' else ""}
 
@@ -414,7 +419,7 @@ in
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pkgs.apacheHttpd.override { mpm = mainCfg.multiProcessingModule; };
         example = "pkgs.apacheHttpd_2_4";
         description = ''
@@ -445,7 +450,7 @@ in
       extraModules = mkOption {
         type = types.listOf types.unspecified;
         default = [];
-        example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${php}/modules/libphp5.so"; } ]'';
+        example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
         description = ''
           Additional Apache modules to be used.  These can be
           specified as a string in the case of modules distributed
@@ -505,7 +510,7 @@ in
       virtualHosts = mkOption {
         type = types.listOf (types.submodule (
           { options = import ./per-server-options.nix {
-              inherit pkgs;
+              inherit lib;
               forMainServer = false;
             };
           }));
@@ -572,7 +577,7 @@ in
 
     # Include the options shared between the main server and virtual hosts.
     // (import ./per-server-options.nix {
-      inherit pkgs;
+      inherit lib;
       forMainServer = true;
     });
 
@@ -582,6 +587,12 @@ in
   ###### implementation
 
   config = mkIf config.services.httpd.enable {
+  
+    assertions = [ { assertion = mainCfg.enableSSL == true
+                               -> mainCfg.sslServerCert != null
+                                    && mainCfg.sslServerKey != null;
+                     message = "SSL is enabled for HTTPD, but sslServerCert and/or sslServerKey haven't been specified."; }
+                 ];
 
     users.extraUsers = optionalAttrs (mainCfg.user == "wwwrun") singleton
       { name = "wwwrun";
@@ -610,7 +621,7 @@ in
       { description = "Apache HTTPD";
 
         wantedBy = [ "multi-user.target" ];
-        requires = [ "keys.target" ];
+        wants = [ "keys.target" ];
         after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
 
         path =
@@ -622,8 +633,8 @@ in
           ++ concatMap (svc: svc.extraServerPath) allSubservices;
 
         environment =
-          { PHPRC = if enablePHP then phpIni else "";
-          } // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+          optionalAttrs enablePHP { PHPRC = phpIni; }
+          // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
 
         preStart =
           ''
diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
index 423087991e13..7d59c13b9575 100644
--- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, serverInfo, php, ... }:
+{ config, lib, pkgs, serverInfo, php, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -93,6 +93,10 @@ let
         ensureDir $out
         cp -r * $out
         cp ${mediawikiConfig} $out/LocalSettings.php
+        sed -i 's|/bin/bash|${pkgs.stdenv.shell}|' \
+          $out/maintenance/fuzz-tester.php \
+          $out/bin/ulimit.sh \
+          $out/includes/GlobalFunctions.php
       '';
   };
 
@@ -122,7 +126,18 @@ in
         </Directory>
       ''}
 
-      Alias ${config.urlPrefix} ${mediawikiRoot}
+      ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
+        RewriteEngine On
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
+        RewriteRule ${if config.enableUploads
+          then "!^/images"
+          else "^.*\$"
+        } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
+          then ""
+          else "${config.articleUrlPrefix}/"
+        }index.php [L]
+      ''}
 
       <Directory ${mediawikiRoot}>
           Order allow,deny
@@ -135,6 +150,8 @@ in
       ''}
     '';
 
+  documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
+
   enablePHP = true;
 
   options = {
@@ -290,6 +307,7 @@ in
             echo COMMIT
           ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
       fi
+      ${php}/bin/php ${mediawikiRoot}/maintenance/update.php
     '');
 
   robotsEntries = optionalString (config.articleUrlPrefix != "")
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 53f34e28c27e..b8e863345398 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
@@ -3,9 +3,9 @@
 # has additional options that affect the web server as a whole, like
 # the user/group to run under.)
 
-{ forMainServer, pkgs }:
+{ forMainServer, lib }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix
index dc82fd34f2fa..ad791d7d9582 100644
--- a/nixos/modules/services/web-servers/apache-httpd/trac.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/trac.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, serverInfo, ... }:
+{ config, lib, pkgs, serverInfo, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
index a6e6042fdf6d..cab16593bcbc 100644
--- a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
@@ -1,4 +1,6 @@
-{ config, pkgs, serverInfo, ... }:
+{ config, lib, pkgs, serverInfo, ... }:
+
+with lib;
 
 let
 
@@ -51,7 +53,7 @@ in
 
   options = {
 
-    urlPrefix = pkgs.lib.mkOption {
+    urlPrefix = mkOption {
       default = "/zabbix";
       description = "
         The URL prefix under which the Zabbix service appears.
@@ -59,9 +61,9 @@ in
       ";
     };
 
-    configFile = pkgs.lib.mkOption {
+    configFile = mkOption {
       default = null;
-      type = with pkgs.lib.types; nullOr path;
+      type = types.nullOr types.path;
       description = ''
         The configuration file (zabbix.conf.php) which contains the database
         connection settings. If not set, the configuration settings will created
@@ -69,7 +71,7 @@ in
       '';
     };
 
-    stateDir = pkgs.lib.mkOption {
+    stateDir = mkOption {
       default = "/var/lib/zabbix/frontend";
       description = "
         Directory where the dynamically generated configuration data
diff --git a/nixos/modules/services/web-servers/jboss/default.nix b/nixos/modules/services/web-servers/jboss/default.nix
index e1bcede6563c..8a292ad67917 100644
--- a/nixos/modules/services/web-servers/jboss/default.nix
+++ b/nixos/modules/services/web-servers/jboss/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixos/modules/services/web-servers/lighttpd/cgit.nix
index 62264f1db452..dbff565bd8a3 100644
--- a/nixos/modules/services/web-servers/lighttpd/cgit.nix
+++ b/nixos/modules/services/web-servers/lighttpd/cgit.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.lighttpd.cgit;
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index 4cc34c65d843..3ba934c72bf8 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -1,8 +1,8 @@
 # NixOS module for lighttpd web server
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
index f02bd4db2645..d49278be09a8 100644
--- a/nixos/modules/services/web-servers/lighttpd/gitweb.nix
+++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.lighttpd.gitweb;
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 4a1b6de2873f..ff94ee42d28d 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.nginx;
@@ -9,6 +9,12 @@ let
     user ${cfg.user} ${cfg.group};
     daemon off;
     ${cfg.config}
+    ${optionalString (cfg.httpConfig != "") ''
+    http {
+      ${cfg.httpConfig}
+    }
+    ''}
+    ${cfg.appendConfig}
   '';
 in
 
@@ -24,6 +30,7 @@ in
 
       package = mkOption {
         default = pkgs.nginx;
+        type = types.package;
         description = "
           Nginx package to use.
         ";
@@ -36,6 +43,25 @@ in
         ";
       };
 
+      appendConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration lines appended to the generated Nginx
+          configuration file. Commonly used by different modules
+          providing http snippets. <option>appendConfig</option>
+          can be specified more than once and it's value will be
+          concatenated (contrary to <option>config</option> which
+          can be set only once).
+        '';
+      };
+
+      httpConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Configuration lines to be appended inside of the http {} block.";
+      };
+
       stateDir = mkOption {
         default = "/var/spool/nginx";
         description = "
diff --git a/nixos/modules/services/web-servers/phpfpm.nix b/nixos/modules/services/web-servers/phpfpm.nix
new file mode 100644
index 000000000000..4a14f9b41a42
--- /dev/null
+++ b/nixos/modules/services/web-servers/phpfpm.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.phpfpm;
+
+  stateDir = "/run/phpfpm";
+
+  pidFile = "${stateDir}/phpfpm.pid";
+
+  cfgFile = pkgs.writeText "phpfpm.conf" ''
+    [global]
+    pid = ${pidFile}
+    error_log = syslog
+    daemonize = yes
+    ${cfg.extraConfig}
+
+    ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
+  '';
+
+in {
+
+  options = {
+    services.phpfpm = {
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration that should be put in the global section of
+          the PHP FPM configuration file. Do not specify the options
+          <literal>pid</literal>, <literal>error_log</literal> or
+          <literal>daemonize</literal> here, since they are generated by
+          NixOS.
+        '';
+      };
+
+      phpPackage = mkOption {
+        default = pkgs.php54;
+        description = ''
+          The PHP package to use for running the FPM service.
+        '';
+      };
+
+      poolConfigs = mkOption {
+        type = types.attrsOf types.lines;
+        default = {};
+        example = {
+          mypool = ''
+            listen = /run/phpfpm/mypool
+            user = nobody
+            pm = dynamic
+            pm.max_children = 75
+            pm.start_servers = 10
+            pm.min_spare_servers = 5
+            pm.max_spare_servers = 20
+            pm.max_requests = 500
+          '';
+        };
+        description = ''
+          A mapping between PHP FPM pool names and their configurations.
+          See the documentation on <literal>php-fpm.conf</literal> for
+          details on configuration directives. If no pools are defined,
+          the phpfpm service is disabled.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.poolConfigs != {}) {
+
+    systemd.services.phpfpm = {
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p "${stateDir}"
+      '';
+      serviceConfig = {
+        ExecStart = "${cfg.phpPackage}/sbin/php-fpm -y ${cfgFile}";
+        PIDFile = pidFile;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index a68828de5d8e..b5eee8f8be8f 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index 7e327120c3d1..364f6c68faca 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -1,9 +1,9 @@
-{ config, pkgs, ...}:
+{ config, lib, pkgs, ...}:
 let
   cfg = config.services.varnish;
 
 in
-with pkgs.lib;
+with lib;
 {
   options = {
     services.varnish = {
diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix
new file mode 100644
index 000000000000..7f48012f158e
--- /dev/null
+++ b/nixos/modules/services/web-servers/winstone.nix
@@ -0,0 +1,129 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.winstone;
+
+  winstoneOpts = { name, ... }: {
+    options = {
+      name = mkOption {
+        default = name;
+        internal = true;
+      };
+
+      serviceName = mkOption {
+        type = types.str;
+        description = ''
+          The name of the systemd service. By default, it is
+          derived from the winstone instance name.
+        '';
+      };
+
+      warFile = mkOption {
+        type = types.str;
+        description = ''
+          The WAR file that Winstone should serve.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running Winstone.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run this Winstone process and
+          own the working directory.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      workDir = mkOption {
+        type = types.str;
+        description = ''
+          The working directory for this Winstone instance. Will
+          contain extracted webapps etc. The directory will be
+          created if it doesn't exist.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          Winstone.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone process.
+        '';
+      };
+    };
+
+    config = {
+      workDir = mkDefault "/run/winstone/${name}";
+      serviceName = mkDefault "winstone-${name}";
+    };
+  };
+
+  mkService = cfg: let
+    opts = concatStringsSep " " (cfg.extraOptions ++ [
+      "--warfile ${cfg.warFile}"
+    ]);
+
+    javaOpts = concatStringsSep " " (cfg.extraJavaOptions ++ [
+      "-Djava.io.tmpdir=${cfg.workDir}"
+      "-jar ${pkgs.winstone}/lib/winstone.jar"
+    ]);
+  in {
+    wantedBy = [ "multi-user.target" ];
+    description = "winstone service for ${cfg.name}";
+    preStart = ''
+      mkdir -p "${cfg.workDir}"
+      chown ${cfg.user}:${cfg.group} "${cfg.workDir}"
+    '';
+    serviceConfig = {
+      ExecStart = "${cfg.javaPackage}/bin/java ${javaOpts} ${opts}";
+      User = cfg.user;
+      PermissionsStartOnly = true;
+    };
+  };
+
+in {
+
+  options = {
+    services.winstone = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ winstoneOpts ];
+      description = ''
+        Defines independent Winstone services, each serving one WAR-file.
+      '';
+    };
+  };
+
+  config = mkIf (cfg != {}) {
+
+    systemd.services = mapAttrs' (n: c: nameValuePair c.serviceName (mkService c)) cfg;
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index 576f4b08fb90..21117118457d 100644
--- a/nixos/modules/services/web-servers/zope2.nix
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -1,6 +1,6 @@
-{ pkgs, config, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 035b23b4e1bb..b82398ccf9dd 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -1,15 +1,15 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   xcfg = config.services.xserver;
   cfg = xcfg.desktopManager;
 
-  # Whether desktop manager `d' is capable of setting a background.
-  # If it isn't, the `feh' program is used as a fallback.
-  needBGCond = d: ! (d ? bgSupport && d.bgSupport);
+  # If desktop manager `d' isn't capable of setting a background and
+  # the xserver is enabled, the `feh' program is used as a fallback.
+  needBGCond = d: ! (d ? bgSupport && d.bgSupport) && xcfg.enable;
 
 in
 
@@ -17,7 +17,7 @@ in
   # Note: the order in which desktop manager modules are imported here
   # determines the default: later modules (if enabled) are preferred.
   # E.g., if KDE is enabled, it supersedes xterm.
-  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ];
+  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ./gnome3.nix ./xbmc.nix ];
 
   options = {
 
diff --git a/nixos/modules/services/x11/desktop-managers/e17.nix b/nixos/modules/services/x11/desktop-managers/e17.nix
index 3d91617c62aa..4cac53c9c75b 100644
--- a/nixos/modules/services/x11/desktop-managers/e17.nix
+++ b/nixos/modules/services/x11/desktop-managers/e17.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
new file mode 100644
index 000000000000..4620bc6722af
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -0,0 +1,129 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.desktopManager.gnome3;
+  gnome3 = pkgs.gnome3;
+
+  # Remove packages of ys from xs, based on their names
+  removePackagesByName = xs: ys:
+    let
+      pkgName = drv: (builtins.parseDrvName drv.name).name;
+	  ysNames = map pkgName ys;
+      res = (filter (x: !(builtins.elem (pkgName x) ysNames)) xs);
+    in
+      filter (x: !(builtins.elem (pkgName x) ysNames)) xs;
+
+in {
+
+  options = {
+
+    services.xserver.desktopManager.gnome3.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable Gnome 3 desktop manager.";
+    };
+
+    environment.gnome3.excludePackages = mkOption {
+      default = [];
+      example = "[ pkgs.gnome3.totem ]";
+      type = types.listOf types.package;
+      description = "Which packages gnome should exclude from the default environment";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # Enable helpful DBus services.
+    security.polkit.enable = true;
+    services.udisks2.enable = true;
+    services.accounts-daemon.enable = true;
+    services.gnome3.at-spi2-core.enable = true;
+    services.gnome3.evolution-data-server.enable = true;
+    services.gnome3.gnome-keyring.enable = true;
+    services.gnome3.gnome-online-accounts.enable = mkDefault true;
+    services.gnome3.gnome-user-share.enable = mkDefault true;
+    services.gnome3.sushi.enable = mkDefault true;
+    services.gnome3.tracker.enable = mkDefault true;
+    hardware.pulseaudio.enable = mkDefault true;
+    services.telepathy.enable = mkDefault true;
+    networking.networkmanager.enable = true;
+    services.upower.enable = config.powerManagement.enable;
+
+    fonts.extraFonts = [ pkgs.dejavu_fonts ];
+
+    services.xserver.desktopManager.session = singleton
+      { name = "gnome3";
+        start = ''
+          # Set GTK_DATA_PREFIX so that GTK+ can find the themes
+          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=gnome
+
+          # Don't let epiphany depend upon gnome-shell
+          export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${pkgs.gnome3.gnome_shell}/share/gsettings-schemas/${pkgs.gnome3.gnome_shell.name}
+
+          # Let gnome-control-center find gnome-shell search providers
+          export GNOME_SEARCH_PROVIDERS_DIR=${config.system.path}/share/gnome-shell/search-providers/
+
+          # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+          ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update
+
+          ${gnome3.gnome_session}/bin/gnome-session&
+          waitPID=$!
+        '';
+      };
+
+    environment.variables.GIO_EXTRA_MODULES = [ "${gnome3.dconf}/lib/gio/modules"
+                                                "${pkgs.glib_networking}/lib/gio/modules" ];
+    environment.systemPackages =
+      [ pkgs.desktop_file_utils
+        pkgs.glib_networking
+        pkgs.gtk3 # for gtk-update-icon-cache
+        pkgs.ibus
+        pkgs.shared_mime_info # for update-mime-database
+        gnome3.dconf
+        gnome3.gnome-backgrounds
+        gnome3.gnome_control_center
+        gnome3.gnome_icon_theme
+        gnome3.gnome-menus
+        gnome3.gnome_settings_daemon
+        gnome3.gnome_shell
+        gnome3.gnome_themes_standard
+      ] ++ (removePackagesByName [
+        gnome3.baobab
+        gnome3.empathy
+        gnome3.eog
+        gnome3.epiphany
+        gnome3.evince
+        gnome3.gucharmap
+        gnome3.nautilus
+        gnome3.totem
+        gnome3.vino
+        gnome3.yelp
+        gnome3.gnome-calculator
+        gnome3.gnome-contacts
+        gnome3.gnome-font-viewer
+        gnome3.gnome-screenshot
+        gnome3.gnome-shell-extensions
+        gnome3.gnome-system-log
+        gnome3.gnome-system-monitor
+        gnome3.gnome_terminal
+        gnome3.gnome-user-docs
+
+        gnome3.file-roller
+        gnome3.gnome-tweak-tool
+      ] config.environment.gnome3.excludePackages);
+
+    # Needed for themes and backgrounds
+    environment.pathsToLink = [ "/share" ];
+
+  };
+
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix
index 108b52bb951d..26b0612671c4 100644
--- a/nixos/modules/services/x11/desktop-managers/kde4.nix
+++ b/nixos/modules/services/x11/desktop-managers/kde4.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -159,7 +159,7 @@ in
 
     # Enable helpful DBus services.
     services.udisks.enable = ! wantsUdisks2;
-    services.udisks2.enable = wantsUdisks2;
+    services.udisks2.enable = true;
     services.upower.enable = config.powerManagement.enable;
 
     security.pam.services.kde = { allowNullPassword = true; };
diff --git a/nixos/modules/services/x11/desktop-managers/xbmc.nix b/nixos/modules/services/x11/desktop-managers/xbmc.nix
new file mode 100644
index 000000000000..97e966ca0197
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/xbmc.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.xserver.desktopManager.xbmc;
+in
+
+{
+  options = {
+    services.xserver.desktopManager.xbmc = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the xbmc multimedia center.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.desktopManager.session = [{
+      name = "xbmc";
+      start = ''
+        ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
+        waitPID=$!
+      '';
+    }];
+    
+    environment.systemPackages = [ pkgs.xbmc ];
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 5e5fab3ed2bb..a72eea76239f 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -29,7 +29,7 @@ in
         start =
           ''
             # Set GTK_PATH so that GTK+ can find the theme engines.
-            export GTK_PATH=${config.system.path}/lib/gtk-2.0
+            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}
@@ -81,7 +81,7 @@ in
     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";
+    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
 
     # Enable helpful DBus services.
     services.udisks2.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/xterm.nix b/nixos/modules/services/x11/desktop-managers/xterm.nix
index edc61c103ea9..eab914071410 100644
--- a/nixos/modules/services/x11/desktop-managers/xterm.nix
+++ b/nixos/modules/services/x11/desktop-managers/xterm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -19,7 +19,7 @@ in
 
   };
 
-  config = mkIf cfg.enable {
+  config = mkIf (config.services.xserver.enable && cfg.enable) {
 
     services.xserver.desktopManager.session = singleton
       { name = "xterm";
diff --git a/nixos/modules/services/x11/display-managers/auto.nix b/nixos/modules/services/x11/display-managers/auto.nix
index 33d97e0e07a9..c02ccdf12b65 100644
--- a/nixos/modules/services/x11/display-managers/auto.nix
+++ b/nixos/modules/services/x11/display-managers/auto.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 575386bac0aa..3bf18bd58c84 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -7,9 +7,9 @@
 # (e.g., KDE, Gnome or a plain xterm), and optionally the *window
 # manager* (e.g. kwin or twm).
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -51,17 +51,6 @@ let
 
       ''}
 
-      ${optionalString cfg.startOpenSSHAgent ''
-        if test -z "$SSH_AUTH_SOCK"; then
-            # Restart this script as a child of the SSH agent.  (It is
-            # also possible to start the agent as a child that prints
-            # the required environment variabled on stdout, but in
-            # that mode ssh-agent is not terminated when we log out.)
-            export SSH_ASKPASS=${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass
-            exec ${pkgs.openssh}/bin/ssh-agent "$0" "$sessionType"
-        fi
-      ''}
-
       ${optionalString cfg.startGnuPGAgent ''
         if test -z "$SSH_AUTH_SOCK"; then
             # Restart this script as a child of the GnuPG agent.
diff --git a/nixos/modules/services/x11/display-managers/kdm.nix b/nixos/modules/services/x11/display-managers/kdm.nix
index c51e7edfddf3..7ec489ae3e73 100644
--- a/nixos/modules/services/x11/display-managers/kdm.nix
+++ b/nixos/modules/services/x11/display-managers/kdm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index e4125891e6cb..d459c59b0483 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -55,8 +55,8 @@ let
     ''
       [LightDM]
       greeter-user = ${config.users.extraUsers.lightdm.name}
-      xgreeters-directory = ${cfg.greeter.package}
-      xsessions-directory = ${dmcfg.session.desktops}
+      greeters-directory = ${cfg.greeter.package}
+      sessions-directory = ${dmcfg.session.desktops}
 
       [SeatDefaults]
       xserver-command = ${xserverWrapper}
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
index 35834ef3764b..114d34557a07 100644
--- a/nixos/modules/services/x11/display-managers/slim.nix
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -1,10 +1,11 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
   dmcfg = config.services.xserver.displayManager;
+
   cfg = dmcfg.slim;
 
   slimConfig = pkgs.writeText "slim.cfg"
@@ -109,6 +110,12 @@ in
         execCmd = "exec ${pkgs.slim}/bin/slim";
       };
 
+    services.xserver.displayManager.sessionCommands =
+      ''
+        # Export the config/themes for slimlock.
+        export SLIM_THEMESDIR=${slimThemesDir}
+      '';
+
     # Allow null passwords so that the user can login as root on the
     # installation CD.
     security.pam.services.slim = { allowNullPassword = true; startSession = true; };
diff --git a/nixos/modules/services/x11/hardware/multitouch.nix b/nixos/modules/services/x11/hardware/multitouch.nix
index 4f9048bfd910..6e6e88e67218 100644
--- a/nixos/modules/services/x11/hardware/multitouch.nix
+++ b/nixos/modules/services/x11/hardware/multitouch.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let cfg = config.services.xserver.multitouch; in
 
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index 91e01f2e30b1..f2227a34a20c 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -1,10 +1,24 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
-
-let cfg = config.services.xserver.synaptics; in
-
-{
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xserver.synaptics;
+    tapConfig = if cfg.tapButtons then enabledTapConfig else disabledTapConfig;
+    enabledTapConfig = ''
+      Option "MaxTapTime" "180"
+      Option "MaxTapMove" "220"
+      Option "TapButton1" "${builtins.elemAt cfg.buttonsMap 0}"
+      Option "TapButton2" "${builtins.elemAt cfg.buttonsMap 1}"
+      Option "TapButton3" "${builtins.elemAt cfg.buttonsMap 2}"
+    '';
+    disabledTapConfig = ''
+      Option "MaxTapTime" "0"
+      Option "MaxTapMove" "0"
+      Option "TapButton1" "0"
+      Option "TapButton2" "0"
+      Option "TapButton3" "0"
+    '';
+in {
 
   options = {
 
@@ -106,15 +120,10 @@ let cfg = config.services.xserver.synaptics; in
           MatchIsTouchpad "on"
           ${optionalString (cfg.dev != null) ''MatchDevicePath "${cfg.dev}"''}
           Driver "synaptics"
-          Option "MaxTapTime" "180"
-          Option "MaxTapMove" "220"
           Option "MinSpeed" "${cfg.minSpeed}"
           Option "MaxSpeed" "${cfg.maxSpeed}"
           Option "AccelFactor" "${cfg.accelFactor}"
-          ${if cfg.tapButtons then "" else ''Option "MaxTapTime" "0"''}
-          Option "TapButton1" "${builtins.elemAt cfg.buttonsMap 0}"
-          Option "TapButton2" "${builtins.elemAt cfg.buttonsMap 1}"
-          Option "TapButton3" "${builtins.elemAt cfg.buttonsMap 2}"
+          ${optionalString cfg.tapButtons tapConfig}
           Option "ClickFinger1" "${builtins.elemAt cfg.buttonsMap 0}"
           Option "ClickFinger2" "${builtins.elemAt cfg.buttonsMap 1}"
           Option "ClickFinger3" "${builtins.elemAt cfg.buttonsMap 2}"
diff --git a/nixos/modules/services/x11/hardware/wacom.nix b/nixos/modules/services/x11/hardware/wacom.nix
index dfc588cd2132..540ed168b489 100644
--- a/nixos/modules/services/x11/hardware/wacom.nix
+++ b/nixos/modules/services/x11/hardware/wacom.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index b9ad962d8e46..d73b58de6c08 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -1,5 +1,5 @@
-{ config, pkgs, ... }:
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
+with lib;
 let
   cfg = config.services.redshift;
 
@@ -14,24 +14,37 @@ in {
 
     services.redshift.latitude = mkOption {
       description = "Your current latitude";
-      type = types.string;
+      type = types.uniq types.string;
     };
 
     services.redshift.longitude = mkOption {
       description = "Your current longitude";
-      type = types.string;
+      type = types.uniq types.string;
     };
 
     services.redshift.temperature = {
       day = mkOption {
         description = "Colour temperature to use during day time";
         default = 5500;
-        type = types.int;
+        type = types.uniq types.int;
       };
       night = mkOption {
         description = "Colour temperature to use during night time";
         default = 3700;
-        type = types.int;
+        type = types.uniq types.int;
+      };
+    };
+
+    services.redshift.brightness = {
+      day = mkOption {
+        description = "Screen brightness to apply during the day (between 0.1 and 1.0)";
+        default = "1";
+        type = types.uniq types.string;
+      };
+      night = mkOption {
+        description = "Screen brightness to apply during the night (between 0.1 and 1.0)";
+        default = "1";
+        type = types.uniq types.string;
       };
     };
   };
@@ -41,10 +54,12 @@ in {
       description = "Redshift colour temperature adjuster";
       requires = [ "display-manager.service" ];
       after = [ "display-manager.service" ];
-      script = ''
+      wantedBy = [ "graphical.target" ];
+      serviceConfig.ExecStart = ''
         ${pkgs.redshift}/bin/redshift \
           -l ${cfg.latitude}:${cfg.longitude} \
-          -t ${toString cfg.temperature.day}:${toString cfg.temperature.night}
+          -t ${toString cfg.temperature.day}:${toString cfg.temperature.night} \
+          -b ${toString cfg.brightness.day}:${toString cfg.brightness.night}
       '';
       environment = { DISPLAY = ":0"; };
       serviceConfig.Restart = "always";
diff --git a/nixos/modules/services/x11/terminal-server.nix b/nixos/modules/services/x11/terminal-server.nix
index bf9c3435503d..bdc23c0acd18 100644
--- a/nixos/modules/services/x11/terminal-server.nix
+++ b/nixos/modules/services/x11/terminal-server.nix
@@ -5,9 +5,9 @@
 # not, a X server (Xvfb) is started for that user.  The Xvfb instances
 # persist across VNC sessions.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -27,7 +27,7 @@ in
   config = {
 
     services.xserver.enable = true;
-    services.xserver.videoDrivers = [];
+    hardware.opengl.videoDrivers = [];
 
     # Enable KDM.  Any display manager will do as long as it supports XDMCP.
     services.xserver.displayManager.kdm.enable = true;
diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix
index 1c61419a44c0..9b2f042a87a6 100644
--- a/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixos/modules/services/x11/window-managers/awesome.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/compiz.nix b/nixos/modules/services/x11/window-managers/compiz.nix
index 209401f26468..ffd71e5f91ec 100644
--- a/nixos/modules/services/x11/window-managers/compiz.nix
+++ b/nixos/modules/services/x11/window-managers/compiz.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 4d52e398b477..f27ba3661413 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.xserver.windowManager;
@@ -16,7 +16,6 @@ in
       ./wmii.nix
       ./xmonad.nix
       ./i3.nix
-      ./xbmc.nix
       ./herbstluftwm.nix
     ];
 
diff --git a/nixos/modules/services/x11/window-managers/herbstluftwm.nix b/nixos/modules/services/x11/window-managers/herbstluftwm.nix
index 9480abba43b7..6cda910b6b33 100644
--- a/nixos/modules/services/x11/window-managers/herbstluftwm.nix
+++ b/nixos/modules/services/x11/window-managers/herbstluftwm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.xserver.windowManager.herbstluftwm;
diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix
index e53d86187ae6..e85c3bce591d 100644
--- a/nixos/modules/services/x11/window-managers/i3.nix
+++ b/nixos/modules/services/x11/window-managers/i3.nix
@@ -1,6 +1,6 @@
-{ pkgs, config, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.services.xserver.windowManager.i3;
diff --git a/nixos/modules/services/x11/window-managers/icewm.nix b/nixos/modules/services/x11/window-managers/icewm.nix
index b7da4051c141..36028da453a5 100644
--- a/nixos/modules/services/x11/window-managers/icewm.nix
+++ b/nixos/modules/services/x11/window-managers/icewm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/metacity.nix b/nixos/modules/services/x11/window-managers/metacity.nix
index 712e2038594e..d13cbcfe40e8 100644
--- a/nixos/modules/services/x11/window-managers/metacity.nix
+++ b/nixos/modules/services/x11/window-managers/metacity.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/twm.nix b/nixos/modules/services/x11/window-managers/twm.nix
index d80ffe4942fb..684b34c2f246 100644
--- a/nixos/modules/services/x11/window-managers/twm.nix
+++ b/nixos/modules/services/x11/window-managers/twm.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/wmii.nix b/nixos/modules/services/x11/window-managers/wmii.nix
index b61521274fba..75f6fdfe3bc4 100644
--- a/nixos/modules/services/x11/window-managers/wmii.nix
+++ b/nixos/modules/services/x11/window-managers/wmii.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/window-managers/xbmc.nix b/nixos/modules/services/x11/window-managers/xbmc.nix
deleted file mode 100644
index 46494202b404..000000000000
--- a/nixos/modules/services/x11/window-managers/xbmc.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{pkgs, config, ...}:
-
-let
-  inherit (pkgs.lib) mkOption mkIf;
-  cfg = config.services.xserver.windowManager.xbmc;
-in
-
-{
-  options = {
-    services.xserver.windowManager.xbmc = {
-      enable = mkOption {
-        default = false;
-        example = true;
-        description = "Enable the xbmc multimedia center.";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    services.xserver.windowManager = {
-      session = [{
-        name = "xbmc";
-        start = "
-          ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
-          waitPID=$!
-        ";
-      }];
-    };
-    environment.systemPackages = [ pkgs.xbmc ];
-  };
-}
diff --git a/nixos/modules/services/x11/xfs.nix b/nixos/modules/services/x11/xfs.nix
index 44c1d533c3ac..196f3beb41e9 100644
--- a/nixos/modules/services/x11/xfs.nix
+++ b/nixos/modules/services/x11/xfs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 5600ce7fac13..65f93b544996 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, pkgs_i686, ... }:
+{ config, lib, pkgs, pkgs_i686, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -20,9 +20,16 @@ let
     nvidiaLegacy304 = { modules = [ kernelPackages.nvidia_x11_legacy304 ]; driverName = "nvidia"; };
     unichrome    = { modules = [ pkgs.xorgVideoUnichrome ]; };
     virtualbox   = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
+    ati = { modules = [ pkgs.xorg.xf86videoati pkgs.xorg.glamoregl ]; };
+    intel-testing = { modules = with pkgs.xorg; [ xf86videointel-testing glamoregl ]; driverName = "intel"; };
   };
 
-  driverNames = config.services.mesa.videoDrivers;
+  driverNames = config.hardware.opengl.videoDrivers;
+
+  needsAcpid =
+     (elem "nvidia" driverNames) ||
+     (elem "nvidiaLegacy173" driverNames) ||
+     (elem "nvidiaLegacy304" driverNames);
 
   drivers = flip map driverNames
     (name: { inherit name; driverName = name; } //
@@ -181,7 +188,7 @@ in
         description = ''
           The name of the video driver for your graphics card.  This
           option is obsolete; please set the
-          <option>services.mesa.videoDrivers</option> instead.
+          <option>hardware.opengl.videoDrivers</option> instead.
         '';
       };
 
@@ -194,17 +201,6 @@ in
         '';
       };
 
-      startOpenSSHAgent = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Whether to start the OpenSSH agent when you log in.  The OpenSSH agent
-          remembers private keys for you so that you don't have to type in
-          passphrases every time you make an SSH connection.  Use
-          <command>ssh-add</command> to add a key to the agent.
-        '';
-      };
-
       startGnuPGAgent = mkOption {
         type = types.bool;
         default = false;
@@ -372,6 +368,14 @@ in
         '';
       };
 
+      useGlamor = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to use the Glamor module for 2D acceleration,
+          if possible.
+        '';
+      };
     };
 
   };
@@ -381,15 +385,15 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    services.mesa.enable = true;
-    services.mesa.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
+    hardware.opengl.enable = true;
+    hardware.opengl.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
 
     assertions =
-      [ { assertion = !(cfg.startOpenSSHAgent && cfg.startGnuPGAgent);
+      [ { assertion = !(config.programs.ssh.startAgent && cfg.startGnuPGAgent);
           message =
             ''
-              The OpenSSH agent and GnuPG agent cannot be started both.
-              Choose between `startOpenSSHAgent' and `startGnuPGAgent'.
+              The OpenSSH agent and GnuPG agent cannot be started both. Please
+              choose between ‘programs.ssh.startAgent’ and ‘services.xserver.startGnuPGAgent’.
             '';
         }
         { assertion = config.security.polkit.enable;
@@ -428,6 +432,8 @@ in
       ++ optional (elem "virtualbox" driverNames) xorg.xrefresh
       ++ optional (elem "ati_unfree" driverNames) kernelPackages.ati_drivers_x11;
 
+    services.acpid.enable = mkIf needsAcpid true;
+
     environment.pathsToLink =
       [ "/etc/xdg" "/share/xdg" "/share/applications" "/share/icons" "/share/pixmaps" ];
 
@@ -436,7 +442,8 @@ in
     systemd.services."display-manager" =
       { description = "X11 Server";
 
-        after = [ "systemd-udev-settle.service" "local-fs.target" ];
+        after = [ "systemd-udev-settle.service" "local-fs.target" ]
+                ++ optional needsAcpid "acpid.service";
 
         restartIfChanged = false;
 
@@ -523,6 +530,13 @@ in
           '')}
         EndSection
 
+        ${if cfg.useGlamor then ''
+          Section "Module"
+            Load "dri2"
+            Load "glamoregl"
+          EndSection
+        '' else ""}
+
         # For each supported driver, add a "Device" and "Screen"
         # section.
         ${flip concatMapStrings drivers (driver: ''
@@ -530,6 +544,7 @@ in
           Section "Device"
             Identifier "Device-${driver.name}[0]"
             Driver "${driver.driverName}"
+            ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
             ${cfg.deviceSection}
             ${xrandrDeviceSection}
           EndSection
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 1545bcb8a1f9..41fe7d309a5a 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -1,7 +1,7 @@
 # generate the script used to activate the configuration.
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/system/activation/no-clone.nix b/nixos/modules/system/activation/no-clone.nix
index c9ab691ce477..7f4584435266 100644
--- a/nixos/modules/system/activation/no-clone.nix
+++ b/nixos/modules/system/activation/no-clone.nix
@@ -1,6 +1,6 @@
-{pkgs, ...}:
+{ lib, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   boot.loader.grub.device = mkOverride 0 "nodev";
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 91beed1130eb..25b5afe99da4 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -27,7 +27,10 @@ EOF
     exit 1;
 }
 
-die "This is not a NixOS installation (/etc/NIXOS is missing)!\n" unless -f "/etc/NIXOS";
+# This is a NixOS installation if it has /etc/NIXOS or a proper
+# /etc/os-release.
+die "This is not a NixOS installation!\n" unless
+    -f "/etc/NIXOS" || (read_file("/etc/os-release", err_mode => 'quiet') // "") =~ /ID=nixos/s;
 
 openlog("nixos", "", LOG_USER);
 
@@ -96,12 +99,18 @@ sub parseFstab {
 sub parseUnit {
     my ($filename) = @_;
     my $info = {};
-    foreach my $line (read_file($filename)) {
+    parseKeyValues($info, read_file($filename));
+    parseKeyValues($info, read_file("${filename}.d/overrides.conf")) if -f "${filename}.d/overrides.conf";
+    return $info;
+}
+
+sub parseKeyValues {
+    my $info = shift;
+    foreach my $line (@_) {
         # FIXME: not quite correct.
         $line =~ /^([^=]+)=(.*)$/ or next;
         $info->{$1} = $2;
     }
-    return $info;
 }
 
 sub boolIsTrue {
@@ -109,6 +118,14 @@ sub boolIsTrue {
     return $s eq "yes" || $s eq "true";
 }
 
+# As a fingerprint for determining whether a unit has changed, we use
+# its absolute path. If it has an override file, we append *its*
+# absolute path as well.
+sub fingerprintUnit {
+    my ($s) = @_;
+    return abs_path($s) . (-f "${s}.d/overrides.conf" ? " " . abs_path "${s}.d/overrides.conf" : "");
+}
+
 # Stop all services that no longer exist or have changed in the new
 # configuration.
 my (@unitsToStop, @unitsToSkip);
@@ -125,7 +142,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
     $baseName =~ s/\.[a-z]*$//;
 
     if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
-        if (! -e $newUnitFile) {
+        if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
             push @unitsToStop, $unit;
         }
 
@@ -160,7 +177,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
             }
         }
 
-        elsif (abs_path($prevUnitFile) ne abs_path($newUnitFile)) {
+        elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) {
             if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target") {
                 # Do nothing.  These cannot be restarted directly.
             } elsif ($unit =~ /\.mount$/) {
@@ -170,7 +187,10 @@ while (my ($unit, $state) = each %{$activePrev}) {
                 # FIXME: do something?
             } else {
                 my $unitInfo = parseUnit($newUnitFile);
-                if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
+                if (boolIsTrue($unitInfo->{'X-ReloadIfChanged'} // "no")) {
+                    write_file($reloadListFile, { append => 1 }, "$unit\n");
+                }
+                elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
                     push @unitsToSkip, $unit;
                 } else {
                     # If this unit is socket-activated, then stop the
@@ -319,7 +339,7 @@ if (scalar @restart > 0) {
 # that are symlinks to other units.  We shouldn't start both at the
 # same time because we'll get a "Failed to add path to set" error from
 # systemd.
-my @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
+my @start = unique("default.target", "timers.target", "sockets.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
 print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
 $systemdManager->StartUnit($_, "replace") for @start;
 unlink($startListFile);
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 30a529988a96..1600a1fb0104 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, modules, baseModules, ... }:
+{ config, lib, pkgs, modules, baseModules, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -11,7 +11,7 @@ let
   # you can provide an easy way to boot the same configuration
   # as you use, but with another kernel
   # !!! fix this
-  cloner = inheritParent: list: with pkgs.lib;
+  cloner = inheritParent: list:
     map (childConfig:
       (import ../../../lib/eval-config.nix {
         inherit baseModules;
@@ -68,6 +68,7 @@ let
       echo -n "$configurationName" > $out/configuration-name
       echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
       echo -n "$nixosVersion" > $out/nixos-version
+      echo -n "$system" > $out/system
 
       mkdir $out/fine-tune
       childCount=0
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 2b075bf6a6d2..b81bcf20f439 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -159,7 +159,7 @@ in
 
     boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
 
-    boot.kernelModules = [ "loop" ];
+    boot.kernelModules = [ "loop" "configs" ];
 
     boot.initrd.availableKernelModules =
       [ # Note: most of these (especially the SATA/PATA modules)
@@ -203,6 +203,9 @@ in
 
         # To wait for SCSI devices to appear.
         "scsi_wait_scan"
+
+        # Needed by the stage 2 init script.
+        "rtc_cmos"
       ];
 
     boot.initrd.kernelModules =
@@ -215,37 +218,26 @@ in
 
     # Create /etc/modules-load.d/nixos.conf, which is read by
     # systemd-modules-load.service to load required kernel modules.
-    # FIXME: ensure that systemd-modules-load.service is restarted if
-    # this file changes.
     environment.etc = singleton
       { target = "modules-load.d/nixos.conf";
         source = kernelModulesConf;
       };
 
-    # Sigh.  This overrides systemd's systemd-modules-load.service
-    # just so we can set a restart trigger.  Also make
-    # multi-user.target pull it in so that it gets started if it
-    # failed earlier.
     systemd.services."systemd-modules-load" =
-      { description = "Load Kernel Modules";
-        wantedBy = [ "sysinit.target" "multi-user.target" ];
-        before = [ "sysinit.target" "shutdown.target" ];
-        conflicts = [ "shutdown.target" ];
-        unitConfig =
-          { DefaultDependencies = false;
-            ConditionCapability = "CAP_SYS_MODULE";
-          };
+      { wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ kernelModulesConf ];
+        environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
         serviceConfig =
-          { Type = "oneshot";
-            RemainAfterExit = true;
-            ExecStart = "${config.systemd.package}/lib/systemd/systemd-modules-load";
-            # Ignore failed module loads.  Typically some of the
+          { # Ignore failed module loads.  Typically some of the
             # modules in ‘boot.kernelModules’ are "nice to have but
             # not required" (e.g. acpi-cpufreq), so we don't want to
             # barf on those.
             SuccessExitStatus = "0 1";
           };
-        restartTriggers = [ kernelModulesConf ];
+      };
+
+    systemd.services.kmod-static-nodes =
+      { environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
       };
 
     lib.kernelConfig = {
diff --git a/nixos/modules/system/boot/loader/efi.nix b/nixos/modules/system/boot/loader/efi.nix
index 7e739173f9a3..241cfc7e836d 100644
--- a/nixos/modules/system/boot/loader/efi.nix
+++ b/nixos/modules/system/boot/loader/efi.nix
@@ -1,6 +1,6 @@
-{ pkgs, ... }:
+{ lib, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options.boot.loader.efi = {
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
index 9855c8c19dd0..4b5e84f53c1a 100644
--- a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index ef6ff71ed778..a3b09223cbb8 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -133,11 +133,8 @@ in
             chainloader (hd0,1)+1
 
           # GRUB 2 example
-          menuentry "Windows7" {
-            title Windows7
-            insmod ntfs
-            set root='(hd1,1)'
-            chainloader +1
+          menuentry "Windows 7" {
+            chainloader (hd0,4)+1
           }
         '';
         description = ''
diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix
index 80c1a160cfde..94e5a14174b0 100644
--- a/nixos/modules/system/boot/loader/grub/memtest.nix
+++ b/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -1,33 +1,87 @@
 # This module adds Memtest86+ to the GRUB boot menu.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   memtest86 = pkgs.memtest86plus;
+  cfg = config.boot.loader.grub.memtest86;
 in
 
 {
   options = {
 
-    boot.loader.grub.memtest86 = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Make Memtest86+, a memory testing program, available from the
-        GRUB boot menu.
-      '';
+    boot.loader.grub.memtest86 = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Make Memtest86+, a memory testing program, available from the
+          GRUB boot menu.
+        '';
+      };
+
+      params = mkOption {
+        default = [];
+        example = [ "console=ttyS0,115200" ];
+        type = types.listOf types.str;
+        description = ''
+          Parameters added to the Memtest86+ command line. As of memtest86+ 5.01
+          the following list of (apparently undocumented) parameters are
+          accepted:
+
+          <itemizedlist>
+
+          <listitem>
+            <para><literal>console=...</literal>, set up a serial console.
+            Examples:
+            <literal>console=ttyS0</literal>,
+            <literal>console=ttyS0,9600</literal> or
+            <literal>console=ttyS0,115200n8</literal>.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>btrace</literal>, enable boot trace.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>maxcpus=N</literal>, limit number of CPUs.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>onepass</literal>, run one pass and exit if there
+            are no errors.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>tstlist=...</literal>, list of tests to run.
+            Example: <literal>0,1,2</literal>.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs
+            to use for testing.</para>
+          </listitem>
+
+          </itemizedlist>
+
+          This list of command line options was obtained by reading the
+          Memtest86+ source code.
+        '';
+      };
+
     };
   };
 
-  config = mkIf config.boot.loader.grub.memtest86 {
+  config = mkIf cfg.enable {
 
     boot.loader.grub.extraEntries =
       if config.boot.loader.grub.version == 2 then
         ''
           menuentry "Memtest86+" {
-            linux16 @bootRoot@/memtest.bin
+            linux16 @bootRoot@/memtest.bin ${toString cfg.params}
           }
         ''
       else
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
index 9ea224b51f63..db73544181b6 100644
--- a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
@@ -9,7 +9,6 @@ import tempfile
 import errno
 
 def copy_if_not_exists(source, dest):
-    known_paths.append(dest)
     if not os.path.exists(dest):
         shutil.copyfile(source, dest)
 
@@ -38,12 +37,13 @@ def write_loader_conf(generation):
         print >> f, "default nixos-generation-%d" % (generation)
     os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
 
-def copy_from_profile(generation, name):
+def copy_from_profile(generation, name, dry_run=False):
     store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
     suffix = os.path.basename(store_file_path)
     store_dir = os.path.basename(os.path.dirname(store_file_path))
     efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
-    copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
+    if not dry_run:
+        copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
     return efi_file_path
 
 def add_entry(generation):
@@ -72,6 +72,10 @@ def get_generations(profile):
 def remove_old_entries(gens):
     slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
     slice_end = -1 * len(".conf")
+    known_paths = []
+    for gen in gens:
+        known_paths.append(copy_from_profile(gen, "kernel", True))
+        known_paths.append(copy_from_profile(gen, "initrd", True))
     for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
         try:
             gen = int(path[slice_start:slice_end])
@@ -94,7 +98,6 @@ if os.getenv("NIXOS_INSTALL_GRUB") == "1":
     else:
         subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
 
-known_paths = []
 mkdir_p("@efiSysMountPoint@/efi/nixos")
 mkdir_p("@efiSysMountPoint@/loader/entries")
 try:
@@ -106,9 +109,8 @@ except IOError as e:
     machine_id = None
 
 gens = get_generations("system")
+remove_old_entries(gens)
 for gen in gens:
     add_entry(gen)
     if os.readlink(system_dir(gen)) == args.default_config:
         write_loader_conf(gen)
-
-remove_old_entries(gens)
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix b/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
index 7edc30776379..19c613a7c94c 100644
--- a/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   cfg = config.boot.loader.gummiboot;
diff --git a/nixos/modules/system/boot/loader/init-script/init-script.nix b/nixos/modules/system/boot/loader/init-script/init-script.nix
index 4b0fcd85b4b5..3b33d42b4ae4 100644
--- a/nixos/modules/system/boot/loader/init-script/init-script.nix
+++ b/nixos/modules/system/boot/loader/init-script/init-script.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index 5bc856c3df0b..d3f32418a64c 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index ba357f5d2de3..c923cc49c449 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -1,11 +1,11 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
   luks = config.boot.initrd.luks;
 
-  openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
+  openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
     # Wait for luksRoot to appear, e.g. if on a usb drive.
     # XXX: copied and adapted from stage-1-init.sh - should be
     # available as a function.
@@ -31,9 +31,161 @@ let
     fi
     ''}
 
+    open_normally() {
+        cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
+          ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
+    }
+
+    ${optionalString (luks.yubikeySupport && (yubikey != null)) ''
+
+    rbtohex() {
+        ( od -An -vtx1 | tr -d ' \n' )
+    }
+
+    hextorb() {
+        ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
+    }
+
+    open_yubikey() {
+
+        # Make all of these local to this function
+        # to prevent their values being leaked
+        local salt
+        local iterations
+        local k_user
+        local challenge
+        local response
+        local k_luks
+        local opened
+        local new_salt
+        local new_iterations
+        local new_challenge
+        local new_response
+        local new_k_luks
+
+        mkdir -p ${yubikey.storage.mountPoint}
+        mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
+
+        salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
+        iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
+        challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
+        response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
+
+        for try in $(seq 3); do
+
+            ${optionalString yubikey.twoFactor ''
+            echo -n "Enter two-factor passphrase: "
+            read -s k_user
+            echo
+            ''}
+
+            if [ ! -z "$k_user" ]; then
+                k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+            else
+                k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+            fi
+
+            echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
+
+            if [ $? == "0" ]; then
+                opened=true
+                break
+            else
+                opened=false
+                echo "Authentication failed!"
+            fi
+        done
+
+        if [ "$opened" == false ]; then
+            umount ${yubikey.storage.mountPoint}
+            echo "Maximum authentication errors reached"
+            exit 1
+        fi
+
+        echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
+        for i in $(seq ${toString yubikey.saltLength}); do
+            byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
+            new_salt="$new_salt$byte";
+            echo -n .
+        done;
+        echo "ok"
+
+        new_iterations="$iterations"
+        ${optionalString (yubikey.iterationStep > 0) ''
+        new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
+        ''}
+
+        new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
+
+        new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
+
+        if [ ! -z "$k_user" ]; then
+            new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+        else
+            new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+        fi
+
+        mkdir -p ${yubikey.ramfsMountPoint}
+        # A ramfs is used here to ensure that the file used to update
+        # the key slot with cryptsetup will never get swapped out.
+        # Warning: Do NOT replace with tmpfs!
+        mount -t ramfs none ${yubikey.ramfsMountPoint}
+
+        echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
+        echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
+
+        if [ $? == "0" ]; then
+            echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
+        else
+            echo "Warning: Could not update LUKS key, current challenge persists!"
+        fi
+
+        rm -f ${yubikey.ramfsMountPoint}/new_key
+        umount ${yubikey.ramfsMountPoint}
+        rm -rf ${yubikey.ramfsMountPoint}
+
+        umount ${yubikey.storage.mountPoint}
+    }
+
+    ${optionalString (yubikey.gracePeriod > 0) ''
+    echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
+    for i in $(seq ${toString yubikey.gracePeriod}); do
+        sleep 1
+        echo -n .
+    done
+    echo "ok"
+    ''}
+
+    yubikey_missing=true
+    ykinfo -v 1>/dev/null 2>&1
+    if [ $? != "0" ]; then
+        echo -n "waiting 10 seconds for yubikey to appear..."
+        for try in $(seq 10); do
+            sleep 1
+            ykinfo -v 1>/dev/null 2>&1
+            if [ $? == "0" ]; then
+                yubikey_missing=false
+                break
+            fi
+            echo -n .
+        done
+        echo "ok"
+    else
+        yubikey_missing=false
+    fi
+
+    if [ "$yubikey_missing" == true ]; then
+        echo "no yubikey found, falling back to non-yubikey open procedure"
+        open_normally
+    else
+        open_yubikey
+    fi
+    ''}
+
     # open luksRoot and scan for logical volumes
-    cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
-      ${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
+    ${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
+    open_normally
+    ''}
   '';
 
   isPreLVM = f: f.preLVM;
@@ -139,10 +291,108 @@ in
           '';
         };
 
-      };
+        yubikey = mkOption {
+          default = null;
+          type = types.nullOr types.optionSet;
+          description = ''
+            The options to use for this LUKS device in Yubikey-PBA.
+            If null (the default), Yubikey-PBA will be disabled for this device.
+          '';
 
+          options = {
+            twoFactor = mkOption {
+              default = true;
+              type = types.bool;
+              description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
+            };
+
+            slot = mkOption {
+              default = 2;
+              type = types.int;
+              description = "Which slot on the Yubikey to challenge";
+            };
+
+            saltLength = mkOption {
+              default = 16;
+              type = types.int;
+              description = "Length of the new salt in byte (64 is the effective maximum)";
+            };
+
+            keyLength = mkOption {
+              default = 64;
+              type = types.int;
+              description = "Length of the LUKS slot key derived with PBKDF2 in byte";
+            };
+
+            iterationStep = mkOption {
+              default = 0;
+              type = types.int;
+              description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
+            };
+
+            gracePeriod = mkOption {
+              default = 2;
+              type = types.int;
+              description = "Time in seconds to wait before attempting to find the Yubikey";
+            };
+
+            ramfsMountPoint = mkOption {
+              default = "/crypt-ramfs";
+              type = types.string;
+              description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
+            };
+
+            storage = mkOption {
+              type = types.optionSet;
+              description = "Options related to the storing the salt";
+
+              options = {
+                device = mkOption {
+                  default = /dev/sda1;
+                  type = types.path;
+                  description = ''
+                    An unencrypted device that will temporarily be mounted in stage-1.
+                    Must contain the current salt to create the challenge for this LUKS device.
+                  '';
+                };
+
+                fsType = mkOption {
+                  default = "vfat";
+                  type = types.string;
+                  description = "The filesystem of the unencrypted device";
+                };
+
+                mountPoint = mkOption {
+                  default = "/crypt-storage";
+                  type = types.string;
+                  description = "Path where the unencrypted device will be mounted in stage-1";
+                };
+
+                path = mkOption {
+                  default = "/crypt-storage/default";
+                  type = types.string;
+                  description = ''
+                    Absolute path of the salt on the unencrypted device with
+                    that device's root directory as "/".
+                  '';
+                };
+              };
+            };
+          };
+        };
+
+      };
     };
 
+    boot.initrd.luks.yubikeySupport = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+            Enables support for authenticating with a Yubikey on LUKS devices.
+            See the NixOS wiki for information on how to properly setup a LUKS device
+            and a Yubikey to work with this feature.
+          '';
+    };
   };
 
   config = mkIf (luks.devices != []) {
@@ -157,15 +407,48 @@ in
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = ''
       cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
-      # XXX: do we have a function that does this?
-      for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
-        cp -pdvn $lib $out/lib
-        cp -pvn $(readlink -f $lib) $out/lib
-      done
+
+      cp -pdv ${pkgs.libgcrypt}/lib/libgcrypt*.so.* $out/lib
+      cp -pdv ${pkgs.libgpgerror}/lib/libgpg-error*.so.* $out/lib
+      cp -pdv ${pkgs.cryptsetup}/lib/libcryptsetup*.so.* $out/lib
+      cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
+
+      ${optionalString luks.yubikeySupport ''
+      cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
+      cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
+      cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
+
+      cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o $out/bin/pbkdf2-sha512 -lcrypto
+      strip -s $out/bin/pbkdf2-sha512
+
+      cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
+      cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
+      cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
+      cp -pdv ${pkgs.openssl}/lib/libssl*.so.* $out/lib
+      cp -pdv ${pkgs.openssl}/lib/libcrypto*.so.* $out/lib
+
+      mkdir -p $out/etc/ssl
+      cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
+
+      cat > $out/bin/openssl-wrap <<EOF
+#!$out/bin/sh
+EOF
+      chmod +x $out/bin/openssl-wrap
+      ''}
     '';
 
     boot.initrd.extraUtilsCommandsTest = ''
       $out/bin/cryptsetup --version
+      ${optionalString luks.yubikeySupport ''
+        $out/bin/ykchalresp -V
+        $out/bin/ykinfo -V
+        cat > $out/bin/openssl-wrap <<EOF
+#!$out/bin/sh
+export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
+$out/bin/openssl "\$@"
+EOF
+        $out/bin/openssl-wrap version
+      ''}
     '';
 
     boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index 027a7ac99d51..7b214cd1e1f5 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
@@ -68,7 +68,10 @@ with pkgs.lib;
 
   config = mkIf (!config.boot.isContainer) {
 
-    environment.etc = singleton
+    environment.etc = [
+      { source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
+        target = "modprobe.d/ubuntu.conf";
+      }
       { source = pkgs.writeText "modprobe.conf"
           ''
             ${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
@@ -77,26 +80,11 @@ with pkgs.lib;
             ${config.boot.extraModprobeConfig}
           '';
         target = "modprobe.d/nixos.conf";
-      };
+      }
+    ];
 
     environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
 
-    boot.blacklistedKernelModules =
-      [ # This module is for debugging and generates gigantic amounts
-        # of log output, so it should never be loaded automatically.
-        "evbug"
-
-        # This module causes ALSA to occassionally select the wrong
-        # default sound device, and is little more than an annoyance
-        # on modern machines.
-        "snd_pcsp"
-
-        # The cirrusfb module prevents X11 from starting.  FIXME:
-        # Ubuntu blacklists all framebuffer devices because they're
-        # "buggy" and cause suspend problems.  Maybe we should too?
-        "cirrusfb"
-      ];
-
     system.activationScripts.modprobe =
       ''
         # Allow the kernel to find our wrapped modprobe (which searches
diff --git a/nixos/modules/system/boot/pbkdf2-sha512.c b/nixos/modules/system/boot/pbkdf2-sha512.c
new file mode 100644
index 000000000000..b40c383ac023
--- /dev/null
+++ b/nixos/modules/system/boot/pbkdf2-sha512.c
@@ -0,0 +1,38 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <openssl/evp.h>
+
+void hextorb(uint8_t* hex, uint8_t* rb)
+{
+	while(sscanf(hex, "%2x", rb) == 1)
+	{
+		hex += 2;
+		rb += 1;
+	}
+	*rb = '\0';
+}
+
+int main(int argc, char** argv)
+{
+	uint8_t k_user[2048];
+	uint8_t salt[2048];
+	uint8_t key[4096];
+
+	uint32_t key_length = atoi(argv[1]);
+	uint32_t iteration_count = atoi(argv[2]);
+
+	hextorb(argv[3], salt);
+	uint32_t salt_length = strlen(argv[3]) / 2;
+
+	fgets(k_user, 2048, stdin);
+	uint32_t k_user_length = strlen(k_user);
+	if(k_user[k_user_length - 1] == '\n') {
+			k_user[k_user_length - 1] = '\0';
+	}
+
+	PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
+	fwrite(key, 1, key_length, stdout);
+
+	return 0;
+}
\ No newline at end of file
diff --git a/nixos/modules/system/boot/shutdown.nix b/nixos/modules/system/boot/shutdown.nix
index 44cadcd64a76..68bc936c5b0b 100644
--- a/nixos/modules/system/boot/shutdown.nix
+++ b/nixos/modules/system/boot/shutdown.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
 
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 1f65026b5def..216937a619b1 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -14,7 +14,7 @@ fail() {
     # in an interactive shell.
     cat <<EOF
 
-An error occured in stage 1 of the boot process, which must mount the
+An error occurred in stage 1 of the boot process, which must mount the
 root filesystem on \`$targetRoot' and then start stage 2.  Press one
 of the following keys:
 
@@ -139,8 +139,6 @@ mkdir -p /dev/.mdadm
 systemd-udevd --daemon
 udevadm trigger --action=add
 udevadm settle || true
-modprobe scsi_wait_scan || true
-udevadm settle || true
 
 
 # Load boot-time keymap before any LVM/LUKS initialization
@@ -320,6 +318,10 @@ while read -u 3 mountPoint; do
         echo -n "waiting for device $device to appear..."
         for try in $(seq 1 20); do
             sleep 1
+            # also re-try lvm activation now that new block devices might have appeared
+            lvm vgchange -ay
+            # and tell udev to create nodes for the new LVs
+            udevadm trigger --action=add
             if test -e $device; then break; fi
             echo -n "."
         done
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 8ed3aecb6911..c38d33c45d6e 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -3,9 +3,9 @@
 # the modules necessary to mount the root file system, then calls the
 # init in the root file system to start the second boot stage.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -74,7 +74,7 @@ let
       cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
       cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
       cp -v ${pkgs.lvm2}/lib/libdevmapper.so.*.* $out/lib
-      cp -v ${pkgs.systemd}/lib/libsystemd-daemon.so.* $out/lib
+      cp -v ${pkgs.systemd}/lib/libsystemd.so.* $out/lib
 
       # Add RAID mdadm tool.
       cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 2fadd3de1f0f..a64c6cdfa191 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -82,7 +82,7 @@ done
 
 # More special file systems, initialise required directories.
 mkdir -m 0755 /dev/shm
-mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
+mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" none /dev/shm
 mkdir -m 0755 -p /dev/pts
 [ -e /proc/bus/usb ] && mount -t usbfs none /proc/bus/usb # UML doesn't have USB by default
 mkdir -m 01777 -p /tmp
@@ -96,28 +96,14 @@ mkdir -m 0755 -p /etc/nixos
 
 # Miscellaneous boot time cleanup.
 rm -rf /var/run /var/lock
-rm -f /etc/resolv.conf
-touch /etc/resolv.conf
 rm -f /etc/{group,passwd,shadow}.lock
 
 if test -n "@cleanTmpDir@"; then
     echo -n "cleaning \`/tmp'..."
     find /tmp -maxdepth 1 -mindepth 1 -print0 | xargs -0r rm -rf --one-file-system
     echo " done"
-else
-    # Get rid of ICE locks...
-    rm -rf /tmp/.ICE-unix
 fi
 
-# ... and ensure that it's owned by root.
-mkdir -m 1777 /tmp/.ICE-unix
-
-# This is a good time to clean up /nix/var/nix/chroots.  Doing an `rm
-# -rf' on it isn't safe in general because it can contain bind mounts
-# to /nix/store and other places.  But after rebooting these are all
-# gone, of course.
-rm -rf /nix/var/nix/chroots # recreated in activate-configuration.sh
-
 
 # Also get rid of temporary GC roots.
 rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
@@ -131,6 +117,15 @@ if ! mountpoint -q /run; then
     mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
 fi
 
+# Create a ramfs on /run/keys to hold secrets that shouldn't be
+# written to disk (generally used for NixOps, harmless elsewhere).
+if ! mountpoint -q /run/keys; then
+    rm -rf /run/keys
+    mkdir -m 0750 /run/keys
+    chown 0:96 /run/keys
+    mount -t ramfs none /run/keys
+fi
+
 mkdir -m 0755 -p /run/lock
 
 
@@ -146,12 +141,32 @@ if test -n "$resumeDevice"; then
 fi
 
 
+# Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
+if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then
+    cat /etc/resolv.conf | resolvconf -m 1000 -a host
+else
+    touch /etc/resolv.conf
+fi
+
+
+# Create /var/setuid-wrappers as a tmpfs.
+rm -rf /var/setuid-wrappers
+mkdir -m 0755 -p /var/setuid-wrappers
+mount -t tmpfs -o "mode=0755" none /var/setuid-wrappers
+
+
 # Run the script that performs all configuration activation that does
 # not have to be done at boot time.
 echo "running activation script..."
 $systemConfig/activate
 
 
+# Restore the system time from the hardware clock.  We do this after
+# running the activation script to be sure that /etc/localtime points
+# at the current time zone.
+hwclock --hctosys
+
+
 # Record the boot configuration.
 ln -sfn "$systemConfig" /run/booted-system
 
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index aa0d7e0c138b..f53c3b8b8e70 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -19,11 +19,13 @@ let
     isExecutable = true;
     inherit (config.boot) devShmSize runSize cleanTmpDir;
     inherit (config.nix) readOnlyStore;
+    inherit (config.networking) useHostResolvConf;
     ttyGid = config.ids.gids.tty;
     path =
       [ pkgs.coreutils
         pkgs.utillinux
         pkgs.sysvtools
+        pkgs.openresolv
       ] ++ (optional config.boot.cleanTmpDir pkgs.findutils)
       ++ optional config.nix.readOnlyStore readonlyMountpoint;
     postBootCommands = pkgs.writeText "local-cmds"
@@ -79,6 +81,7 @@ in
         '';
       };
 
+      # FIXME: should replace this with something that uses systemd-tmpfiles.
       cleanTmpDir = mkOption {
         type = types.bool;
         default = false;
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index c0518599f17a..a6183c47eb1b 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -1,6 +1,6 @@
-{ config, pkgs }:
+{ config, lib }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -28,7 +28,7 @@ let
 
 in rec {
 
-  unitOptions = {
+  sharedOptions = {
 
     enable = mkOption {
       default = true;
@@ -41,6 +41,37 @@ in rec {
       '';
     };
 
+    requiredBy = mkOption {
+      default = [];
+      type = types.listOf types.string;
+      description = "Units that require (i.e. depend on and need to go down with) this unit.";
+    };
+
+    wantedBy = mkOption {
+      default = [];
+      type = types.listOf types.string;
+      description = "Units that want (i.e. depend on) this unit.";
+    };
+
+  };
+
+  concreteUnitOptions = sharedOptions // {
+
+    text = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Text of this systemd unit.";
+    };
+
+    unit = mkOption {
+      internal = true;
+      description = "The generated unit.";
+    };
+
+  };
+
+  commonUnitOptions = sharedOptions // {
+
     description = mkOption {
       default = "";
       type = types.str;
@@ -109,18 +140,6 @@ in rec {
       '';
     };
 
-    requiredBy = mkOption {
-      default = [];
-      type = types.listOf types.str;
-      description = "Units that require (i.e. depend on and need to go down with) this unit.";
-    };
-
-    wantedBy = mkOption {
-      default = [];
-      type = types.listOf types.str;
-      description = "Units that want (i.e. depend on) this unit.";
-    };
-
     unitConfig = mkOption {
       default = {};
       example = { RequiresMountsFor = "/data"; };
@@ -135,6 +154,7 @@ in rec {
 
     restartTriggers = mkOption {
       default = [];
+      type = types.listOf types.unspecified;
       description = ''
         An arbitrary list of items such as derivations.  If any item
         in the list changes between reconfigurations, the service will
@@ -145,7 +165,7 @@ in rec {
   };
 
 
-  serviceOptions = unitOptions // {
+  serviceOptions = commonUnitOptions // {
 
     environment = mkOption {
       default = {};
@@ -236,6 +256,17 @@ in rec {
       '';
     };
 
+    reloadIfChanged = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether the service should be reloaded during a NixOS
+        configuration switch if its definition has changed.  If
+        enabled, the value of <option>restartIfChanged</option> is
+        ignored.
+      '';
+    };
+
     stopIfChanged = mkOption {
       type = types.bool;
       default = true;
@@ -268,7 +299,7 @@ in rec {
   };
 
 
-  socketOptions = unitOptions // {
+  socketOptions = commonUnitOptions // {
 
     listenStreams = mkOption {
       default = [];
@@ -295,7 +326,7 @@ in rec {
   };
 
 
-  timerOptions = unitOptions // {
+  timerOptions = commonUnitOptions // {
 
     timerConfig = mkOption {
       default = {};
@@ -314,7 +345,24 @@ in rec {
   };
 
 
-  mountOptions = unitOptions // {
+  pathOptions = commonUnitOptions // {
+
+    pathConfig = mkOption {
+      default = {};
+      example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
+      type = types.attrsOf unitOption;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Path]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.path</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
+
+  mountOptions = commonUnitOptions // {
 
     what = mkOption {
       example = "/dev/sda1";
@@ -358,7 +406,7 @@ in rec {
     };
   };
 
-  automountOptions = unitOptions // {
+  automountOptions = commonUnitOptions // {
 
     where = mkOption {
       example = "/mnt";
@@ -382,4 +430,6 @@ in rec {
     };
   };
 
+  targetOptions = commonUnitOptions;
+
 }
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index b575deb24b7b..6c6adab66e7c 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -1,8 +1,8 @@
-{ config, pkgs, utils, ... }:
+{ config, lib, pkgs, utils, ... }:
 
-with pkgs.lib;
+with lib;
 with utils;
-with import ./systemd-unit-options.nix { inherit config pkgs; };
+with import ./systemd-unit-options.nix { inherit config lib; };
 
 let
 
@@ -11,29 +11,26 @@ let
   systemd = cfg.package;
 
   makeUnit = name: unit:
-    pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
-      ((if !unit.enable then  ''
-        mkdir -p $out
-        ln -s /dev/null $out/${name}
-      '' else if unit.linkTarget != null then ''
-        mkdir -p $out
-        ln -s ${unit.linkTarget} $out/${name}
-      '' else if unit.text != null then ''
-        mkdir -p $out
-        echo -n "$text" > $out/${name}
-      '' else "") + optionalString (unit.extraConfig != {}) ''
-        mkdir -p $out/${name}.d
-        ${concatStringsSep "\n" (mapAttrsToList (n: v: "echo -n \"${v}\" > $out/${name}.d/${n}") unit.extraConfig)}
-      '');
-
-  upstreamUnits =
+    if unit.enable then
+      pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
+        ''
+          mkdir -p $out
+          echo -n "$text" > $out/${name}
+        ''
+    else
+      pkgs.runCommand "unit" { preferLocalBuild = true; }
+        ''
+          mkdir -p $out
+          ln -s /dev/null $out/${name}
+        '';
+
+  upstreamSystemUnits =
     [ # Targets.
       "basic.target"
       "sysinit.target"
       "sockets.target"
       "graphical.target"
       "multi-user.target"
-      "getty.target"
       "network.target"
       "network-online.target"
       "nss-lookup.target"
@@ -43,6 +40,7 @@ let
       "sigpwr.target"
       "timers.target"
       "paths.target"
+      "rpcbind.target"
 
       # Rescue mode.
       "rescue.target"
@@ -55,6 +53,13 @@ let
       "systemd-udev-settle.service"
       "systemd-udev-trigger.service"
 
+      # Consoles.
+      "getty.target"
+      "getty@.service"
+      "serial-getty@.service"
+      "container-getty@.service"
+      "systemd-vconsole-setup.service"
+
       # Hardware (started by udev when a relevant device is plugged in).
       "sound.target"
       "bluetooth.target"
@@ -67,12 +72,15 @@ let
       #"systemd-vconsole-setup.service"
       "systemd-user-sessions.service"
       "dbus-org.freedesktop.login1.service"
+      "dbus-org.freedesktop.machine1.service"
       "user@.service"
 
       # Journal.
       "systemd-journald.socket"
       "systemd-journald.service"
       "systemd-journal-flush.service"
+      "systemd-journal-gatewayd.socket"
+      "systemd-journal-gatewayd.service"
       "syslog.socket"
 
       # SysV init compatibility.
@@ -80,7 +88,8 @@ let
       "systemd-initctl.service"
 
       # Kernel module loading.
-      #"systemd-modules-load.service"
+      "systemd-modules-load.service"
+      "kmod-static-nodes.service"
 
       # Filesystems.
       "systemd-fsck@.service"
@@ -93,10 +102,16 @@ 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"
 
+      # Maintaining state across reboots.
+      "systemd-random-seed.service"
+      "systemd-backlight@.service"
+      "systemd-rfkill@.service"
+
       # Hibernate / suspend.
       "hibernate.target"
       "suspend.target"
@@ -121,12 +136,30 @@ let
       "final.target"
       "kexec.target"
       "systemd-kexec.service"
+      "systemd-update-utmp.service"
 
       # Password entry.
       "systemd-ask-password-console.path"
       "systemd-ask-password-console.service"
       "systemd-ask-password-wall.path"
       "systemd-ask-password-wall.service"
+
+      # Slices / containers.
+      "slices.target"
+      "-.slice"
+      "system.slice"
+      "user.slice"
+      "machine.slice"
+      "systemd-machined.service"
+
+      # Temporary file creation / cleanup.
+      "systemd-tmpfiles-clean.service"
+      "systemd-tmpfiles-clean.timer"
+      "systemd-tmpfiles-setup.service"
+      "systemd-tmpfiles-setup-dev.service"
+
+      # Misc.
+      "systemd-sysctl.service"
     ]
 
     ++ optionals cfg.enableEmergencyMode [
@@ -134,16 +167,26 @@ let
       "emergency.service"
     ];
 
-  upstreamWants =
+  upstreamSystemWants =
     [ #"basic.target.wants"
       "sysinit.target.wants"
       "sockets.target.wants"
       "local-fs.target.wants"
       "multi-user.target.wants"
-      "shutdown.target.wants"
       "timers.target.wants"
     ];
 
+  upstreamUserUnits =
+    [ "basic.target"
+      "default.target"
+      "exit.target"
+      "paths.target"
+      "shutdown.target"
+      "sockets.target"
+      "systemd-exit.service"
+      "timers.target"
+    ];
+
   makeJobScript = name: text:
     let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; };
     in "${x}/bin/${name}";
@@ -151,15 +194,23 @@ let
   unitConfig = { name, config, ... }: {
     config = {
       unitConfig =
-        { Requires = concatStringsSep " " config.requires;
-          Wants = concatStringsSep " " config.wants;
-          After = concatStringsSep " " config.after;
-          Before = concatStringsSep " " config.before;
-          BindsTo = concatStringsSep " " config.bindsTo;
-          PartOf = concatStringsSep " " config.partOf;
-          Conflicts = concatStringsSep " " config.conflicts;
-          "X-Restart-Triggers" = toString config.restartTriggers;
-        } // optionalAttrs (config.description != "") {
+        optionalAttrs (config.requires != [])
+          { Requires = toString config.requires; }
+        // optionalAttrs (config.wants != [])
+          { Wants = toString config.wants; }
+        // optionalAttrs (config.after != [])
+          { After = toString config.after; }
+        // optionalAttrs (config.before != [])
+          { Before = toString config.before; }
+        // optionalAttrs (config.bindsTo != [])
+          { BindsTo = toString config.bindsTo; }
+        // optionalAttrs (config.partOf != [])
+          { PartOf = toString config.partOf; }
+        // optionalAttrs (config.conflicts != [])
+          { Conflicts = toString config.conflicts; }
+        // optionalAttrs (config.restartTriggers != [])
+          { X-Restart-Triggers = toString config.restartTriggers; }
+        // optionalAttrs (config.description != "") {
           Description = config.description;
         };
     };
@@ -244,6 +295,11 @@ let
         (if isList value then value else [value]))
         as));
 
+  commonUnitText = def: ''
+      [Unit]
+      ${attrsToSection def.unitConfig}
+    '';
+
   targetToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
       text =
@@ -255,15 +311,16 @@ let
 
   serviceToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Service]
           ${let env = cfg.globalEnvironment // def.environment;
             in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
-          ${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"}
+          ${if def.reloadIfChanged then ''
+            X-ReloadIfChanged=true
+          '' else if !def.restartIfChanged then ''
+            X-RestartIfChanged=false
+          '' else ""}
           ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
           ${attrsToSection def.serviceConfig}
         '';
@@ -271,11 +328,8 @@ let
 
   socketToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Socket]
           ${attrsToSection def.socketConfig}
           ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
@@ -284,23 +338,26 @@ let
 
   timerToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Timer]
           ${attrsToSection def.timerConfig}
         '';
     };
 
-  mountToUnit = name: def:
+  pathToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
+          [Path]
+          ${attrsToSection def.pathConfig}
+        '';
+    };
 
+  mountToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text = commonUnitText def +
+        ''
           [Mount]
           ${attrsToSection def.mountConfig}
         '';
@@ -308,70 +365,99 @@ let
 
   automountToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Automount]
           ${attrsToSection def.automountConfig}
         '';
     };
 
-  units = pkgs.runCommand "units" { preferLocalBuild = true; }
-    ''
+  generateUnits = type: units: upstreamUnits: upstreamWants:
+    pkgs.runCommand "${type}-units" { preferLocalBuild = true; } ''
       mkdir -p $out
+
+      # Copy the upstream systemd units we're interested in.
       for i in ${toString upstreamUnits}; do
-        fn=${systemd}/example/systemd/system/$i
+        fn=${systemd}/example/systemd/${type}/$i
         if ! [ -e $fn ]; then echo "missing $fn"; false; fi
         if [ -L $fn ]; then
-          cp -pd $fn $out/
+          target="$(readlink "$fn")"
+          if [ ''${target:0:3} = ../ ]; then
+            ln -s "$(readlink -f "$fn")" $out/
+          else
+            cp -pd $fn $out/
+          fi
         else
           ln -s $fn $out/
         fi
       done
 
+      # Copy .wants links, but only those that point to units that
+      # we're interested in.
       for i in ${toString upstreamWants}; do
-        fn=${systemd}/example/systemd/system/$i
+        fn=${systemd}/example/systemd/${type}/$i
         if ! [ -e $fn ]; then echo "missing $fn"; false; fi
         x=$out/$(basename $fn)
         mkdir $x
         for i in $fn/*; do
           y=$x/$(basename $i)
           cp -pd $i $y
-          if ! [ -e $y ]; then rm -v $y; fi
+          if ! [ -e $y ]; then rm $y; fi
         done
       done
 
-      for i in ${toString (mapAttrsToList (n: v: v.unit) cfg.units)}; do
-        ln -fs $i/* $out/
+      # Symlink all units provided listed in systemd.packages.
+      for i in ${toString cfg.packages}; do
+        files=$(echo $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*)
+        if [ -n "$files" ]; then
+          ln -s $files $out/
+        fi
       done
 
-      for i in ${toString cfg.packages}; do
-        ln -s $i/etc/systemd/system/* $out/
+      # Symlink all units defined by systemd.units. If these are also
+      # provided by systemd or systemd.packages, then add them as
+      # <unit-name>.d/overrides.conf, which makes them extend the
+      # upstream unit.
+      for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
+        fn=$(basename $i/*)
+        if [ -e $out/$fn ]; then
+          if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
+            ln -sfn /dev/null $out/$fn
+          else
+            mkdir $out/$fn.d
+            ln -s $i/$fn $out/$fn.d/overrides.conf
+          fi
+       else
+          ln -fs $i/$fn $out/
+        fi
       done
 
+      # Created .wants and .requires symlinks from the wantedBy and
+      # requiredBy options.
       ${concatStrings (mapAttrsToList (name: unit:
           concatMapStrings (name2: ''
             mkdir -p $out/'${name2}.wants'
             ln -sfn '../${name}' $out/'${name2}.wants'/
-          '') unit.wantedBy) cfg.units)}
+          '') unit.wantedBy) units)}
 
       ${concatStrings (mapAttrsToList (name: unit:
           concatMapStrings (name2: ''
             mkdir -p $out/'${name2}.requires'
             ln -sfn '../${name}' $out/'${name2}.requires'/
-          '') unit.requiredBy) cfg.units)}
+          '') unit.requiredBy) units)}
 
-      ln -s ${cfg.defaultUnit} $out/default.target
+      ${optionalString (type == "system") ''
+        # Stupid misc. symlinks.
+        ln -s ${cfg.defaultUnit} $out/default.target
 
-      ln -s rescue.target $out/kbrequest.target
+        ln -s rescue.target $out/kbrequest.target
 
-      mkdir -p $out/getty.target.wants/
-      ln -s ../autovt@tty1.service $out/getty.target.wants/
+        mkdir -p $out/getty.target.wants/
+        ln -s ../autovt@tty1.service $out/getty.target.wants/
 
-      ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
-            ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
+        ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
+              ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
+      ''}
     ''; # */
 
 in
@@ -393,55 +479,9 @@ in
       default = {};
       type = types.attrsOf types.optionSet;
       options = { name, config, ... }:
-        { options = {
-            text = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              description = "Text of this systemd unit.";
-            };
-            enable = mkOption {
-              default = true;
-              type = types.bool;
-              description = ''
-                If set to false, this unit will be a symlink to
-                /dev/null. This is primarily useful to prevent specific
-                template instances (e.g. <literal>serial-getty@ttyS0</literal>)
-                from being started.
-              '';
-            };
-            requiredBy = mkOption {
-              default = [];
-              type = types.listOf types.string;
-              description = "Units that require (i.e. depend on and need to go down with) this unit.";
-            };
-            wantedBy = mkOption {
-              default = [];
-              type = types.listOf types.string;
-              description = "Units that want (i.e. depend on) this unit.";
-            };
-            unit = mkOption {
-              internal = true;
-              description = "The generated unit.";
-            };
-            linkTarget = mkOption {
-              default = null;
-              description = "The file to symlink this target to.";
-              type = types.nullOr types.path;
-            };
-            extraConfig = mkOption {
-              default = {};
-              example = { "foo@1.conf" = "X-RestartIfChanged=false"; };
-              type = types.attrsOf types.lines;
-              description = ''
-                Extra files to be appended to the configuration for the unit.
-                This can be used to override configuration for a unit provided
-                by systemd or another package, or to override only a single instance
-                of a template unit.
-              '';
-            };
-          };
+        { options = concreteUnitOptions;
           config = {
-            unit = makeUnit name config;
+            unit = mkDefault (makeUnit name config);
           };
         };
     };
@@ -455,7 +495,7 @@ in
     systemd.targets = mkOption {
       default = {};
       type = types.attrsOf types.optionSet;
-      options = [ unitOptions unitConfig ];
+      options = [ targetOptions unitConfig ];
       description = "Definition of systemd target units.";
     };
 
@@ -480,6 +520,13 @@ in
       description = "Definition of systemd timer units.";
     };
 
+    systemd.paths = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ pathOptions unitConfig ];
+      description = "Definition of systemd path units.";
+    };
+
     systemd.mounts = mkOption {
       default = [];
       type = types.listOf types.optionSet;
@@ -567,6 +614,14 @@ in
       '';
     };
 
+    services.journald.enableHttpGateway = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable the HTTP gateway to the journal.
+      '';
+    };
+
     services.logind.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -590,6 +645,41 @@ in
       '';
     };
 
+    systemd.tmpfiles.rules = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "d /tmp 1777 root root 10d" ];
+      description = ''
+        Rules for creating and cleaning up temporary files
+        automatically. See
+        <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for the exact format. You should not use this option to create
+        files required by systemd services, since there is no
+        guarantee that <command>systemd-tmpfiles</command> runs when
+        the system is reconfigured using
+        <command>nixos-rebuild</command>.
+      '';
+    };
+
+    systemd.user.units = mkOption {
+      description = "Definition of systemd per-user units.";
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = { name, config, ... }:
+        { options = concreteUnitOptions;
+          config = {
+            unit = mkDefault (makeUnit name config);
+          };
+        };
+    };
+
+    systemd.user.services = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ serviceOptions unitConfig serviceConfig ];
+      description = "Definition of systemd per-user service units.";
+    };
+
   };
 
 
@@ -597,11 +687,20 @@ in
 
   config = {
 
-    system.build.units = units;
+    assertions = mapAttrsToList (name: service: {
+      assertion = service.serviceConfig.Type or "" == "oneshot" -> service.serviceConfig.Restart or "no" == "no";
+      message = "${name}: Type=oneshot services must have Restart=no";
+    }) cfg.services;
+
+    system.build.units = cfg.units;
 
     environment.systemPackages = [ systemd ];
 
-    environment.etc."systemd/system".source = units;
+    environment.etc."systemd/system".source =
+      generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
+
+    environment.etc."systemd/user".source =
+      generateUnits "user" cfg.user.units upstreamUserUnits [];
 
     environment.etc."systemd/system.conf".text =
       ''
@@ -645,8 +744,11 @@ in
       '';
 
     # Target for ‘charon send-keys’ to hook into.
+    users.extraGroups.keys.gid = config.ids.gids.keys;
+
     systemd.targets.keys =
       { description = "Security Keys";
+        unitConfig.X-StopOnReconfiguration = true;
       };
 
     systemd.units =
@@ -654,6 +756,7 @@ in
       // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
       // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
       // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
+      // mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
       // listToAttrs (map
                    (v: let n = escapeSystemdPath v.where;
                        in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
@@ -661,6 +764,9 @@ in
                    (v: let n = escapeSystemdPath v.where;
                        in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
 
+    systemd.user.units =
+      mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services;
+
     system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [
       "CGROUPS" "AUTOFS4_FS" "DEVTMPFS"
     ];
@@ -673,6 +779,8 @@ in
       };
 
     users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
+    users.extraUsers.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
+    users.extraGroups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
 
     # Generate timer units for all services that have a ‘startAt’ value.
     systemd.timers =
@@ -682,43 +790,25 @@ in
         })
         (filterAttrs (name: service: service.startAt != "") cfg.services);
 
-    # FIXME: These are borrowed from upstream systemd.
-    systemd.services."systemd-update-utmp" =
-      { description = "Update UTMP about System Reboot/Shutdown";
-        wantedBy = [ "sysinit.target" ];
-        after = [ "systemd-remount-fs.service" ];
-        before = [ "sysinit.target" "shutdown.target" ];
-        conflicts = [ "shutdown.target" ];
-        unitConfig = {
-          DefaultDependencies = false;
-          RequiresMountsFor = "/var/log";
-        };
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${systemd}/lib/systemd/systemd-update-utmp reboot";
-          ExecStop = "${systemd}/lib/systemd/systemd-update-utmp shutdown";
-        };
-        restartIfChanged = false;
-      };
+    systemd.sockets.systemd-journal-gatewayd.wantedBy =
+      optional config.services.journald.enableHttpGateway "sockets.target";
 
-    systemd.services."systemd-random-seed" =
-      { description = "Load/Save Random Seed";
-        wantedBy = [ "sysinit.target" "multi-user.target" ];
-        after = [ "systemd-remount-fs.service" ];
-        before = [ "sysinit.target" "shutdown.target" ];
-        conflicts = [ "shutdown.target" ];
-        unitConfig = {
-          DefaultDependencies = false;
-          RequiresMountsFor = "/var/lib";
-        };
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${systemd}/lib/systemd/systemd-random-seed load";
-          ExecStop = "${systemd}/lib/systemd/systemd-random-seed save";
-        };
+    # Provide the systemd-user PAM service, required to run systemd
+    # user instances.
+    security.pam.services.systemd-user =
+      { # Ensure that pam_systemd gets included. This is special-cased
+        # in systemd to provide XDG_RUNTIME_DIR.
+        startSession = true;
       };
 
+    environment.etc."tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
+
+    environment.etc."tmpfiles.d/nixos.conf".text =
+      ''
+        # This file is created automatically and should not be modified.
+        # Please change the option ‘systemd.tmpfiles.rules’ instead.
+        ${concatStringsSep "\n" cfg.tmpfiles.rules}
+      '';
+
   };
 }
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index a8f0a59b6fa9..22d55a9e246c 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -1,8 +1,8 @@
 # Management of static files in /etc.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -19,6 +19,8 @@ let
     sources = map (x: x.source) etc';
     targets = map (x: x.target) etc';
     modes = map (x: x.mode) etc';
+    uids  = map (x: x.uid) etc';
+    gids  = map (x: x.gid) etc';
   };
 
 in
@@ -87,6 +89,24 @@ in
               '';
             };
 
+            uid = mkOption {
+              default = 0;
+              type = types.int;
+              description = ''
+                UID of created file. Only takes affect when the file is
+                copied (that is, the mode is not 'symlink').
+                '';
+            };
+
+            gid = mkOption {
+              default = 0;
+              type = types.int;
+              description = ''
+                GID of created file. Only takes affect when the file is
+                copied (that is, the mode is not 'symlink').
+              '';
+            };
+
           };
 
           config = {
diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh
index 7cf68db9ddce..60d4ba1301a3 100644
--- a/nixos/modules/system/etc/make-etc.sh
+++ b/nixos/modules/system/etc/make-etc.sh
@@ -6,6 +6,8 @@ set -f
 sources_=($sources)
 targets_=($targets)
 modes_=($modes)
+uids_=($uids)
+gids_=($gids)
 set +f
 
 for ((i = 0; i < ${#targets_[@]}; i++)); do
@@ -35,6 +37,8 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
         
         if test "${modes_[$i]}" != symlink; then
             echo "${modes_[$i]}" > $out/etc/$target.mode
+            echo "${uids_[$i]}" > $out/etc/$target.uid
+            echo "${gids_[$i]}" > $out/etc/$target.gid
         fi
         
     fi
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
index 4b79dbaab89e..8ba9a370b27a 100644
--- a/nixos/modules/system/etc/setup-etc.pl
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -60,7 +60,15 @@ sub link {
         if ($mode eq "direct-symlink") {
             atomicSymlink readlink("$static/$fn"), $target or warn;
         } else {
+            open UID, "<$_.uid";
+            my $uid = <UID>; chomp $uid;
+            close UID;
+            open GID, "<$_.gid";
+            my $gid = <GID>; chomp $gid;
+            close GID;
+
             copy "$static/$fn", "$target.tmp" or warn;
+            chown int($uid), int($gid), "$target.tmp" or warn;
             chmod oct($mode), "$target.tmp" or warn;
             rename "$target.tmp", $target or warn;
         }
diff --git a/nixos/modules/system/upstart/upstart.nix b/nixos/modules/system/upstart/upstart.nix
index aa5c8dfe64b2..5c0461304072 100644
--- a/nixos/modules/system/upstart/upstart.nix
+++ b/nixos/modules/system/upstart/upstart.nix
@@ -1,7 +1,7 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
-with import ../boot/systemd-unit-options.nix { inherit config pkgs; };
+with lib;
+with import ../boot/systemd-unit-options.nix { inherit config lib; };
 
 let
 
@@ -93,7 +93,7 @@ let
             if job.daemonType == "fork" || job.daemonType == "daemon" then { Type = "forking"; GuessMainPID = true; } else
             if job.daemonType == "none" then { } else
             throw "invalid daemon type `${job.daemonType}'")
-        // optionalAttrs (!job.task && job.respawn)
+        // optionalAttrs (!job.task && !(job.script == "" && job.exec == "") && job.respawn)
           { Restart = "always"; }
         // optionalAttrs job.task
           { Type = "oneshot"; RemainAfterExit = false; };
diff --git a/nixos/modules/tasks/cpu-freq.nix b/nixos/modules/tasks/cpu-freq.nix
index 705ec93a1360..eb1dfe5f6be2 100644
--- a/nixos/modules/tasks/cpu-freq.nix
+++ b/nixos/modules/tasks/cpu-freq.nix
@@ -1,6 +1,11 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
+
+let
+  cpupower = config.boot.kernelPackages.cpupower;
+  cfg = config.powerManagement;
+in
 
 {
   ###### interface
@@ -23,31 +28,28 @@ with pkgs.lib;
 
   ###### implementation
 
-  config = mkIf (config.powerManagement.cpuFreqGovernor != null) {
-
-    environment.systemPackages = [ pkgs.cpufrequtils ];
-
-    jobs.cpufreq =
-      { description = "CPU Frequency Governor Setup";
-
-        after = [ "systemd-modules-load.service" ];
-        wantedBy = [ "multi-user.target" ];
+  config = mkIf (!config.boot.isContainer && config.powerManagement.cpuFreqGovernor != null) {
 
-        unitConfig.ConditionPathIsReadWrite = "/sys/devices/";
+    boot.kernelModules = [ "acpi-cpufreq" "speedstep-lib" "pcc-cpufreq"
+      "cpufreq_${cfg.cpuFreqGovernor}"
+    ];
 
-        path = [ pkgs.cpufrequtils ];
+    environment.systemPackages = [ cpupower ];
 
-        preStart = ''
-          for i in $(seq 0 $(($(nproc) - 1))); do
-            for gov in $(cpufreq-info -c $i -g); do
-              if [ "$gov" = ${config.powerManagement.cpuFreqGovernor} ]; then
-                echo "<6>setting governor on CPU $i to ‘$gov’"
-                cpufreq-set -c $i -g $gov
-              fi
-            done
-          done
-        '';
+    systemd.services.cpufreq = {
+      description = "CPU Frequency Governor Setup";
+      after = [ "systemd-modules-load.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ cpupower ];
+      script = ''
+        cpupower frequency-set -g ${cfg.cpuFreqGovernor}
+      '';
+      unitConfig.ConditionVirtualization = false;
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = "yes";
       };
-  };
+    };
 
+  };
 }
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index b0bcd2eb373e..954d0b0781f7 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, utils, ... }:
+{ config, lib, pkgs, utils, ... }:
 
-with pkgs.lib;
+with lib;
 with utils;
 
 let
@@ -148,7 +148,7 @@ in
     system.fsPackages = [ pkgs.dosfstools ];
 
     environment.systemPackages =
-      [ pkgs.ntfs3g pkgs.cifs_utils ]
+      [ pkgs.ntfs3g pkgs.cifs_utils pkgs.fuse ]
       ++ config.system.fsPackages;
 
     environment.etc.fstab.text =
diff --git a/nixos/modules/tasks/filesystems/btrfs.nix b/nixos/modules/tasks/filesystems/btrfs.nix
index d95a32e2e3f7..d0a2ac645e0b 100644
--- a/nixos/modules/tasks/filesystems/btrfs.nix
+++ b/nixos/modules/tasks/filesystems/btrfs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index 2b720a93b893..e8c3d8ab56d5 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/tasks/filesystems/reiserfs.nix b/nixos/modules/tasks/filesystems/reiserfs.nix
index f8c6a7000040..a3bfb3fed8ef 100644
--- a/nixos/modules/tasks/filesystems/reiserfs.nix
+++ b/nixos/modules/tasks/filesystems/reiserfs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/tasks/filesystems/vfat.nix b/nixos/modules/tasks/filesystems/vfat.nix
index 5ca72f142b7d..4cfe6e208f7e 100644
--- a/nixos/modules/tasks/filesystems/vfat.nix
+++ b/nixos/modules/tasks/filesystems/vfat.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/tasks/filesystems/xfs.nix b/nixos/modules/tasks/filesystems/xfs.nix
index 6800696a05a3..5225b62a88c5 100644
--- a/nixos/modules/tasks/filesystems/xfs.nix
+++ b/nixos/modules/tasks/filesystems/xfs.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 7c3c662eeac9..1d75a24692c0 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 #
 # todo:
 #   - crontab for scrubs, etc
@@ -6,23 +6,35 @@
 #   - /etc/zfs/zpool.cache handling
 
 
-with pkgs.lib;
+with lib;
 
 let
 
   cfgSpl = config.boot.spl;
+  cfgSnapshots = config.services.zfs.autoSnapshot;
+
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
+
+  enableAutoSnapshots = cfgSnapshots.enable;
+  enableZfs = inInitrd || inSystem || enableAutoSnapshots;
+
   kernel = config.boot.kernelPackages;
 
+  autosnapPkg = pkgs.zfstools.override {
+    zfs = config.boot.kernelPackages.zfs;
+  };
+
+  zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
+
 in
 
 {
 
   ###### interface
-  
-  options = { 
-    boot.spl.hostid = mkOption { 
+
+  options = {
+    boot.spl.hostid = mkOption {
       default = "";
       example = "0xdeadbeef";
       description = ''
@@ -34,62 +46,177 @@ in
         manually import pools.
       '';
     };
-  };
 
-  ###### implementation
+    services.zfs.autoSnapshot = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable the (OpenSolaris-compatible) ZFS auto-snapshotting service.
+          Note that you must set the <literal>com.sun:auto-snapshot</literal>
+          property to <literal>true</literal> on all datasets which you wish
+          to auto-snapshot.
 
-  config = mkIf ( inInitrd || inSystem ) {
+          You can override a child dataset to use, or not use auto-snapshotting
+          by setting its flag with the given interval:
+          <literal>zfs set com.sun:auto-snapshot:weekly=false DATASET</literal>
+        '';
+      };
 
-    boot = { 
-      kernelModules = [ "spl" "zfs" ] ;
-      extraModulePackages = [ kernel.zfs kernel.spl ];
-      extraModprobeConfig = mkIf (cfgSpl.hostid != "") ''
-        options spl spl_hostid=${cfgSpl.hostid}
-      '';
-    };
+      frequent = mkOption {
+        default = 4;
+        type = types.int;
+        description = ''
+          Number of frequent (15-minute) auto-snapshots that you wish to keep.
+        '';
+      };
+
+      hourly = mkOption {
+        default = 24;
+        type = types.int;
+        description = ''
+          Number of hourly auto-snapshots that you wish to keep.
+        '';
+      };
+
+      daily = mkOption {
+        default = 7;
+        type = types.int;
+        description = ''
+          Number of daily auto-snapshots that you wish to keep.
+        '';
+      };
 
-    boot.initrd = mkIf inInitrd { 
-      kernelModules = [ "spl" "zfs" ] ;
-      extraUtilsCommands =
-        ''
-          cp -v ${kernel.zfs}/sbin/zfs $out/bin
-          cp -v ${kernel.zfs}/sbin/zdb $out/bin
-          cp -v ${kernel.zfs}/sbin/zpool $out/bin
-          cp -pdv ${kernel.zfs}/lib/lib*.so* $out/lib
-          cp -pdv ${pkgs.zlib}/lib/lib*.so* $out/lib
+      weekly = mkOption {
+        default = 4;
+        type = types.int;
+        description = ''
+          Number of weekly auto-snapshots that you wish to keep.
         '';
-      postDeviceCommands =
-        ''
-          zpool import -f -a -d /dev
+      };
+
+      monthly = mkOption {
+        default = 12;
+        type = types.int;
+        description = ''
+          Number of monthly auto-snapshots that you wish to keep.
         '';
+      };
     };
+  };
 
-    systemd.services."zpool-import" = {
-      description = "Import zpools";
-      after = [ "systemd-udev-settle.service" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
+  ###### implementation
+
+  config = mkMerge [
+    (mkIf enableZfs {
+      boot = {
+        kernelModules = [ "spl" "zfs" ] ;
+        extraModulePackages = [ kernel.zfs kernel.spl ];
+        extraModprobeConfig = mkIf (cfgSpl.hostid != "") ''
+          options spl spl_hostid=${cfgSpl.hostid}
+        '';
+      };
+
+      boot.initrd = mkIf inInitrd {
+        kernelModules = [ "spl" "zfs" ] ;
+        extraUtilsCommands =
+          ''
+            cp -v ${kernel.zfs}/sbin/zfs $out/bin
+            cp -v ${kernel.zfs}/sbin/zdb $out/bin
+            cp -v ${kernel.zfs}/sbin/zpool $out/bin
+            cp -pdv ${kernel.zfs}/lib/lib*.so* $out/lib
+            cp -pdv ${pkgs.zlib}/lib/lib*.so* $out/lib
+          '';
+        postDeviceCommands =
+          ''
+            zpool import -f -a
+          '';
+      };
+
+      systemd.services."zpool-import" = {
+        description = "Import zpools";
+        after = [ "systemd-udev-settle.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${kernel.zfs}/sbin/zpool import -f -a";
+        };
         restartIfChanged = false;
-        ExecStart = "${kernel.zfs}/sbin/zpool import -f -a -d /dev";
       };
-    };
 
-    systemd.services."zfs-mount" = {
-      description = "Mount ZFS Volumes";
-      after = [ "zpool-import.service" ];
-      wantedBy = [ "local-fs.target" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
+      systemd.services."zfs-mount" = {
+        description = "Mount ZFS Volumes";
+        after = [ "zpool-import.service" ];
+        wantedBy = [ "local-fs.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${kernel.zfs}/sbin/zfs mount -a";
+          ExecStop = "${kernel.zfs}/sbin/zfs umount -a";
+        };
         restartIfChanged = false;
-        ExecStart = "${kernel.zfs}/sbin/zfs mount -a";
-        ExecStop = "${kernel.zfs}/sbin/zfs umount -a";
       };
-    };
 
-    system.fsPackages = [ kernel.zfs ];                  # XXX: needed? zfs doesn't have (need) a fsck
-    environment.systemPackages = [ kernel.zfs ];
-    services.udev.packages = [ kernel.zfs ];             # to hook zvol naming, etc. 
-  };
+      system.fsPackages = [ kernel.zfs ];                  # XXX: needed? zfs doesn't have (need) a fsck
+      environment.systemPackages = [ kernel.zfs ];
+      services.udev.packages = [ kernel.zfs ];             # to hook zvol naming, etc.
+    })
+
+    (mkIf enableAutoSnapshots {
+      systemd.services."zfs-snapshot-frequent" = {
+        description = "ZFS auto-snapshotting every 15 mins";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} frequent ${toString cfgSnapshots.frequent}";
+        };
+        restartIfChanged = false;
+        startAt = "*:15,30,45";
+      };
+
+      systemd.services."zfs-snapshot-hourly" = {
+        description = "ZFS auto-snapshotting every hour";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} hourly ${toString cfgSnapshots.hourly}";
+        };
+        restartIfChanged = false;
+        startAt = "hourly";
+      };
+
+      systemd.services."zfs-snapshot-daily" = {
+        description = "ZFS auto-snapshotting every day";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} daily ${toString cfgSnapshots.daily}";
+        };
+        restartIfChanged = false;
+        startAt = "daily";
+      };
+
+      systemd.services."zfs-snapshot-weekly" = {
+        description = "ZFS auto-snapshotting every week";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} weekly ${toString cfgSnapshots.weekly}";
+        };
+        restartIfChanged = false;
+        startAt = "weekly";
+      };
+
+      systemd.services."zfs-snapshot-monthly" = {
+        description = "ZFS auto-snapshotting every month";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} monthly ${toString cfgSnapshots.monthly}";
+        };
+        restartIfChanged = false;
+        startAt = "monthly";
+      };
+    })
+  ];
 }
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
index 1083fb784fc0..03c42404e5d5 100644
--- a/nixos/modules/tasks/kbd.nix
+++ b/nixos/modules/tasks/kbd.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -52,19 +52,7 @@ in
     # /dev/tty0 to prevent putting the X server in non-raw mode, and
     # it has a restart trigger.
     systemd.services."systemd-vconsole-setup" =
-      { description = "Setup Virtual Console";
-        wantedBy = [ "sysinit.target" "multi-user.target" ];
-        before = [ "sysinit.target" "shutdown.target" ];
-        conflicts = [ "shutdown.target" ];
-        unitConfig =
-          { DefaultDependencies = "no";
-            ConditionPathExists = "/dev/tty1";
-          };
-        serviceConfig =
-          { Type = "oneshot";
-            RemainAfterExit = true;
-            ExecStart = "${config.systemd.package}/lib/systemd/systemd-vconsole-setup /dev/tty1";
-          };
+      { wantedBy = [ "multi-user.target" ];
         restartTriggers = [ vconsoleConf ];
       };
 
diff --git a/nixos/modules/tasks/lvm.nix b/nixos/modules/tasks/lvm.nix
index 0e0272388c76..d56a8a2f63a8 100644
--- a/nixos/modules/tasks/lvm.nix
+++ b/nixos/modules/tasks/lvm.nix
@@ -1,10 +1,12 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
+
+with lib;
 
 {
 
   ###### implementation
 
-  config = {
+  config = mkIf (!config.boot.isContainer) {
 
     environment.systemPackages = [ pkgs.lvm2 ];
 
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 9619f0f5ebe7..9cc8b154324f 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -50,6 +50,26 @@ let
         '';
       };
 
+      ipv6Address = mkOption {
+        default = null;
+        example = "2001:1470:fffd:2098::e006";
+        type = types.nullOr types.string;
+        description = ''
+          IPv6 address of the interface.  Leave empty to configure the
+          interface using NDP.
+        '';
+      };
+
+      ipv6prefixLength = mkOption {
+        default = 64;
+        example = 64;
+        type = types.int;
+        description = ''
+          Subnet mask of the interface, specified as the number of
+          bits in the prefix (<literal>64</literal>).
+        '';
+      };
+
       macAddress = mkOption {
         default = null;
         example = "00:11:22:33:44:55";
@@ -171,6 +191,15 @@ in
       '';
     };
 
+    networking.useHostResolvConf = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        In containers, whether to use the
+        <filename>resolv.conf</filename> supplied by the host.
+      '';
+    };
+
     networking.localCommands = mkOption {
       default = "";
       example = "text=anything; echo You can put $text here.";
@@ -401,9 +430,11 @@ in
                 EOF
 
                 # Disable or enable IPv6.
-                if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
-                  echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
-                fi
+                ${optionalString (!config.boot.isContainer) ''
+                  if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
+                    echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
+                  fi
+                ''}
 
                 # Set the default gateway.
                 ${optionalString (cfg.defaultGateway != "") ''
@@ -435,6 +466,7 @@ in
           (let mask =
                 if i.prefixLength != null then toString i.prefixLength else
                 if i.subnetMask != "" then i.subnetMask else "32";
+               staticIPv6 = cfg.enableIPv6 && i.ipv6Address != null;
           in
           { description = "Configuration of ${i.name}";
             wantedBy = [ "network-interfaces.target" ];
@@ -468,11 +500,31 @@ in
                     echo "configuring interface..."
                     ip -4 addr flush dev "${i.name}"
                     ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (staticIPv6)
+                ''
+                  # Only do a flush/add if it's necessary.  This is
+                  # useful when the Nix store is accessed via this
+                  # interface (e.g. in a QEMU VM test).
+                  if ! ip -6 -o a show dev "${i.name}" | grep "${i.ipv6Address}/${toString i.ipv6prefixLength}"; then
+                    echo "configuring interface..."
+                    ip -6 addr flush dev "${i.name}"
+                    ip -6 addr add "${i.ipv6Address}/${toString i.ipv6prefixLength}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (i.ipAddress != null || staticIPv6)
+                ''
+                  if [ restart_network_setup = true ]; then
                     # Ensure that the default gateway remains set.
                     # (Flushing this interface may have removed it.)
                     ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  else
-                    echo "skipping configuring interface"
                   fi
                   ${config.systemd.package}/bin/systemctl start ip-up.target
                 ''
diff --git a/nixos/modules/tasks/scsi-link-power-management.nix b/nixos/modules/tasks/scsi-link-power-management.nix
index 071a80865983..a74023dec21a 100644
--- a/nixos/modules/tasks/scsi-link-power-management.nix
+++ b/nixos/modules/tasks/scsi-link-power-management.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   ###### interface
diff --git a/nixos/modules/testing/minimal-kernel.nix b/nixos/modules/testing/minimal-kernel.nix
index 0418de800c8d..0cbca71e1326 100644
--- a/nixos/modules/testing/minimal-kernel.nix
+++ b/nixos/modules/testing/minimal-kernel.nix
@@ -5,7 +5,7 @@ let
     (map (builtins.getAttr "configLine") config.system.requiredKernelConfig))
   );
 
-  origKernel = pkgs.linuxManualConfig {
+  origKernel = pkgs.buildLinux {
     inherit (pkgs.linux) src version;
     inherit configfile;
     allowImportFromDerivation = true;
diff --git a/nixos/modules/testing/service-runner.nix b/nixos/modules/testing/service-runner.nix
index 6f17ed77dad9..dfe8b430e045 100644
--- a/nixos/modules/testing/service-runner.nix
+++ b/nixos/modules/testing/service-runner.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 28494e1c7b2a..9100a433cd63 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -1,9 +1,9 @@
 # This module allows the test driver to connect to the virtual machine
 # via a root shell attached to port 514.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let kernel = config.boot.kernelPackages.kernel; in
 
@@ -86,6 +86,8 @@ let kernel = config.boot.kernelPackages.kernel; in
       (isEnabled "VIRTIO_CONSOLE")
     ];
 
+    networking.usePredictableInterfaceNames = false;
+
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index abd2a1084bd9..18b18dd4b230 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports = [ ../profiles/headless.nix ./ec2-data.nix ];
@@ -164,5 +164,5 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  This doesn't really matter,
   # since the only PAM services that allow logging in with a null
   # password are local ones that are inaccessible on EC2 machines.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 }
diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix
new file mode 100644
index 000000000000..195a8056bf82
--- /dev/null
+++ b/nixos/modules/virtualisation/container-config.nix
@@ -0,0 +1,91 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  config = mkIf config.boot.isContainer {
+
+    # Disable some features that are not useful in a container.
+    sound.enable = mkDefault false;
+    services.udisks2.enable = mkDefault false;
+
+    networking.useHostResolvConf = true;
+
+    # Shut up warnings about not having a boot loader.
+    system.build.installBootLoader = "${pkgs.coreutils}/bin/true";
+
+    # Provide a root login prompt on /var/lib/root-login.socket that
+    # doesn't ask for a password. This socket can only be used by root
+    # on the host.
+    systemd.sockets.root-login =
+      { description = "Root Login Socket";
+        wantedBy = [ "sockets.target" ];
+        socketConfig =
+          { ListenStream = "/var/lib/root-login.socket";
+            SocketMode = "0600";
+            Accept = true;
+          };
+      };
+
+    systemd.services."root-login@" =
+      { description = "Root Login %i";
+        environment.TERM = "linux";
+        serviceConfig =
+          { Type = "simple";
+            StandardInput = "socket";
+            ExecStart = "${pkgs.socat}/bin/socat -t0 - \"exec:${pkgs.shadow}/bin/login -f root,pty,setsid,setpgid,stderr,ctty\"";
+            TimeoutStopSec = 1; # FIXME
+          };
+        restartIfChanged = false;
+      };
+
+    # Provide a daemon on /var/lib/run-command.socket that reads a
+    # command from stdin and executes it.
+    systemd.sockets.run-command =
+      { description = "Run Command Socket";
+        wantedBy = [ "sockets.target" ];
+        socketConfig =
+          { ListenStream = "/var/lib/run-command.socket";
+            SocketMode = "0600";  # only root can connect
+            Accept = true;
+          };
+      };
+
+    systemd.services."run-command@" =
+      { description = "Run Command %i";
+        environment.TERM = "linux";
+        serviceConfig =
+          { Type = "simple";
+            StandardInput = "socket";
+            TimeoutStopSec = 1; # FIXME
+          };
+        script =
+          ''
+            #! ${pkgs.stdenv.shell} -e
+            source /etc/bashrc
+            read c
+            eval "command=($c)"
+            exec "''${command[@]}"
+          '';
+        restartIfChanged = false;
+      };
+
+    systemd.services.container-startup-done =
+      { description = "Container Startup Notification";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "multi-user.target" ];
+        script =
+          ''
+            if [ -p /var/lib/startup-done ]; then
+              echo done > /var/lib/startup-done
+            fi
+          '';
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+        restartIfChanged = false;
+      };
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index bcbfaacd703f..4fca872d72eb 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -1,6 +1,31 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
+
+let
+
+  runInNetns = pkgs.stdenv.mkDerivation {
+    name = "run-in-netns";
+    unpackPhase = "true";
+    buildPhase = ''
+      mkdir -p $out/bin
+      gcc ${./run-in-netns.c} -o $out/bin/run-in-netns
+    '';
+    installPhase = "true";
+  };
+
+  nixos-container = pkgs.substituteAll {
+    name = "nixos-container";
+    dir = "bin";
+    isExecutable = true;
+    src = ./nixos-container.pl;
+    perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
+    inherit (pkgs) socat;
+  };
+
+  system = config.nixpkgs.system;
+
+in
 
 {
   options = {
@@ -14,19 +39,12 @@ with pkgs.lib;
       '';
     };
 
-    systemd.containers = mkOption {
+    containers = mkOption {
       type = types.attrsOf (types.submodule (
         { config, options, name, ... }:
         {
           options = {
 
-            root = mkOption {
-              type = types.path;
-              description = ''
-                The root directory of the container.
-              '';
-            };
-
             config = mkOption {
               description = ''
                 A specification of the desired configuration of this
@@ -45,21 +63,54 @@ with pkgs.lib;
               '';
             };
 
+            privateNetwork = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether to give the container its own private virtual
+                Ethernet interface.  The interface is called
+                <literal>eth0</literal>, and is hooked up to the interface
+                <literal>c-<replaceable>container-name</replaceable></literal>
+                on the host.  If this option is not set, then the
+                container shares the network interfaces of the host,
+                and can bind to any port on any interface.
+              '';
+            };
+
+            hostAddress = mkOption {
+              type = types.nullOr types.string;
+              default = null;
+              example = "10.231.136.1";
+              description = ''
+                The IPv4 address assigned to the host interface.
+              '';
+            };
+
+            localAddress = mkOption {
+              type = types.nullOr types.string;
+              default = null;
+              example = "10.231.136.2";
+              description = ''
+                The IPv4 address assigned to <literal>eth0</literal>
+                in the container.
+              '';
+            };
+
           };
 
           config = mkMerge
-            [ { root = mkDefault "/var/lib/containers/${name}";
-              }
-              (mkIf options.config.isDefined {
+            [ (mkIf options.config.isDefined {
                 path = (import ../../lib/eval-config.nix {
+                  inherit system;
                   modules =
                     let extraConfig =
                       { boot.isContainer = true;
-                        security.initialRootPassword = "!";
+                        security.initialRootPassword = mkDefault "!";
                         networking.hostName = mkDefault name;
+                        networking.useDHCP = false;
                       };
                     in [ extraConfig config.config ];
-                  prefix = [ "systemd" "containers" name ];
+                  prefix = [ "containers" name ];
                 }).config.system.build.toplevel;
               })
             ];
@@ -69,12 +120,10 @@ with pkgs.lib;
       example = literalExample
         ''
           { webserver =
-              { root = "/containers/webserver";
-                path = "/nix/var/nix/profiles/webserver";
+              { path = "/nix/var/nix/profiles/webserver";
               };
             database =
-              { root = "/containers/database";
-                config =
+              { config =
                   { config, pkgs, ... }:
                   { services.postgresql.enable = true;
                     services.postgresql.package = pkgs.postgresql92;
@@ -94,44 +143,151 @@ with pkgs.lib;
   };
 
 
-  config = {
+  config = mkIf (!config.boot.isContainer) {
+
+    systemd.services."container@" =
+      { description = "Container '%i'";
 
-    systemd.services = mapAttrs' (name: container: nameValuePair "container-${name}"
-      { description = "Container '${name}'";
+        unitConfig.RequiresMountsFor = [ "/var/lib/containers/%i" ];
 
-        wantedBy = [ "multi-user.target" ];
+        path = [ pkgs.iproute ];
 
-        unitConfig.RequiresMountsFor = [ container.root ];
+        environment.INSTANCE = "%i";
+        environment.root = "/var/lib/containers/%i";
 
         preStart =
           ''
-            mkdir -p -m 0755 ${container.root}/etc
-            if ! [ -e ${container.root}/etc/os-release ]; then
-              touch ${container.root}/etc/os-release
+            mkdir -p -m 0755 $root/var/lib
+
+            # Create a named pipe to get a signal when the container
+            # has finished booting.
+            rm -f $root/var/lib/startup-done
+            mkfifo -m 0600 $root/var/lib/startup-done
+         '';
+
+        script =
+          ''
+            mkdir -p -m 0755 "$root/etc" "$root/var/lib"
+            if ! [ -e "$root/etc/os-release" ]; then
+              touch "$root/etc/os-release"
+            fi
+
+            mkdir -p -m 0755 \
+              "/nix/var/nix/profiles/per-container/$INSTANCE" \
+              "/nix/var/nix/gcroots/per-container/$INSTANCE"
+
+            if [ -f "/etc/containers/$INSTANCE.conf" ]; then
+              . "/etc/containers/$INSTANCE.conf"
             fi
+
+            # Cleanup from last time.
+            ifaceHost=c-$INSTANCE
+            ifaceCont=ctmp-$INSTANCE
+            ns=net-$INSTANCE
+            ip netns del $ns 2> /dev/null || true
+            ip link del $ifaceHost 2> /dev/null || true
+            ip link del $ifaceCont 2> /dev/null || true
+
+            if [ "$PRIVATE_NETWORK" = 1 ]; then
+              # Create a pair of virtual ethernet devices.  On the host,
+              # we get ‘c-<container-name’, and on the guest, we get
+              # ‘eth0’.
+              ip link add $ifaceHost type veth peer name $ifaceCont
+              ip netns add $ns
+              ip link set $ifaceCont netns $ns
+              ip netns exec $ns ip link set $ifaceCont name eth0
+              ip netns exec $ns ip link set dev eth0 up
+              ip link set dev $ifaceHost up
+              if [ -n "$HOST_ADDRESS" ]; then
+                ip addr add $HOST_ADDRESS dev $ifaceHost
+                ip netns exec $ns ip route add $HOST_ADDRESS dev eth0
+                ip netns exec $ns ip route add default via $HOST_ADDRESS
+              fi
+              if [ -n "$LOCAL_ADDRESS" ]; then
+                ip netns exec $ns ip addr add $LOCAL_ADDRESS dev eth0
+                ip route add $LOCAL_ADDRESS dev $ifaceHost
+              fi
+              runInNetNs="${runInNetns}/bin/run-in-netns $ns"
+              extraFlags="--capability=CAP_NET_ADMIN"
+            fi
+
+            # If the host is 64-bit and the container is 32-bit, add a
+            # --personality flag.
+            ${optionalString (config.nixpkgs.system == "x86_64-linux") ''
+              if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then
+                extraFlags+=" --personality=x86"
+              fi
+            ''}
+
+            exec $runInNetNs ${config.systemd.package}/bin/systemd-nspawn \
+              -M "$INSTANCE" -D "$root" $extraFlags \
+              --bind-ro=/nix/store \
+              --bind-ro=/nix/var/nix/db \
+              --bind-ro=/nix/var/nix/daemon-socket \
+              --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
+              --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
+              "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
           '';
 
-        serviceConfig.ExecStart =
-          "${config.systemd.package}/bin/systemd-nspawn -M ${name} -D ${container.root} --bind-ro=/nix ${container.path}/init";
+        postStart =
+          ''
+            # This blocks until the container-startup-done service
+            # writes something to this pipe.  FIXME: it also hangs
+            # until the start timeout expires if systemd-nspawn exits.
+            read x < $root/var/lib/startup-done
+            rm -f $root/var/lib/startup-done
+          '';
 
         preStop =
           ''
-            pid="$(cat /sys/fs/cgroup/systemd/machine/${name}.nspawn/system/tasks 2> /dev/null)"
-            if [ -n "$pid" ]; then
-              # Send the RTMIN+3 signal, which causes the container
-              # systemd to start halt.target.
-              echo "killing container systemd, PID = $pid"
-              kill -RTMIN+3 $pid
-              # Wait for the container to exit.  We can't let systemd
-              # do this because it will send a signal to the entire
-              # cgroup.
-              for ((n = 0; n < 180; n++)); do
-                if ! kill -0 $pid 2> /dev/null; then break; fi
-                sleep 1
-              done
+            machinectl poweroff "$INSTANCE"
+          '';
+
+        restartIfChanged = false;
+        #reloadIfChanged = true; # FIXME
+
+        serviceConfig.ExecReload = pkgs.writeScript "reload-container"
+          ''
+            #! ${pkgs.stdenv.shell} -e
+            SYSTEM_PATH=/nix/var/nix/profiles/system
+            if [ -f "/etc/containers/$INSTANCE.conf" ]; then
+              . "/etc/containers/$INSTANCE.conf"
             fi
+            echo $SYSTEM_PATH/bin/switch-to-configuration test | \
+              ${pkgs.socat}/bin/socat unix:$root/var/lib/run-command.socket -
+          '';
+
+        serviceConfig.SyslogIdentifier = "container %i";
+      };
+
+    # Generate a configuration file in /etc/containers for each
+    # container so that container@.target can get the container
+    # configuration.
+    environment.etc = mapAttrs' (name: cfg: nameValuePair "containers/${name}.conf"
+      { text =
+          ''
+            SYSTEM_PATH=${cfg.path}
+            ${optionalString cfg.privateNetwork ''
+              PRIVATE_NETWORK=1
+              ${optionalString (cfg.hostAddress != null) ''
+                HOST_ADDRESS=${cfg.hostAddress}
+              ''}
+              ${optionalString (cfg.localAddress != null) ''
+                LOCAL_ADDRESS=${cfg.localAddress}
+              ''}
+            ''}
           '';
-      }) config.systemd.containers;
+      }) config.containers;
+
+    # FIXME: auto-start containers.
+
+    # Generate /etc/hosts entries for the containers.
+    networking.extraHosts = concatStrings (mapAttrsToList (name: cfg: optionalString (cfg.localAddress != null)
+      ''
+        ${cfg.localAddress} ${name}.containers
+      '') config.containers);
+
+    environment.systemPackages = [ nixos-container ];
 
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/virtualisation/ec2-data.nix b/nixos/modules/virtualisation/ec2-data.nix
index 5133a98cd962..246d35065317 100644
--- a/nixos/modules/virtualisation/ec2-data.nix
+++ b/nixos/modules/virtualisation/ec2-data.nix
@@ -2,9 +2,9 @@
 # host name of virtual machines running on Amazon EC2, Eucalyptus and
 # OpenStack Compute (Nova).
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   options = {
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 098c9ede8533..34b8b0e9c1ca 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -1,7 +1,9 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
+{ config, lib, pkgs, ... }:
 
+with lib;
+let
+  diskSize = "100G";
+in
 {
   imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ];
 
@@ -12,7 +14,7 @@ with pkgs.lib;
             ''
               mkdir $out
               diskImage=$out/$diskImageBase
-              truncate $diskImage --size 10G
+              truncate $diskImage --size ${diskSize}
               mv closure xchg/
             '';
 
@@ -20,8 +22,9 @@ with pkgs.lib;
             ''
               PATH=$PATH:${pkgs.gnutar}/bin:${pkgs.gzip}/bin
               pushd $out
-              tar -Szcf $diskImageBase.tar.gz $diskImageBase
-              rm $out/$diskImageBase
+              mv $diskImageBase disk.raw
+              tar -Szcf $diskImageBase.tar.gz disk.raw
+              rm $out/disk.raw
               popd
             '';
           diskImageBase = "nixos-${config.system.nixosVersion}-${pkgs.stdenv.system}.raw";
@@ -32,7 +35,7 @@ with pkgs.lib;
         ''
           # Create partition table
           ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
-          ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 10G
+          ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 ${diskSize}
           ${pkgs.parted}/sbin/parted /dev/vda print
           . /sys/class/block/vda1/uevent
           mknod /dev/vda1 b $MAJOR $MINOR
@@ -114,7 +117,7 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  This doesn't really matter,
   # since the only PAM services that allow logging in with a null
   # password are local ones that are inaccessible on Google Compute machines.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 
   # Configure default metadata hostnames
   networking.extraHosts = ''
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 583b09192e33..d7d700d88412 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -1,8 +1,8 @@
 # Systemd services for libvirtd.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -24,6 +24,7 @@ in
 
     virtualisation.libvirtd.enable =
       mkOption {
+        type = types.bool;
         default = false;
         description =
           ''
@@ -36,6 +37,7 @@ in
 
     virtualisation.libvirtd.enableKVM =
       mkOption {
+        type = types.bool;
         default = true;
         description =
           ''
@@ -45,6 +47,7 @@ in
 
     virtualisation.libvirtd.extraConfig =
       mkOption {
+        type = types.lines;
         default = "";
         description =
           ''
diff --git a/nixos/modules/virtualisation/nixos-container.pl b/nixos/modules/virtualisation/nixos-container.pl
new file mode 100644
index 000000000000..2fd41a340962
--- /dev/null
+++ b/nixos/modules/virtualisation/nixos-container.pl
@@ -0,0 +1,238 @@
+#! @perl@
+
+use strict;
+use POSIX;
+use File::Path;
+use File::Slurp;
+use Fcntl ':flock';
+use Getopt::Long qw(:config gnu_getopt);
+
+my $socat = '@socat@/bin/socat';
+
+# Parse the command line.
+
+sub showHelp {
+    print <<EOF;
+Usage: nixos-container list
+       nixos-container create <container-name> [--config <string>] [--ensure-unique-name]
+       nixos-container destroy <container-name>
+       nixos-container start <container-name>
+       nixos-container stop <container-name>
+       nixos-container login <container-name>
+       nixos-container root-login <container-name>
+       nixos-container run <container-name> -- args...
+       nixos-container set-root-password <container-name> <password>
+       nixos-container show-ip <container-name>
+EOF
+    exit 0;
+}
+
+my $ensureUniqueName = 0;
+my $extraConfig = "";
+
+GetOptions(
+    "help" => sub { showHelp() },
+    "ensure-unique-name" => \$ensureUniqueName,
+    "config=s" => \$extraConfig
+    ) or exit 1;
+
+my $action = $ARGV[0] or die "$0: no action specified\n";
+
+
+# Execute the selected action.
+
+mkpath("/etc/containers", 0, 0755);
+mkpath("/var/lib/containers", 0, 0700);
+
+if ($action eq "list") {
+    foreach my $confFile (glob "/etc/containers/*.conf") {
+        $confFile =~ /\/([^\/]+).conf$/ or next;
+        print "$1\n";
+    }
+    exit 0;
+}
+
+my $containerName = $ARGV[1] or die "$0: no container name specified\n";
+$containerName =~ /^[a-zA-Z0-9\-]+$/ or die "$0: invalid container name\n";
+
+sub writeNixOSConfig {
+    my ($nixosConfigFile) = @_;
+
+    my $nixosConfig = <<EOF;
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{ boot.isContainer = true;
+  security.initialRootPassword = mkDefault "!";
+  networking.hostName = mkDefault "$containerName";
+  networking.useDHCP = false;
+  $extraConfig
+}
+EOF
+
+    write_file($nixosConfigFile, $nixosConfig);
+}
+
+if ($action eq "create") {
+    # Acquire an exclusive lock to prevent races with other
+    # invocations of ‘nixos-container create’.
+    my $lockFN = "/run/lock/nixos-container";
+    open(my $lock, '>>', $lockFN) or die "$0: opening $lockFN: $!";
+    flock($lock, LOCK_EX) or die "$0: could not lock $lockFN: $!";
+
+    my $confFile = "/etc/containers/$containerName.conf";
+    my $root = "/var/lib/containers/$containerName";
+
+    # Maybe generate a unique name.
+    if ($ensureUniqueName) {
+        my $base = $containerName;
+        for (my $nr = 0; ; $nr++) {
+            $containerName = "$base-$nr";
+            $confFile = "/etc/containers/$containerName.conf";
+            $root = "/var/lib/containers/$containerName";
+            last unless -e $confFile || -e $root;
+        }
+    }
+
+    die "$0: container ‘$containerName’ already exists\n" if -e $confFile;
+
+    # Get an unused IP address.
+    my %usedIPs;
+    foreach my $confFile2 (glob "/etc/containers/*.conf") {
+        my $s = read_file($confFile2) or die;
+        $usedIPs{$1} = 1 if $s =~ /^HOST_ADDRESS=([0-9\.]+)$/m;
+        $usedIPs{$1} = 1 if $s =~ /^LOCAL_ADDRESS=([0-9\.]+)$/m;
+    }
+
+    my ($ipPrefix, $hostAddress, $localAddress);
+    for (my $nr = 1; $nr < 255; $nr++) {
+        $ipPrefix = "10.233.$nr";
+        $hostAddress = "$ipPrefix.1";
+        $localAddress = "$ipPrefix.2";
+        last unless $usedIPs{$hostAddress} || $usedIPs{$localAddress};
+        $ipPrefix = undef;
+    }
+
+    die "$0: out of IP addresses\n" unless defined $ipPrefix;
+
+    my @conf;
+    push @conf, "PRIVATE_NETWORK=1\n";
+    push @conf, "HOST_ADDRESS=$hostAddress\n";
+    push @conf, "LOCAL_ADDRESS=$localAddress\n";
+    write_file($confFile, \@conf);
+
+    close($lock);
+
+    print STDERR "host IP is $hostAddress, container IP is $localAddress\n";
+
+    mkpath("$root/etc/nixos", 0, 0755);
+
+    my $nixosConfigFile = "$root/etc/nixos/configuration.nix";
+    writeNixOSConfig $nixosConfigFile;
+
+    # The per-container directory is restricted to prevent users on
+    # the host from messing with guest users who happen to have the
+    # same uid.
+    my $profileDir = "/nix/var/nix/profiles/per-container";
+    mkpath($profileDir, 0, 0700);
+    $profileDir = "$profileDir/$containerName";
+    mkpath($profileDir, 0, 0755);
+
+    system("nix-env", "-p", "$profileDir/system",
+           "-I", "nixos-config=$nixosConfigFile", "-f", "<nixpkgs/nixos>",
+           "--set", "-A", "system") == 0
+        or die "$0: failed to build initial container configuration\n";
+
+    print "$containerName\n" if $ensureUniqueName;
+    exit 0;
+}
+
+my $root = "/var/lib/containers/$containerName";
+my $profileDir = "/nix/var/nix/profiles/per-container/$containerName";
+my $confFile = "/etc/containers/$containerName.conf";
+die "$0: container ‘$containerName’ does not exist\n" if !-e $confFile;
+
+sub isContainerRunning {
+    my $status = `systemctl show 'container\@$containerName'`;
+    return $status =~ /ActiveState=active/;
+}
+
+sub stopContainer {
+    system("systemctl", "stop", "container\@$containerName") == 0
+        or die "$0: failed to stop container\n";
+}
+
+if ($action eq "destroy") {
+    die "$0: cannot destroy declarative container (remove it from your configuration.nix instead)\n"
+        unless POSIX::access($confFile, &POSIX::W_OK);
+
+    stopContainer if isContainerRunning;
+
+    rmtree($profileDir) if -e $profileDir;
+    rmtree($root) if -e $root;
+    unlink($confFile) or die;
+}
+
+elsif ($action eq "start") {
+    system("systemctl", "start", "container\@$containerName") == 0
+        or die "$0: failed to start container\n";
+}
+
+elsif ($action eq "stop") {
+    stopContainer;
+}
+
+elsif ($action eq "update") {
+    my $nixosConfigFile = "$root/etc/nixos/configuration.nix";
+
+    # FIXME: may want to be more careful about clobbering the existing
+    # configuration.nix.
+    writeNixOSConfig $nixosConfigFile if defined $extraConfig;
+
+    system("nix-env", "-p", "$profileDir/system",
+           "-I", "nixos-config=$nixosConfigFile", "-f", "<nixpkgs/nixos>",
+           "--set", "-A", "system") == 0
+        or die "$0: failed to build container configuration\n";
+
+    if (isContainerRunning) {
+        print STDERR "reloading container...\n";
+        system("systemctl", "reload", "container\@$containerName") == 0
+            or die "$0: failed to reload container\n";
+    }
+}
+
+elsif ($action eq "login") {
+    exec("machinectl", "login", "--", $containerName);
+}
+
+elsif ($action eq "root-login") {
+    exec($socat, "unix:$root/var/lib/root-login.socket", "-,echo=0,raw");
+}
+
+elsif ($action eq "run") {
+    shift @ARGV; shift @ARGV;
+    open(SOCAT, "|-", $socat, "unix:$root/var/lib/run-command.socket", "-");
+    print SOCAT join(' ', map { "'$_'" } @ARGV), "\n";
+    close(SOCAT);
+}
+
+elsif ($action eq "set-root-password") {
+    # FIXME: don't get password from the command line.
+    my $password = $ARGV[2] or die "$0: no password given\n";
+    open(SOCAT, "|-", $socat, "unix:$root/var/lib/run-command.socket", "-");
+    print SOCAT "passwd\n";
+    print SOCAT "$password\n";
+    print SOCAT "$password\n";
+    close(SOCAT);
+}
+
+elsif ($action eq "show-ip") {
+    my $s = read_file($confFile) or die;
+    $s =~ /^LOCAL_ADDRESS=([0-9\.]+)$/m or die "$0: cannot get IP address\n";
+    print "$1\n";
+}
+
+else {
+    die "$0: unknown action ‘$action’\n";
+}
diff --git a/nixos/modules/virtualisation/nova-image.nix b/nixos/modules/virtualisation/nova-image.nix
index 5c9481b71278..2523dacc0b56 100644
--- a/nixos/modules/virtualisation/nova-image.nix
+++ b/nixos/modules/virtualisation/nova-image.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   imports = [ ../profiles/qemu-guest.nix ../profiles/headless.nix ./ec2-data.nix ];
diff --git a/nixos/modules/virtualisation/nova.nix b/nixos/modules/virtualisation/nova.nix
index e0d25183574e..8795b5b52d5a 100644
--- a/nixos/modules/virtualisation/nova.nix
+++ b/nixos/modules/virtualisation/nova.nix
@@ -1,8 +1,8 @@
 # Module for Nova, a.k.a. OpenStack Compute.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 2218e1045eb8..6605b94439bc 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -7,9 +7,9 @@
 # the VM in the host.  On the other hand, the root filesystem is a
 # read/writable disk image persistent across VM reboots.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -386,8 +386,7 @@ in
 
     # When building a regular system configuration, override whatever
     # video driver the host uses.
-    services.xserver.videoDriver = mkVMOverride null;
-    services.xserver.videoDrivers = mkVMOverride [ "vesa" ];
+    hardware.opengl.videoDrivers = mkVMOverride [ "vesa" ];
     services.xserver.defaultDepth = mkVMOverride 0;
     services.xserver.resolutions = mkVMOverride [ { x = 1024; y = 768; } ];
     services.xserver.monitorSection =
@@ -400,6 +399,11 @@ in
     # Wireless won't work in the VM.
     networking.wireless.enable = mkVMOverride false;
 
+    # Speed up booting by not waiting for ARP.
+    networking.dhcpcd.extraConfig = "noarp";
+
+    networking.usePredictableInterfaceNames = false;
+
     system.requiredKernelConfig = with config.lib.kernelConfig;
       [ (isEnabled "VIRTIO_BLK")
         (isEnabled "VIRTIO_PCI")
diff --git a/nixos/modules/virtualisation/run-in-netns.c b/nixos/modules/virtualisation/run-in-netns.c
new file mode 100644
index 000000000000..d375bddf2e6b
--- /dev/null
+++ b/nixos/modules/virtualisation/run-in-netns.c
@@ -0,0 +1,50 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+
+int main(int argc, char * * argv)
+{
+    if (argc < 3) {
+        fprintf(stderr, "%s: missing arguments\n", argv[0]);
+        return 1;
+    }
+
+    char nsPath[PATH_MAX];
+
+    sprintf(nsPath, "/run/netns/%s", argv[1]);
+
+    int fd = open(nsPath, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "%s: opening network namespace: %s\n", argv[0], strerror(errno));
+        return 1;
+    }
+
+    if (setns(fd, CLONE_NEWNET) == -1) {
+        fprintf(stderr, "%s: setting network namespace: %s\n", argv[0], strerror(errno));
+        return 1;
+    }
+
+    umount2(nsPath, MNT_DETACH);
+    if (unlink(nsPath) == -1) {
+        fprintf(stderr, "%s: unlinking network namespace: %s\n", argv[0], strerror(errno));
+        return 1;
+    }
+
+    /* FIXME: Remount /sys so that /sys/class/net reflects the
+       interfaces visible in the network namespace. This requires
+       bind-mounting /sys/fs/cgroups etc. */
+
+    execv(argv[2], argv + 2);
+    fprintf(stderr, "%s: running command: %s\n", argv[0], strerror(errno));
+    return 1;
+}
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index 664fd21781cd..96354f1d81d0 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -1,8 +1,8 @@
 # Module for VirtualBox guests.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
@@ -38,6 +38,8 @@ optionalAttrs (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) # ugly...
 
     boot.extraModulePackages = [ kernel.virtualboxGuestAdditions ];
 
+    boot.kernelModules = [ "vboxsf" ];
+
     users.extraGroups.vboxsf.gid = config.ids.gids.vboxsf;
 
     systemd.services.virtualbox =
@@ -52,7 +54,7 @@ optionalAttrs (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) # ugly...
         serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/sbin/VBoxService VBoxService --foreground";
       };
 
-    services.xserver.videoDrivers = mkOverride 50 [ "virtualbox" ];
+    hardware.opengl.videoDrivers = mkOverride 50 [ "virtualbox" ];
 
     services.xserver.config =
       ''
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index 71bdf31a98d2..3247881784e4 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -1,6 +1,6 @@
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 {
   system.build.virtualBoxImage =
@@ -92,7 +92,7 @@ with pkgs.lib;
         --audiocontroller ac97 --audio alsa \
         --rtcuseutc on \
         --usb on --mouse usbtablet
-      VBoxManage storagectl "$vmName" --name SATA --add sata --sataportcount 4 --bootable on --hostiocache on
+      VBoxManage storagectl "$vmName" --name SATA --add sata --portcount 4 --bootable on --hostiocache on
       VBoxManage storageattach "$vmName" --storagectl SATA --port 0 --device 0 --type hdd \
         --medium ${config.system.build.virtualBoxImage}/disk.vdi
 
@@ -111,5 +111,5 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  For NixOps, we
   # don't need this because the user can login via SSH, and for the
   # demo images, there is a demo user account that can sudo to root.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 }
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 40f6929be4f0..566059472c9f 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -1,8 +1,8 @@
 # Xen hypervisor (Dom0) support.
 
-{ config, pkgs, ... }:
+{ config, lib, pkgs, ... }:
 
-with pkgs.lib;
+with lib;
 
 let
 
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index dccc3acbf464..32f523750004 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -44,7 +44,7 @@ in rec {
         (all nixos.iso_graphical)
         (all nixos.ova)
 
-        #(all nixos.tests.efi-installer.simple)
+        # (all nixos.tests.efi-installer.simple)
         (all nixos.tests.firefox)
         (all nixos.tests.firewall)
         (all nixos.tests.installer.grub1)
@@ -60,6 +60,8 @@ in rec {
         (all nixos.tests.openssh)
         (all nixos.tests.printing)
         (all nixos.tests.proxy)
+        (all nixos.tests.udisks)
+        (all nixos.tests.udisks2)
         (all nixos.tests.xfce)
 
         nixpkgs.tarball
diff --git a/nixos/release.nix b/nixos/release.nix
index ff094cce05fa..4a10ff39ed0c 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -14,6 +14,8 @@ let
 
   forAllSystems = pkgs.lib.genAttrs systems;
 
+  callTest = fn: args: forAllSystems (system: import fn ({ inherit system; } // args));
+
   pkgs = import nixpkgs { system = "x86_64-linux"; };
 
   lib = pkgs.lib;
@@ -207,13 +209,43 @@ in rec {
   */
 
 
-  # Run the tests in ./tests/default.nix for each platform.  You can
-  # run a test by doing e.g. "nix-build -A tests.login.x86_64-linux".
-  tests =
-    with lib;
-    let
-      testsFor = system:
-        mapAttrsRecursiveCond (x: !x ? test) (n: v: listToAttrs [(nameValuePair system v.test)])
-          (import ./tests { inherit nixpkgs system; });
-    in fold recursiveUpdate {} (map testsFor systems);
+  # Run the tests for each platform.  You can run a test by doing
+  # e.g. ‘nix-build -A tests.login.x86_64-linux’, or equivalently,
+  # ‘nix-build tests/login.nix -A result’.
+  tests.avahi = callTest tests/avahi.nix {};
+  tests.bittorrent = callTest tests/bittorrent.nix {};
+  tests.containers = callTest tests/containers.nix {};
+  tests.firefox = callTest tests/firefox.nix {};
+  tests.firewall = callTest tests/firewall.nix {};
+  tests.gnome3 = callTest tests/gnome3.nix {};
+  tests.installer.grub1 = forAllSystems (system: (import tests/installer.nix { inherit system; }).grub1.test);
+  tests.installer.lvm = forAllSystems (system: (import tests/installer.nix { inherit system; }).lvm.test);
+  tests.installer.rebuildCD = forAllSystems (system: (import tests/installer.nix { inherit system; }).rebuildCD.test);
+  tests.installer.separateBoot = forAllSystems (system: (import tests/installer.nix { inherit system; }).separateBoot.test);
+  tests.installer.simple = forAllSystems (system: (import tests/installer.nix { inherit system; }).simple.test);
+  tests.ipv6 = callTest tests/ipv6.nix {};
+  tests.jenkins = callTest tests/jenkins.nix {};
+  tests.kde4 = callTest tests/kde4.nix {};
+  tests.latestKernel.login = callTest tests/login.nix { latestKernel = true; };
+  tests.login = callTest tests/login.nix {};
+  tests.logstash = callTest tests/logstash.nix {};
+  tests.misc = callTest tests/misc.nix {};
+  tests.mumble = callTest tests/mumble.nix {};
+  tests.munin = callTest tests/munin.nix {};
+  tests.mysql = callTest tests/mysql.nix {};
+  tests.mysqlReplication = callTest tests/mysql-replication.nix {};
+  tests.nat = callTest tests/nat.nix {};
+  tests.nfs3 = callTest tests/nfs.nix { version = 3; };
+  tests.openssh = callTest tests/openssh.nix {};
+  tests.printing = callTest tests/printing.nix {};
+  tests.proxy = callTest tests/proxy.nix {};
+  tests.quake3 = callTest tests/quake3.nix {};
+  tests.rabbitmq = callTest tests/rabbitmq.nix {};
+  tests.runInMachine = callTest tests/run-in-machine.nix {};
+  tests.simple = callTest tests/simple.nix {};
+  tests.tomcat = callTest tests/tomcat.nix {};
+  tests.udisks = callTest tests/udisks.nix {};
+  tests.udisks2 = callTest tests/udisks2.nix {};
+  tests.xfce = callTest tests/xfce.nix {};
+
 }
diff --git a/nixos/tests/avahi.nix b/nixos/tests/avahi.nix
index d95361dcd83d..4091e7ece501 100644
--- a/nixos/tests/avahi.nix
+++ b/nixos/tests/avahi.nix
@@ -1,8 +1,7 @@
-{ pkgs, ... }:
+# Test whether `avahi-daemon' and `libnss-mdns' work as expected.
 
-with pkgs;
+import ./make-test.nix {
 
-{
   nodes = {
     one =
       { config, pkgs, ... }: {
@@ -17,7 +16,6 @@ with pkgs;
       };
   };
 
-  # Test whether `avahi-daemon' and `libnss-mdns' work as expected.
   testScript =
     '' startAll;
 
diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
index 6e67edb0b820..b58657a5ecdb 100644
--- a/nixos/tests/bittorrent.nix
+++ b/nixos/tests/bittorrent.nix
@@ -6,7 +6,7 @@
 # which only works if the first client successfully uses the UPnP-IGD
 # protocol to poke a hole in the NAT.
 
-{ pkgs, ... }:
+import ./make-test.nix ({ pkgs, ... }:
 
 let
 
@@ -33,6 +33,8 @@ in
           services.httpd.enable = true;
           services.httpd.adminAddr = "foo@example.org";
           services.httpd.documentRoot = "/tmp";
+
+          networking.firewall.enable = false; # FIXME: figure out what ports we actually need
         };
 
       router =
@@ -40,8 +42,9 @@ in
         { environment.systemPackages = [ pkgs.miniupnpd ];
           virtualisation.vlans = [ 1 2 ];
           networking.nat.enable = true;
-          networking.nat.internalIPs = [ "192.168.2.0/24" ];
+          networking.nat.internalInterfaces = [ "eth2" ];
           networking.nat.externalInterface = "eth1";
+          networking.firewall.enable = false;
         };
 
       client1 =
@@ -50,11 +53,13 @@ in
           virtualisation.vlans = [ 2 ];
           networking.defaultGateway =
             nodes.router.config.networking.interfaces.eth2.ipAddress;
+          networking.firewall.enable = false;
         };
 
       client2 =
         { config, pkgs, ... }:
         { environment.systemPackages = [ pkgs.transmission ];
+          networking.firewall.enable = false;
         };
     };
 
@@ -66,8 +71,8 @@ in
       # Enable NAT on the router and start miniupnpd.
       $router->waitForUnit("nat");
       $router->succeed(
-          "iptables -t nat -N MINIUPNPD",
-          "iptables -t nat -A PREROUTING -i eth1 -j MINIUPNPD",
+          "iptables -w -t nat -N MINIUPNPD",
+          "iptables -w -t nat -A PREROUTING -i eth1 -j MINIUPNPD",
           "echo 1 > /proc/sys/net/ipv4/ip_forward",
           "miniupnpd -f ${miniupnpdConf nodes}"
       );
@@ -75,7 +80,7 @@ in
       # Create the torrent.
       $tracker->succeed("mkdir /tmp/data");
       $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2");
-      $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -t http://tracker:6969/announce -o /tmp/test.torrent");
+      $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -t http://${nodes.tracker.config.networking.interfaces.eth1.ipAddress}:6969/announce -o /tmp/test.torrent");
       $tracker->succeed("chmod 644 /tmp/test.torrent");
 
       # Start the tracker.  !!! use a less crappy tracker
@@ -104,4 +109,4 @@ in
       $client2->succeed("cmp /tmp/test.tar.bz2 ${file}");
     '';
 
-}
+})
diff --git a/nixos/tests/common/user-account.nix b/nixos/tests/common/user-account.nix
index 8157cf8d263e..0239a3c4d08a 100644
--- a/nixos/tests/common/user-account.nix
+++ b/nixos/tests/common/user-account.nix
@@ -7,5 +7,6 @@
       createHome = true;
       useDefaultShell = true;
       password = "foobar";
+      uid = 1000;
     };
 }
diff --git a/nixos/tests/containers.nix b/nixos/tests/containers.nix
new file mode 100644
index 000000000000..8ad9cd6e0d79
--- /dev/null
+++ b/nixos/tests/containers.nix
@@ -0,0 +1,79 @@
+# Test for NixOS' container support.
+
+import ./make-test.nix {
+
+  machine =
+    { config, pkgs, ... }:
+    { imports = [ ../modules/installer/cd-dvd/channel.nix ];
+      virtualisation.writableStore = true;
+      virtualisation.memorySize = 768;
+
+      containers.webserver =
+        { privateNetwork = true;
+          hostAddress = "10.231.136.1";
+          localAddress = "10.231.136.2";
+          config =
+            { services.httpd.enable = true;
+              services.httpd.adminAddr = "foo@example.org";
+              networking.firewall.allowedTCPPorts = [ 80 ];
+              networking.firewall.allowPing = true;
+            };
+        };
+
+      virtualisation.pathsInNixDB = [ pkgs.stdenv ];
+    };
+
+  testScript =
+    ''
+      $machine->succeed("nixos-container list") =~ /webserver/ or die;
+
+      # Start the webserver container.
+      $machine->succeed("nixos-container start webserver");
+
+      # Since "start" returns after the container has reached
+      # multi-user.target, we should now be able to access it.
+      my $ip = $machine->succeed("nixos-container show-ip webserver");
+      chomp $ip;
+      $machine->succeed("ping -c1 $ip");
+      $machine->succeed("curl --fail http://$ip/ > /dev/null");
+
+      # Stop the container.
+      $machine->succeed("nixos-container stop webserver");
+      $machine->fail("curl --fail --connect-timeout 2 http://$ip/ > /dev/null");
+
+      # Make sure we have a NixOS tree (required by ‘nixos-container create’).
+      $machine->succeed("nix-env -qa -A nixos.pkgs.hello >&2");
+
+      # Create some containers imperatively.
+      my $id1 = $machine->succeed("nixos-container create foo --ensure-unique-name");
+      chomp $id1;
+      $machine->log("created container $id1");
+
+      my $id2 = $machine->succeed("nixos-container create foo --ensure-unique-name");
+      chomp $id2;
+      $machine->log("created container $id2");
+
+      die if $id1 eq $id2;
+
+      my $ip1 = $machine->succeed("nixos-container show-ip $id1");
+      chomp $ip1;
+      my $ip2 = $machine->succeed("nixos-container show-ip $id2");
+      chomp $ip2;
+      die if $ip1 eq $ip2;
+
+      # Start one of them.
+      $machine->succeed("nixos-container start $id1");
+
+      # Execute commands via the root shell.
+      $machine->succeed("nixos-container run $id1 -- uname") =~ /Linux/ or die;
+      $machine->succeed("nixos-container set-root-password $id1 foobar");
+
+      # Destroy the containers.
+      $machine->succeed("nixos-container destroy $id1");
+      $machine->succeed("nixos-container destroy $id2");
+
+      # Destroying a declarative container should fail.
+      $machine->fail("nixos-container destroy webserver");
+    '';
+
+}
diff --git a/nixos/tests/default.nix b/nixos/tests/default.nix
deleted file mode 100644
index 574e1dd2f8b8..000000000000
--- a/nixos/tests/default.nix
+++ /dev/null
@@ -1,40 +0,0 @@
-{ nixpkgs ? <nixpkgs>
-, system ? builtins.currentSystem
-, minimal ? false
-}:
-
-with import ../lib/testing.nix { inherit system minimal; };
-
-{
-  avahi = makeTest (import ./avahi.nix);
-  bittorrent = makeTest (import ./bittorrent.nix);
-  firefox = makeTest (import ./firefox.nix);
-  firewall = makeTest (import ./firewall.nix);
-  installer = makeTests (import ./installer.nix);
-  efi-installer = makeTests (import ./efi-installer.nix);
-  ipv6 = makeTest (import ./ipv6.nix);
-  kde4 = makeTest (import ./kde4.nix);
-  #kexec = makeTest (import ./kexec.nix);
-  login = makeTest (import ./login.nix {});
-  logstash = makeTest (import ./logstash.nix);
-  latestKernel.login = makeTest (import ./login.nix ({ config, pkgs, ... }: { boot.kernelPackages = pkgs.linuxPackages_latest; }));
-  misc = makeTest (import ./misc.nix);
-  #mpich = makeTest (import ./mpich.nix);
-  mysql = makeTest (import ./mysql.nix);
-  mysql_replication = makeTest (import ./mysql-replication.nix);
-  munin = makeTest (import ./munin.nix);
-  nat = makeTest (import ./nat.nix);
-  nfs3 = makeTest (import ./nfs.nix { version = 3; });
-  #nfs4 = makeTest (import ./nfs.nix { version = 4; });
-  openssh = makeTest (import ./openssh.nix);
-  #partition = makeTest (import ./partition.nix);
-  printing = makeTest (import ./printing.nix);
-  proxy = makeTest (import ./proxy.nix);
-  quake3 = makeTest (import ./quake3.nix);
-  simple = makeTest (import ./simple.nix);
-  #subversion = makeTest (import ./subversion.nix);
-  tomcat = makeTest (import ./tomcat.nix);
-  #trac = makeTest (import ./trac.nix);
-  xfce = makeTest (import ./xfce.nix);
-  runInMachine.test = import ./run-in-machine.nix { inherit system; };
-}
diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix
index ca634ffcf1e0..b42d473b8025 100644
--- a/nixos/tests/firefox.nix
+++ b/nixos/tests/firefox.nix
@@ -1,6 +1,4 @@
-{ pkgs, ... }:
-
-{
+import ./make-test.nix ({ pkgs, ... }: {
 
   machine =
     { config, pkgs, ... }:
@@ -16,7 +14,6 @@
       $machine->waitForWindow(qr/Valgrind/);
       $machine->sleep(40); # wait until Firefox has finished loading the page
       $machine->screenshot("screen");
-
     '';
 
-}
+})
diff --git a/nixos/tests/firewall.nix b/nixos/tests/firewall.nix
index de32b98e5d2f..d10e10b1d91c 100644
--- a/nixos/tests/firewall.nix
+++ b/nixos/tests/firewall.nix
@@ -1,8 +1,6 @@
 # Test the firewall module.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix {
 
   nodes =
     { walled =
@@ -17,6 +15,7 @@
         { config, pkgs, ... }:
         { services.httpd.enable = true;
           services.httpd.adminAddr = "foo@example.org";
+          networking.firewall.enable = false;
         };
     };
 
@@ -33,7 +32,7 @@
       $walled->succeed("curl -v http://localhost/ >&2");
 
       # Connections to the firewalled machine should fail.
-      $attacker->fail("curl -v http://walled/ >&2");
+      $attacker->fail("curl --fail --connect-timeout 2 http://walled/ >&2");
       $attacker->fail("ping -c 1 walled >&2");
 
       # Outgoing connections/pings should still work.
diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix
new file mode 100644
index 000000000000..f1a6ce633313
--- /dev/null
+++ b/nixos/tests/gnome3.nix
@@ -0,0 +1,29 @@
+import ./make-test.nix {
+
+  machine =
+    { config, pkgs, ... }:
+
+    { imports = [ ./common/user-account.nix ];
+
+      services.xserver.enable = true;
+
+      services.xserver.displayManager.auto.enable = true;
+      services.xserver.displayManager.auto.user = "alice";
+      services.xserver.desktopManager.gnome3.enable = true;
+    };
+
+  testScript =
+    ''
+      $machine->waitForX;
+      $machine->sleep(15);
+
+      # Check that logging in has given the user ownership of devices.
+      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+
+      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
+      $machine->waitForWindow(qr/Terminal/);
+      $machine->sleep(10);
+      $machine->screenshot("screen");
+    '';
+
+}
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 7581c10a01d0..43bea22d852e 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -1,7 +1,8 @@
-{ pkgs, system, ... }:
+{ system ? builtins.currentSystem }:
 
-with pkgs.lib;
+with import ../lib/testing.nix { inherit system; };
 with import ../lib/qemu-flags.nix;
+with pkgs.lib;
 
 let
 
@@ -39,7 +40,7 @@ let
 
       { imports =
           [ ./hardware-configuration.nix
-            "''${modulesPath}/testing/test-instrumentation.nix"
+            <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
           ];
 
         boot.loader.grub.version = ${toString grubVersion};
@@ -48,7 +49,6 @@ let
         ''}
         boot.loader.grub.device = "${grubDevice}";
         boot.loader.grub.extraConfig = "serial; terminal_output.serial";
-        boot.initrd.kernelModules = [ "virtio_console" ];
 
         environment.systemPackages = [ ${optionalString testChannel "pkgs.rlwrap"} ];
       }
@@ -80,6 +80,8 @@ let
       virtualisation.writableStore = true;
       virtualisation.pathsInNixDB = channelContents ++ [ pkgs.hello.src ];
       virtualisation.memorySize = 768;
+
+      networking.firewall.allowedTCPPorts = [ 80 ];
     };
 
   channelContents = [ pkgs.rlwrap ];
@@ -98,7 +100,7 @@ let
       my $machine = createMachine({ hda => "harddisk",
         hdaInterface => "${iface}",
         cdrom => glob("${iso}/iso/*.iso"),
-        qemuFlags => '${optionalString testChannel (toString (qemuNICFlags 1 1 2))} ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"}'});
+        qemuFlags => '${optionalString testChannel (toString (qemuNICFlags 1 1 2))} ${optionalString (iso.system == "x86_64-linux") "-cpu kvm64"}'});
       $machine->start;
 
       ${optionalString testChannel ''
@@ -177,7 +179,7 @@ let
       # Test nixos-option.
       $machine->succeed("nixos-option boot.initrd.kernelModules | grep virtio_console");
       $machine->succeed("nixos-option -d boot.initrd.kernelModules | grep 'List of modules'");
-      $machine->succeed("nixos-option -l boot.initrd.kernelModules | grep /etc/nixos/configuration.nix");
+      $machine->succeed("nixos-option -l boot.initrd.kernelModules | grep qemu-guest.nix");
 
       $machine->shutdown;
 
@@ -189,8 +191,10 @@ let
     '';
 
 
-  makeTest = { createPartitions, fileSystems, testChannel ? false, grubVersion ? 2, grubDevice ? "/dev/vda" }:
-    { inherit iso;
+  makeInstallerTest =
+    { createPartitions, fileSystems, testChannel ? false, grubVersion ? 2, grubDevice ? "/dev/vda" }:
+    makeTest {
+      inherit iso;
       nodes = if testChannel then { inherit webserver; } else { };
       testScript = testScriptFun {
         inherit createPartitions fileSystems testChannel grubVersion grubDevice;
@@ -205,7 +209,7 @@ in {
 
   # The (almost) simplest partitioning scheme: a swap partition and
   # one big filesystem partition.
-  simple = makeTest
+  simple = makeInstallerTest
     { createPartitions =
         ''
           $machine->succeed(
@@ -224,7 +228,7 @@ in {
     };
 
   # Same as the previous, but now with a separate /boot partition.
-  separateBoot = makeTest
+  separateBoot = makeInstallerTest
     { createPartitions =
         ''
           $machine->succeed(
@@ -238,7 +242,7 @@ in {
               "mkfs.ext3 -L nixos /dev/vda3",
               "mount LABEL=nixos /mnt",
               "mkfs.ext3 -L boot /dev/vda1",
-              "mkdir /mnt/boot",
+              "mkdir -p /mnt/boot",
               "mount LABEL=boot /mnt/boot",
           );
         '';
@@ -247,14 +251,14 @@ in {
 
   # Create two physical LVM partitions combined into one volume group
   # that contains the logical swap and root partitions.
-  lvm = makeTest
+  lvm = makeInstallerTest
     { createPartitions =
         ''
           $machine->succeed(
               "parted /dev/vda mklabel msdos",
-              "parted /dev/vda -- mkpart primary 1M 2048M", # first PV
+              "parted /dev/vda -- mkpart primary 1M 2048M", # PV1
               "parted /dev/vda -- set 1 lvm on",
-              "parted /dev/vda -- mkpart primary 2048M -1s", # second PV
+              "parted /dev/vda -- mkpart primary 2048M -1s", # PV2
               "parted /dev/vda -- set 2 lvm on",
               "udevadm settle",
               "pvcreate /dev/vda1 /dev/vda2",
@@ -270,8 +274,7 @@ in {
       fileSystems = rootFS;
     };
 
-  /*
-  swraid = makeTest
+  swraid = makeInstallerTest
     { createPartitions =
         ''
           $machine->succeed(
@@ -303,10 +306,9 @@ in {
         '';
       fileSystems = rootFS + bootFS;
     };
-  */
 
   # Test a basic install using GRUB 1.
-  grub1 = makeTest
+  grub1 = makeInstallerTest
     { createPartitions =
         ''
           $machine->succeed(
@@ -327,7 +329,7 @@ in {
     };
 
   # Rebuild the CD configuration with a little modification.
-  rebuildCD =
+  rebuildCD = makeTest
     { inherit iso;
       nodes = { };
       testScript =
diff --git a/nixos/tests/ipv6.nix b/nixos/tests/ipv6.nix
index 29d675e180a3..eb15363d3c32 100644
--- a/nixos/tests/ipv6.nix
+++ b/nixos/tests/ipv6.nix
@@ -1,9 +1,7 @@
 # Test of IPv6 functionality in NixOS, including whether router
 # solicication/advertisement using radvd works.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix {
 
   nodes =
     { client = { config, pkgs, ... }: { };
@@ -12,6 +10,7 @@
         { config, pkgs, ... }:
         { services.httpd.enable = true;
           services.httpd.adminAddr = "foo@example.org";
+          networking.firewall.allowedTCPPorts = [ 80 ];
         };
 
       router =
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
new file mode 100644
index 000000000000..9d3f76ca3e15
--- /dev/null
+++ b/nixos/tests/jenkins.nix
@@ -0,0 +1,41 @@
+# verifies:
+#   1. jenkins service starts on master node
+#   2. jenkins user can be extended on both master and slave
+#   3. jenkins service not started on slave node
+
+import ./make-test.nix {
+
+  nodes = {
+
+    master =
+      { config, pkgs, ... }:
+      { services.jenkins.enable = true;
+
+        # should have no effect
+        services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+
+    slave =
+      { config, pkgs, ... }:
+      { services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+
+  };
+
+  testScript = ''
+    startAll;
+
+    $master->waitForUnit("jenkins");
+    print $master->execute("sudo -u jenkins groups");
+    $master->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    print $slave->execute("sudo -u jenkins groups");
+    $slave->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    $slave->mustFail("systemctl status jenkins.service");
+  '';
+}
diff --git a/nixos/tests/kde4.nix b/nixos/tests/kde4.nix
index 3fb35bbab098..725759ab758a 100644
--- a/nixos/tests/kde4.nix
+++ b/nixos/tests/kde4.nix
@@ -1,6 +1,4 @@
-{ pkgs, ... }:
-
-{
+import ./make-test.nix ({ pkgs, ... }: {
 
   machine =
     { config, pkgs, ... }:
@@ -64,4 +62,4 @@
       $machine->screenshot("screen");
     '';
 
-}
+})
diff --git a/nixos/tests/kexec.nix b/nixos/tests/kexec.nix
index b8da332b919b..b09287682c01 100644
--- a/nixos/tests/kexec.nix
+++ b/nixos/tests/kexec.nix
@@ -1,8 +1,6 @@
 # Test whether fast reboots via kexec work.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix  {
 
   machine = { config, pkgs, ... }:
     { virtualisation.vlans = [ ]; };
diff --git a/nixos/tests/login.nix b/nixos/tests/login.nix
index ed7d97867179..44c53c231c81 100644
--- a/nixos/tests/login.nix
+++ b/nixos/tests/login.nix
@@ -1,12 +1,16 @@
-config: { pkgs, ... }:
+import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
 
 {
 
-  machine = config;
+  machine =
+    { config, pkgs, lib, ... }:
+    { boot.kernelPackages = lib.mkIf latestKernel pkgs.linuxPackages_latest;
+    };
 
   testScript =
     ''
-      $machine->waitForUnit("default.target");
+      $machine->waitForUnit('multi-user.target');
+      $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'");
       $machine->screenshot("postboot");
 
       subtest "create user", sub {
@@ -16,9 +20,11 @@ config: { pkgs, ... }:
 
       # Check whether switching VTs works.
       subtest "virtual console switching", sub {
+          $machine->fail("pgrep -f 'agetty.*tty2'");
           $machine->sendKeys("alt-f2");
           $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]");
           $machine->waitForUnit('getty@tty2.service');
+          $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'");
       };
 
       # Log in as alice on a virtual console.
@@ -58,4 +64,4 @@ config: { pkgs, ... }:
       };
     '';
 
-}
+})
diff --git a/nixos/tests/logstash.nix b/nixos/tests/logstash.nix
index ee309d39f872..e6aba7a10126 100644
--- a/nixos/tests/logstash.nix
+++ b/nixos/tests/logstash.nix
@@ -1,9 +1,8 @@
-{ pkgs, ... }:
+# This test runs logstash and checks if messages flows and
+# elasticsearch is started.
 
-# This test runs logstash and checks if messages flows and elasticsearch is
-# started
+import ./make-test.nix {
 
-{
   nodes = {
     one =
       { config, pkgs, ... }:
@@ -28,10 +27,10 @@
           };
         };
     };
-  
+
   testScript = ''
     startAll;
-  
+
     $one->waitForUnit("logstash.service");
     $one->waitUntilSucceeds("journalctl -n 20 _SYSTEMD_UNIT=logstash.service | grep flowers");
     $one->fail("journalctl -n 20 _SYSTEMD_UNIT=logstash.service | grep dragons");
diff --git a/nixos/tests/make-test.nix b/nixos/tests/make-test.nix
new file mode 100644
index 000000000000..285ca5b71d6e
--- /dev/null
+++ b/nixos/tests/make-test.nix
@@ -0,0 +1,5 @@
+f: { system ? builtins.currentSystem, ... } @ args:
+
+with import ../lib/testing.nix { inherit system; };
+
+makeTest (if builtins.isFunction f then f (args // { inherit pkgs; }) else f)
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index d355d705a24c..363be2cbb357 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -1,8 +1,6 @@
 # Miscellaneous small tests that don't warrant their own VM run.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix {
 
   machine =
     { config, pkgs, ... }:
@@ -10,6 +8,7 @@
         [ { device = "/root/swapfile"; size = 128; } ];
       environment.variables.EDITOR = pkgs.lib.mkOverride 0 "emacs";
       services.nixosManual.enable = pkgs.lib.mkOverride 0 true;
+      systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ];
     };
 
   testScript =
@@ -65,6 +64,22 @@
           $machine->succeed('[ "`hostname`" = machine ]');
           $machine->succeed('[ "`hostname -s`" = machine ]');
       };
+
+      # Test whether systemd-udevd automatically loads modules for our hardware.
+      subtest "udev-auto-load", sub {
+          $machine->waitForUnit('systemd-udev-settle.service');
+          $machine->succeed('lsmod | grep psmouse');
+      };
+
+      # Test whether systemd-tmpfiles-clean works.
+      subtest "tmpfiles", sub {
+          $machine->succeed('touch /tmp/foo');
+          $machine->succeed('systemctl start systemd-tmpfiles-clean');
+          $machine->succeed('[ -e /tmp/foo ]');
+          $machine->succeed('date -s "@$(($(date +%s) + 1000000))"'); # move into the future
+          $machine->succeed('systemctl start systemd-tmpfiles-clean');
+          $machine->fail('[ -e /tmp/foo ]');
+      };
     '';
 
 }
diff --git a/nixos/tests/mpich.nix b/nixos/tests/mpich.nix
index d57512ebdfed..13cd0960d07c 100644
--- a/nixos/tests/mpich.nix
+++ b/nixos/tests/mpich.nix
@@ -1,10 +1,6 @@
 # Simple example to showcase distributed tests using NixOS VMs.
 
-{ pkgs, ... }:
-
-with pkgs;
-
-{
+import ./make-test.nix {
   nodes = {
     master =
       { config, pkgs, ... }: {
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
new file mode 100644
index 000000000000..8896830b0c22
--- /dev/null
+++ b/nixos/tests/mumble.nix
@@ -0,0 +1,55 @@
+import ./make-test.nix (
+
+let
+  client = { config, pkgs, ... }: {
+    imports = [ ./common/x11.nix ];
+    environment.systemPackages = [ pkgs.mumble ];
+  };
+in
+{
+  nodes = {
+    server = { config, pkgs, ... }: {
+      services.murmur.enable       = true;
+      services.murmur.registerName = "NixOS tests";
+      networking.firewall.allowedTCPPorts = [ config.services.murmur.port ];
+    };
+
+    client1 = client;
+    client2 = client;
+  };
+
+  testScript = ''
+    startAll;
+
+    $server->waitForUnit("murmur.service");
+    $client1->waitForX;
+    $client2->waitForX;
+
+    $client1->execute("mumble mumble://client1\@server/test &");
+    $client2->execute("mumble mumble://client2\@server/test &");
+
+    $server->sleep(10); # Wait for Mumble UI to pop up
+
+    # cancel client audio configuration
+    $client1->sendKeys("esc");
+    $client2->sendKeys("esc");
+    $server->sleep(1);
+
+    # cancel client cert configuration
+    $client1->sendKeys("esc");
+    $client2->sendKeys("esc");
+    $server->sleep(1);
+
+    # accept server certificate
+    $client1->sendChars("y");
+    $client2->sendChars("y");
+
+    # Find clients in logs
+    $server->waitUntilSucceeds("grep -q 'client1' /var/log/murmur/murmurd.log");
+    $server->waitUntilSucceeds("grep -q 'client2' /var/log/murmur/murmurd.log");
+
+    $server->sleep(5); # wait to get screenshot
+    $client1->screenshot("screen1");
+    $client2->screenshot("screen2");
+  '';
+})
diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix
index 66ae1c0d87f7..acc4b949ab57 100644
--- a/nixos/tests/munin.nix
+++ b/nixos/tests/munin.nix
@@ -1,13 +1,12 @@
-{ pkgs, ... }:
-
 # This test runs basic munin setup with node and cron job running on the same
 # machine.
 
-{
-  nodes = { 
+import ./make-test.nix {
+
+  nodes = {
     one =
       { config, pkgs, ... }:
-        { 
+        {
           services = {
            munin-node.enable = true;
            munin-cron = {
@@ -20,10 +19,10 @@
           };
         };
     };
-  
+
   testScript = ''
     startAll;
-  
+
     $one->waitForUnit("munin-node.service");
     $one->waitForFile("/var/lib/munin/one/one-uptime-uptime-g.rrd");
     $one->waitForFile("/var/www/munin/one/index.html");
diff --git a/nixos/tests/mysql-replication.nix b/nixos/tests/mysql-replication.nix
index 28a1187dd184..7d0cf6d85a1a 100644
--- a/nixos/tests/mysql-replication.nix
+++ b/nixos/tests/mysql-replication.nix
@@ -1,9 +1,10 @@
-{ pkgs, ... }:
+import ./make-test.nix (
 
 let
   replicateUser = "replicate";
   replicatePassword = "secret";
 in
+
 {
   nodes = {
     master =
@@ -11,13 +12,15 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "master";
-	services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
-	services.mysql.initialScript = pkgs.writeText "initmysql"
-        ''
-	  create user '${replicateUser}'@'%' identified by '${replicatePassword}';
-          grant replication slave on *.* to '${replicateUser}'@'%';
-        '';
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "master";
+        services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.initialScript = pkgs.writeText "initmysql"
+          ''
+            create user '${replicateUser}'@'%' identified by '${replicatePassword}';
+            grant replication slave on *.* to '${replicateUser}'@'%';
+          '';
+        networking.firewall.allowedTCPPorts = [ 3306 ];
       };
 
     slave1 =
@@ -25,11 +28,12 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "slave";
-	services.mysql.replication.serverId = 2;
-	services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-	services.mysql.replication.masterUser = replicateUser;
-	services.mysql.replication.masterPassword = replicatePassword;
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "slave";
+        services.mysql.replication.serverId = 2;
+        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
+        services.mysql.replication.masterUser = replicateUser;
+        services.mysql.replication.masterPassword = replicatePassword;
       };
 
     slave2 =
@@ -37,11 +41,12 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "slave";
-	services.mysql.replication.serverId = 3;
-	services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-	services.mysql.replication.masterUser = replicateUser;
-	services.mysql.replication.masterPassword = replicatePassword;
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "slave";
+        services.mysql.replication.serverId = 3;
+        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
+        services.mysql.replication.masterUser = replicateUser;
+        services.mysql.replication.masterPassword = replicatePassword;
       };
   };
 
@@ -54,4 +59,4 @@ in
     $slave2->sleep(100); # Hopefully this is long enough!!
     $slave2->succeed("echo 'use testdb; select * from tests' | mysql -u root -N | grep 4");
   '';
-}
+})
diff --git a/nixos/tests/mysql.nix b/nixos/tests/mysql.nix
index b48850738b72..566d03baf367 100644
--- a/nixos/tests/mysql.nix
+++ b/nixos/tests/mysql.nix
@@ -1,14 +1,14 @@
-{ pkgs, ... }:
+import ./make-test.nix {
 
-{
   nodes = {
     master =
       { pkgs, config, ... }:
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "master";
-	services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.replication.role = "master";
+        services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.package = pkgs.mysql;
       };
   };
 
diff --git a/nixos/tests/nat.nix b/nixos/tests/nat.nix
index a13714d60a94..02981469e106 100644
--- a/nixos/tests/nat.nix
+++ b/nixos/tests/nat.nix
@@ -4,14 +4,13 @@
 # router connected to both that performs Network Address Translation
 # for the client.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix {
 
   nodes =
     { client =
         { config, pkgs, nodes, ... }:
         { virtualisation.vlans = [ 1 ];
+          networking.firewall.allowPing = true;
           networking.defaultGateway =
             nodes.router.config.networking.interfaces.eth2.ipAddress;
         };
@@ -19,6 +18,7 @@
       router =
         { config, pkgs, ... }:
         { virtualisation.vlans = [ 2 1 ];
+          networking.firewall.allowPing = true;
           networking.nat.enable = true;
           networking.nat.internalIPs = [ "192.168.1.0/24" ];
           networking.nat.externalInterface = "eth1";
@@ -27,6 +27,7 @@
       server =
         { config, pkgs, ... }:
         { virtualisation.vlans = [ 2 ];
+          networking.firewall.enable = false;
           services.httpd.enable = true;
           services.httpd.adminAddr = "foo@example.org";
           services.vsftpd.enable = true;
diff --git a/nixos/tests/nfs.nix b/nixos/tests/nfs.nix
index 51abf57e1b75..864d05626b67 100644
--- a/nixos/tests/nfs.nix
+++ b/nixos/tests/nfs.nix
@@ -1,6 +1,4 @@
-{ version }:
-
-{ pkgs, ... }:
+import ./make-test.nix ({ version, ... }:
 
 let
 
@@ -13,6 +11,7 @@ let
             options = "vers=${toString version}";
           }
         ];
+      networking.firewall.enable = false; # FIXME: only open statd
     };
 
 in
@@ -31,6 +30,7 @@ in
               /data 192.168.1.0/255.255.255.0(rw,no_root_squash,no_subtree_check,fsid=0)
             '';
           services.nfs.server.createMountPoints = true;
+          networking.firewall.enable = false; # FIXME: figure out what ports need to be allowed
         };
     };
 
@@ -82,4 +82,4 @@ in
       die "shutdown took too long ($duration seconds)" if $duration > 30;
     '';
 
-}
+})
diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix
index 49d92fbde908..0b9714c275da 100644
--- a/nixos/tests/openssh.nix
+++ b/nixos/tests/openssh.nix
@@ -1,6 +1,5 @@
-{ pkgs, ... }:
+import ./make-test.nix ({ pkgs, ... }: {
 
-{
   nodes = {
 
     server =
@@ -35,4 +34,4 @@
     $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2");
     $client->succeed("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024");
   '';
-}
+})
diff --git a/nixos/tests/partition.nix b/nixos/tests/partition.nix
index 7126e7255ef4..309afa4ce9d0 100644
--- a/nixos/tests/partition.nix
+++ b/nixos/tests/partition.nix
@@ -1,4 +1,4 @@
-{ pkgs, system, ... }:
+import ./make-test.nix ({ pkgs, ... }:
 
 with pkgs.lib;
 
@@ -224,4 +224,4 @@ in {
       ensureMountPoint("/mnt/boot");
     };
   '';
-}
+})
diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix
index 13cc3115d50e..9b96e3d7b20c 100644
--- a/nixos/tests/printing.nix
+++ b/nixos/tests/printing.nix
@@ -1,8 +1,6 @@
 # Test printing via CUPS.
 
-{ pkgs, ... }:
-
-{
+import ./make-test.nix ({pkgs, ... }: {
 
   nodes = {
 
@@ -17,6 +15,7 @@
               Allow from all
             </Location>
           '';
+        networking.firewall.allowedTCPPorts = [ 631 ];
       };
 
     client =
@@ -32,12 +31,14 @@
 
       # Make sure that cups is up on both sides.
       $server->waitForUnit("cupsd.service");
+      $server->waitForUnit("network.target");
       $client->waitForUnit("cupsd.service");
+      $client->waitForUnit("network.target");
       $client->succeed("lpstat -r") =~ /scheduler is running/ or die;
       $client->succeed("lpstat -H") =~ "/var/run/cups/cups.sock" or die;
       $client->succeed("curl --fail http://localhost:631/");
       $client->succeed("curl --fail http://server:631/");
-      $server->fail("curl --fail http://client:631/");
+      $server->fail("curl --fail --connect-timeout 2  http://client:631/");
 
       # Add a HP Deskjet printer connected via USB to the server.
       $server->succeed("lpadmin -p DeskjetLocal -v usb://HP/Deskjet%205400%20series?serial=TH93I152S123XY -m 'drv:///sample.drv/deskjet.ppd' -E");
@@ -87,4 +88,4 @@
       }
     '';
 
-}
+})
diff --git a/nixos/tests/proxy.nix b/nixos/tests/proxy.nix
index 3b79c16ea2c4..88dbdb2720fb 100644
--- a/nixos/tests/proxy.nix
+++ b/nixos/tests/proxy.nix
@@ -1,16 +1,14 @@
-{ pkgs, ... }:
+import ./make-test.nix (
 
 let
 
   backend =
     { config, pkgs, ... }:
 
-    {
-      services.openssh.enable = true;
-
-      services.httpd.enable = true;
+    { services.httpd.enable = true;
       services.httpd.adminAddr = "foo@example.org";
       services.httpd.documentRoot = "${pkgs.valgrind}/share/doc/valgrind/html";
+      networking.firewall.allowedTCPPorts = [ 80 ];
     };
 
 in
@@ -21,8 +19,7 @@ in
     { proxy =
         { config, pkgs, nodes, ... }:
 
-        {
-          services.httpd.enable = true;
+        { services.httpd.enable = true;
           services.httpd.adminAddr = "bar@example.org";
           services.httpd.extraModules = ["proxy_balancer"];
 
@@ -50,6 +47,8 @@ in
               # For testing; don't want to wait forever for dead backend servers.
               ProxyTimeout      5
             '';
+
+          networking.firewall.allowedTCPPorts = [ 80 ];
         };
 
       backend1 = backend;
@@ -91,4 +90,4 @@ in
       $client->succeed("curl --fail http://proxy/");
     '';
 
-}
+})
diff --git a/nixos/tests/quake3.nix b/nixos/tests/quake3.nix
index 925011077805..3ff12fd57c06 100644
--- a/nixos/tests/quake3.nix
+++ b/nixos/tests/quake3.nix
@@ -1,4 +1,4 @@
-{ pkgs, ... }:
+import ./make-test.nix (
 
 let
 
@@ -14,11 +14,13 @@ in
 
 rec {
 
+  makeCoverageReport = true;
+
   client =
     { config, pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      services.xserver.driSupport = true;
+      hardware.opengl.driSupport = true;
       services.xserver.defaultDepth = pkgs.lib.mkOverride 0 16;
       environment.systemPackages = [ pkgs.quake3demo ];
       nixpkgs.config.packageOverrides = overrides;
@@ -35,6 +37,7 @@ rec {
                 "'+map q3dm7' '+addbot grunt' '+addbot daemia' 2> /tmp/log";
             };
           nixpkgs.config.packageOverrides = overrides;
+          networking.firewall.allowedUDPPorts = [ 27960 ];
         };
 
       client1 = client;
@@ -76,4 +79,4 @@ rec {
       $server->stopJob("quake3-server");
     '';
 
-}
+})
diff --git a/nixos/tests/rabbitmq.nix b/nixos/tests/rabbitmq.nix
new file mode 100644
index 000000000000..ffcdde9d87f2
--- /dev/null
+++ b/nixos/tests/rabbitmq.nix
@@ -0,0 +1,17 @@
+# This test runs rabbitmq and checks if rabbitmq is up and running.
+
+import ./make-test.nix ({ pkgs, ... }: {
+
+  nodes = {
+    one = { config, pkgs, ... }: {
+      services.rabbitmq.enable = true;
+    };
+  };
+
+  testScript = ''
+    startAll;
+
+    $one->waitForUnit("rabbitmq.service");
+    $one->waitUntilSucceeds("su -s ${pkgs.stdenv.shell} rabbitmq -c \"rabbitmqctl status\"");
+  '';
+})
diff --git a/nixos/tests/run-in-machine.nix b/nixos/tests/run-in-machine.nix
index 8efe26c17082..7f6e6a6dc573 100644
--- a/nixos/tests/run-in-machine.nix
+++ b/nixos/tests/run-in-machine.nix
@@ -2,7 +2,9 @@
 
 with import ../lib/testing.nix { inherit system; };
 
-runInMachine {
-  drv = pkgs.patchelf;
-  machine = { config, pkgs, ... }: { services.sshd.enable = true; };
+{
+  test = runInMachine {
+    drv = pkgs.hello;
+    machine = { config, pkgs, ... }: { /* services.sshd.enable = true; */ };
+  };
 }
diff --git a/nixos/tests/simple.nix b/nixos/tests/simple.nix
index eee13a101334..e21b919cdf80 100644
--- a/nixos/tests/simple.nix
+++ b/nixos/tests/simple.nix
@@ -1,11 +1,11 @@
-{ pkgs, ... }:
+import ./make-test.nix {
 
-{
   machine = { config, pkgs, ... }: { };
 
   testScript =
     ''
       startAll;
+      $machine->waitForUnit("multi-user.target");
       $machine->shutdown;
     '';
 }
diff --git a/nixos/tests/subversion.nix b/nixos/tests/subversion.nix
index 309da90c5df1..e6746dc08287 100644
--- a/nixos/tests/subversion.nix
+++ b/nixos/tests/subversion.nix
@@ -1,4 +1,4 @@
-{ pkgs, ... }:
+import ./make-test.nix (
 
 let
 
@@ -20,7 +20,7 @@ let
         # To build the kernel with coverage instrumentation, we need a
         # special patch to make coverage data available under /proc.
         linux = pkgs.linux.override (orig: {
-          stdenv = cleanupBuildTree (keepBuildTree orig.stdenv);
+          stdenv = overrideInStdenv pkgs.stdenv [ pkgs.keepBuildTree ];
           extraConfig =
             ''
               GCOV_KERNEL y
@@ -114,4 +114,4 @@ in
       $webserver->stopJob("httpd");
     '';
 
-}
+})
diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix
index 6bc88ec82fa2..3b0b1bb79117 100644
--- a/nixos/tests/tomcat.nix
+++ b/nixos/tests/tomcat.nix
@@ -1,17 +1,15 @@
-{ pkgs, ... }:
+import ./make-test.nix {
 
-{
   nodes = {
     server =
       { pkgs, config, ... }:
 
-      {
-        services.tomcat.enable = true;
+      { services.tomcat.enable = true;
         services.httpd.enable = true;
         services.httpd.adminAddr = "foo@bar.com";
-        services.httpd.extraSubservices = [
-          { serviceType = "tomcat-connector"; }
-        ];
+        services.httpd.extraSubservices =
+          [ { serviceType = "tomcat-connector"; } ];
+        networking.firewall.allowedTCPPorts = [ 80 ];
       };
 
     client = { };
@@ -26,4 +24,5 @@
     $client->succeed("curl --fail http://server/examples/servlets/servlet/HelloWorldExample");
     $client->succeed("curl --fail http://server/examples/jsp/jsp2/simpletag/hello.jsp");
   '';
+
 }
diff --git a/nixos/tests/trac.nix b/nixos/tests/trac.nix
index e0d256f57019..3f17dafaca15 100644
--- a/nixos/tests/trac.nix
+++ b/nixos/tests/trac.nix
@@ -1,6 +1,5 @@
-{ pkgs, ... }:
+import ./make-test.nix ({ pkgs, ... }: {
 
-{
   nodes = {
     storage =
       { config, pkgs, ... }:
@@ -68,4 +67,4 @@
 
       $client->screenshot("screen");
     '';
-}
+})
diff --git a/nixos/tests/udisks.nix b/nixos/tests/udisks.nix
new file mode 100644
index 000000000000..b7f2e2c00315
--- /dev/null
+++ b/nixos/tests/udisks.nix
@@ -0,0 +1,56 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+let
+
+  stick = pkgs.fetchurl {
+    url = http://nixos.org/~eelco/nix/udisks-test.img.xz;
+    sha256 = "0was1xgjkjad91nipzclaz5biv3m4b2nk029ga6nk7iklwi19l8b";
+  };
+
+in
+
+{
+
+  machine =
+    { config, pkgs, ... }:
+    { services.udisks.enable = true;
+      imports = [ ./common/user-account.nix ];
+
+      security.polkit.extraConfig =
+        ''
+          polkit.addRule(function(action, subject) {
+            if (subject.user == "alice") return "yes";
+          });
+        '';
+    };
+
+  testScript =
+    ''
+      my $stick = $machine->stateDir . "/usbstick.img";
+      system("xz -d < ${stick} > $stick") == 0 or die;
+
+      $machine->succeed("udisks --enumerate | grep /org/freedesktop/UDisks/devices/vda");
+      $machine->fail("udisks --enumerate | grep /org/freedesktop/UDisks/devices/sda1");
+
+      # Attach a USB stick and wait for it to show up.
+      $machine->sendMonitorCommand("usb_add disk:$stick");
+      $machine->waitUntilSucceeds("udisks --enumerate | grep /org/freedesktop/UDisks/devices/sda1");
+      $machine->succeed("udisks --show-info /dev/sda1 | grep 'label:.*USBSTICK'");
+
+      # Mount the stick as a non-root user and do some stuff with it.
+      $machine->succeed("su - alice -c 'udisks --enumerate | grep /org/freedesktop/UDisks/devices/sda1'");
+      $machine->succeed("su - alice -c 'udisks --mount /dev/sda1'");
+      $machine->succeed("su - alice -c 'cat /media/USBSTICK/test.txt'") =~ /Hello World/ or die;
+      $machine->succeed("su - alice -c 'echo foo > /media/USBSTICK/bar.txt'");
+
+      # Unmounting the stick should make the mountpoint disappear.
+      $machine->succeed("su - alice -c 'udisks --unmount /dev/sda1'");
+      $machine->fail("[ -d /media/USBSTICK ]");
+
+      # Remove the USB stick.
+      $machine->sendMonitorCommand("usb_del 0.3"); # FIXME
+      $machine->waitUntilFails("udisks --enumerate | grep /org/freedesktop/UDisks/devices/sda1");
+      $machine->fail("[ -e /dev/sda ]");
+    '';
+
+})
diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix
new file mode 100644
index 000000000000..e0c57d7c34d6
--- /dev/null
+++ b/nixos/tests/udisks2.nix
@@ -0,0 +1,56 @@
+import ./make-test.nix ({ pkgs, ... }:
+
+let
+
+  stick = pkgs.fetchurl {
+    url = http://nixos.org/~eelco/nix/udisks-test.img.xz;
+    sha256 = "0was1xgjkjad91nipzclaz5biv3m4b2nk029ga6nk7iklwi19l8b";
+  };
+
+in
+
+{
+
+  machine =
+    { config, pkgs, ... }:
+    { services.udisks2.enable = true;
+      imports = [ ./common/user-account.nix ];
+
+      security.polkit.extraConfig =
+        ''
+          polkit.addRule(function(action, subject) {
+            if (subject.user == "alice") return "yes";
+          });
+        '';
+    };
+
+  testScript =
+    ''
+      my $stick = $machine->stateDir . "/usbstick.img";
+      system("xz -d < ${stick} > $stick") == 0 or die;
+
+      $machine->succeed("udisksctl info -b /dev/vda >&2");
+      $machine->fail("udisksctl info -b /dev/sda1");
+
+      # Attach a USB stick and wait for it to show up.
+      $machine->sendMonitorCommand("usb_add disk:$stick");
+      $machine->waitUntilSucceeds("udisksctl info -b /dev/sda1");
+      $machine->succeed("udisksctl info -b /dev/sda1 | grep 'IdLabel:.*USBSTICK'");
+
+      # Mount the stick as a non-root user and do some stuff with it.
+      $machine->succeed("su - alice -c 'udisksctl info -b /dev/sda1'");
+      $machine->succeed("su - alice -c 'udisksctl mount -b /dev/sda1'");
+      $machine->succeed("su - alice -c 'cat /run/media/alice/USBSTICK/test.txt'") =~ /Hello World/ or die;
+      $machine->succeed("su - alice -c 'echo foo > /run/media/alice/USBSTICK/bar.txt'");
+
+      # Unmounting the stick should make the mountpoint disappear.
+      $machine->succeed("su - alice -c 'udisksctl unmount -b /dev/sda1'");
+      $machine->fail("[ -d /run/media/alice/USBSTICK ]");
+
+      # Remove the USB stick.
+      $machine->sendMonitorCommand("usb_del 0.3"); # FIXME
+      $machine->waitUntilFails("udisksctl info -b /dev/sda1");
+      $machine->fail("[ -e /dev/sda ]");
+    '';
+
+})
diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix
index 9f9692f8a014..ded37943e51d 100644
--- a/nixos/tests/xfce.nix
+++ b/nixos/tests/xfce.nix
@@ -1,6 +1,4 @@
-{ pkgs, ... }:
-
-{
+import ./make-test.nix {
 
   machine =
     { config, pkgs, ... }:
@@ -17,6 +15,7 @@
 
   testScript =
     ''
+      $machine->waitForX;
       $machine->waitForWindow(qr/xfce4-panel/);
       $machine->sleep(10);