summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2013-10-10 13:28:20 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2013-10-10 13:28:20 +0200
commit5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010 (patch)
treea6c0f605be6de3f372ae69905b331f9f75452da7 /nixos/modules
parent6070bc016bd2fd945b04347e25cfd3738622d2ac (diff)
downloadnixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.gz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.bz2
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.lz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.xz
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.tar.zst
nixpkgs-5c1f8cbc70cd5e6867ef6a2a06d27a40daa07010.zip
Move all of NixOS to nixos/ in preparation of the repository merge
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/fonts/corefonts.nix32
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix58
-rw-r--r--nixos/modules/config/fonts/fontdir.nix75
-rw-r--r--nixos/modules/config/fonts/fonts.nix49
-rw-r--r--nixos/modules/config/fonts/ghostscript.nix32
-rw-r--r--nixos/modules/config/gnu.nix45
-rw-r--r--nixos/modules/config/i18n.nix84
-rw-r--r--nixos/modules/config/krb5.nix204
-rw-r--r--nixos/modules/config/ldap.nix246
-rw-r--r--nixos/modules/config/networking.nix88
-rw-r--r--nixos/modules/config/no-x-libs.nix23
-rw-r--r--nixos/modules/config/nsswitch.nix63
-rw-r--r--nixos/modules/config/power-management.nix108
-rw-r--r--nixos/modules/config/pulseaudio.nix148
-rw-r--r--nixos/modules/config/shells-environment.nix179
-rw-r--r--nixos/modules/config/swap.nix124
-rw-r--r--nixos/modules/config/sysctl.nix69
-rw-r--r--nixos/modules/config/system-path.nix142
-rw-r--r--nixos/modules/config/timezone.nix36
-rw-r--r--nixos/modules/config/unix-odbc-drivers.nix34
-rw-r--r--nixos/modules/config/users-groups.nix315
-rw-r--r--nixos/modules/hardware/all-firmware.nix26
-rw-r--r--nixos/modules/hardware/cpu/amd-microcode.nix29
-rw-r--r--nixos/modules/hardware/cpu/intel-microcode.nix29
-rw-r--r--nixos/modules/hardware/network/b43.nix32
-rw-r--r--nixos/modules/hardware/network/broadcom-43xx.nix3
-rw-r--r--nixos/modules/hardware/network/intel-2030.nix3
-rw-r--r--nixos/modules/hardware/network/intel-2100bg.nix30
-rw-r--r--nixos/modules/hardware/network/intel-2200bg.nix30
-rw-r--r--nixos/modules/hardware/network/intel-3945abg.nix29
-rw-r--r--nixos/modules/hardware/network/intel-4965agn.nix3
-rw-r--r--nixos/modules/hardware/network/intel-5000.nix3
-rw-r--r--nixos/modules/hardware/network/intel-5150.nix3
-rw-r--r--nixos/modules/hardware/network/intel-6000.nix3
-rw-r--r--nixos/modules/hardware/network/intel-6000g2a.nix3
-rw-r--r--nixos/modules/hardware/network/intel-6000g2b.nix3
-rw-r--r--nixos/modules/hardware/network/ralink.nix26
-rw-r--r--nixos/modules/hardware/network/rtl8192c.nix26
-rw-r--r--nixos/modules/hardware/network/smc-2632w/default.nix9
-rw-r--r--nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis8
-rw-r--r--nixos/modules/hardware/network/zydas-zd1211.nix5
-rw-r--r--nixos/modules/hardware/pcmcia.nix59
-rw-r--r--nixos/modules/hardware/video/encoder/wis-go7007.nix15
-rw-r--r--nixos/modules/hardware/video/radeon.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/channel.nix43
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix37
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-efi.nix14
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical.nix30
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix8
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-minimal.nix11
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-new-kernel.nix8
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix315
-rw-r--r--nixos/modules/installer/cd-dvd/live-dvd.nix78
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix164
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt89
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-pc.nix164
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix176
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball.nix92
-rw-r--r--nixos/modules/installer/scan/detected.nix13
-rw-r--r--nixos/modules/installer/scan/not-detected.nix9
-rw-r--r--nixos/modules/installer/tools/get-version-suffix29
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/build-vms.nix9
-rw-r--r--nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh62
-rw-r--r--nixos/modules/installer/tools/nixos-checkout.nix55
-rw-r--r--nixos/modules/installer/tools/nixos-gen-seccure-keys.sh13
-rw-r--r--nixos/modules/installer/tools/nixos-hardware-scan.pl248
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh238
-rw-r--r--nixos/modules/installer/tools/nixos-option.sh395
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh222
-rw-r--r--nixos/modules/installer/tools/nixos-version.sh2
-rw-r--r--nixos/modules/installer/tools/tools.nix112
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix19
-rw-r--r--nixos/modules/misc/assertions.nix37
-rw-r--r--nixos/modules/misc/check-config.nix13
-rw-r--r--nixos/modules/misc/crashdump.nix77
-rw-r--r--nixos/modules/misc/ids.nix201
-rw-r--r--nixos/modules/misc/lib.nix15
-rw-r--r--nixos/modules/misc/locate.nix62
-rw-r--r--nixos/modules/misc/nixpkgs.nix87
-rw-r--r--nixos/modules/misc/passthru.nix15
-rw-r--r--nixos/modules/misc/version.nix55
-rw-r--r--nixos/modules/module-list.nix278
-rw-r--r--nixos/modules/profiles/all-hardware.nix55
-rw-r--r--nixos/modules/profiles/base.nix55
-rw-r--r--nixos/modules/profiles/clone-config.nix100
-rw-r--r--nixos/modules/profiles/demo.nix16
-rw-r--r--nixos/modules/profiles/graphical.nix14
-rw-r--r--nixos/modules/profiles/headless.nix21
-rw-r--r--nixos/modules/profiles/installation-device.nix56
-rw-r--r--nixos/modules/profiles/minimal.nix11
-rw-r--r--nixos/modules/profiles/qemu-guest.nix9
-rw-r--r--nixos/modules/programs/atop.nix36
-rw-r--r--nixos/modules/programs/bash/bash.nix217
-rw-r--r--nixos/modules/programs/bash/command-not-found.nix51
-rw-r--r--nixos/modules/programs/bash/command-not-found.pl48
-rw-r--r--nixos/modules/programs/bash/inputrc36
-rw-r--r--nixos/modules/programs/blcr.nix27
-rw-r--r--nixos/modules/programs/environment.nix79
-rw-r--r--nixos/modules/programs/info.nix36
-rw-r--r--nixos/modules/programs/shadow.nix103
-rw-r--r--nixos/modules/programs/shell.nix64
-rw-r--r--nixos/modules/programs/ssh.nix68
-rw-r--r--nixos/modules/programs/ssmtp.nix111
-rw-r--r--nixos/modules/programs/venus.nix174
-rw-r--r--nixos/modules/programs/virtualbox.nix47
-rw-r--r--nixos/modules/programs/wvdial.nix71
-rw-r--r--nixos/modules/programs/zsh/zinputrc42
-rw-r--r--nixos/modules/programs/zsh/zsh.nix180
-rw-r--r--nixos/modules/rename.nix115
-rw-r--r--nixos/modules/security/apparmor-suid.nix46
-rw-r--r--nixos/modules/security/apparmor.nix68
-rw-r--r--nixos/modules/security/ca.nix26
-rw-r--r--nixos/modules/security/pam.nix286
-rw-r--r--nixos/modules/security/pam_usb.nix41
-rw-r--r--nixos/modules/security/polkit.nix121
-rw-r--r--nixos/modules/security/rngd.nix37
-rw-r--r--nixos/modules/security/rtkit.nix39
-rw-r--r--nixos/modules/security/setuid-wrapper.c81
-rw-r--r--nixos/modules/security/setuid-wrappers.nix121
-rw-r--r--nixos/modules/security/sudo.nix90
-rw-r--r--nixos/modules/services/amqp/activemq/ActiveMQBroker.java19
-rw-r--r--nixos/modules/services/amqp/activemq/default.nix131
-rw-r--r--nixos/modules/services/amqp/rabbitmq.nix94
-rw-r--r--nixos/modules/services/audio/alsa.nix67
-rw-r--r--nixos/modules/services/audio/fuppes.nix114
-rw-r--r--nixos/modules/services/audio/fuppes/vfolder.cfg155
-rw-r--r--nixos/modules/services/audio/mpd.nix92
-rw-r--r--nixos/modules/services/backup/almir.nix171
-rw-r--r--nixos/modules/services/backup/bacula.nix408
-rw-r--r--nixos/modules/services/backup/mysql-backup.nix81
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix66
-rw-r--r--nixos/modules/services/backup/rsnapshot.nix65
-rw-r--r--nixos/modules/services/backup/sitecopy-backup.nix104
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix72
-rw-r--r--nixos/modules/services/databases/4store.nix71
-rw-r--r--nixos/modules/services/databases/firebird.nix149
-rw-r--r--nixos/modules/services/databases/memcached.nix97
-rw-r--r--nixos/modules/services/databases/mongodb.nix130
-rw-r--r--nixos/modules/services/databases/mysql.nix245
-rw-r--r--nixos/modules/services/databases/mysql55.nix248
-rw-r--r--nixos/modules/services/databases/openldap.nix58
-rw-r--r--nixos/modules/services/databases/postgresql.nix232
-rw-r--r--nixos/modules/services/databases/redis.nix216
-rw-r--r--nixos/modules/services/databases/virtuoso.nix98
-rw-r--r--nixos/modules/services/games/ghost-one.nix105
-rw-r--r--nixos/modules/services/hardware/acpid.nix114
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix41
-rw-r--r--nixos/modules/services/hardware/nvidia-optimus.nix43
-rw-r--r--nixos/modules/services/hardware/pcscd.nix46
-rw-r--r--nixos/modules/services/hardware/pommed.nix49
-rw-r--r--nixos/modules/services/hardware/sane.nix40
-rw-r--r--nixos/modules/services/hardware/thinkfan.nix95
-rw-r--r--nixos/modules/services/hardware/udev.nix239
-rw-r--r--nixos/modules/services/hardware/udisks.nix44
-rw-r--r--nixos/modules/services/hardware/udisks2.nix53
-rw-r--r--nixos/modules/services/hardware/upower.nix64
-rw-r--r--nixos/modules/services/logging/klogd.nix42
-rw-r--r--nixos/modules/services/logging/logcheck.nix231
-rw-r--r--nixos/modules/services/logging/logrotate.nix38
-rw-r--r--nixos/modules/services/logging/logstash.nix161
-rw-r--r--nixos/modules/services/logging/rsyslogd.nix105
-rw-r--r--nixos/modules/services/logging/syslogd.nix124
-rw-r--r--nixos/modules/services/mail/dovecot.nix168
-rw-r--r--nixos/modules/services/mail/freepops.nix87
-rw-r--r--nixos/modules/services/mail/mail.nix33
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix83
-rw-r--r--nixos/modules/services/mail/postfix.nix405
-rw-r--r--nixos/modules/services/mail/spamassassin.nix64
-rw-r--r--nixos/modules/services/misc/autofs.nix120
-rw-r--r--nixos/modules/services/misc/cgminer.nix140
-rw-r--r--nixos/modules/services/misc/disnix.nix164
-rw-r--r--nixos/modules/services/misc/felix.nix110
-rw-r--r--nixos/modules/services/misc/folding-at-home.nix74
-rw-r--r--nixos/modules/services/misc/gpsd.nix104
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix370
-rw-r--r--nixos/modules/services/misc/nix-gc.nix61
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix116
-rw-r--r--nixos/modules/services/misc/rogue.nix59
-rw-r--r--nixos/modules/services/misc/svnserve.nix46
-rw-r--r--nixos/modules/services/misc/synergy.nix131
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix190
-rw-r--r--nixos/modules/services/monitoring/dd-agent.nix83
-rw-r--r--nixos/modules/services/monitoring/graphite.nix265
-rw-r--r--nixos/modules/services/monitoring/monit.nix52
-rw-r--r--nixos/modules/services/monitoring/nagios/commands.cfg34
-rw-r--r--nixos/modules/services/monitoring/nagios/default.nix186
-rw-r--r--nixos/modules/services/monitoring/nagios/host-templates.cfg27
-rw-r--r--nixos/modules/services/monitoring/nagios/service-templates.cfg32
-rw-r--r--nixos/modules/services/monitoring/nagios/timeperiods.cfg11
-rw-r--r--nixos/modules/services/monitoring/smartd.nix117
-rw-r--r--nixos/modules/services/monitoring/statsd.nix94
-rw-r--r--nixos/modules/services/monitoring/systemhealth.nix133
-rw-r--r--nixos/modules/services/monitoring/ups.nix275
-rw-r--r--nixos/modules/services/monitoring/uptime.nix95
-rw-r--r--nixos/modules/services/monitoring/zabbix-agent.nix100
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix113
-rw-r--r--nixos/modules/services/network-filesystems/drbd.nix77
-rw-r--r--nixos/modules/services/network-filesystems/nfsd.nix147
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix90
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix234
-rw-r--r--nixos/modules/services/networking/amuled.nix78
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix144
-rw-r--r--nixos/modules/services/networking/bind.nix152
-rw-r--r--nixos/modules/services/networking/bitlbee.nix123
-rw-r--r--nixos/modules/services/networking/chrony.nix118
-rw-r--r--nixos/modules/services/networking/cntlm.nix115
-rw-r--r--nixos/modules/services/networking/ddclient.nix127
-rw-r--r--nixos/modules/services/networking/dhclient.nix111
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix143
-rw-r--r--nixos/modules/services/networking/dhcpd.nix131
-rw-r--r--nixos/modules/services/networking/dnsmasq.nix70
-rw-r--r--nixos/modules/services/networking/ejabberd.nix135
-rw-r--r--nixos/modules/services/networking/firewall.nix368
-rw-r--r--nixos/modules/services/networking/flashpolicyd.nix84
-rw-r--r--nixos/modules/services/networking/freenet.nix64
-rw-r--r--nixos/modules/services/networking/git-daemon.nix112
-rw-r--r--nixos/modules/services/networking/gnunet.nix148
-rw-r--r--nixos/modules/services/networking/gogoclient.nix88
-rw-r--r--nixos/modules/services/networking/gvpe.nix144
-rw-r--r--nixos/modules/services/networking/hostapd.nix163
-rw-r--r--nixos/modules/services/networking/ifplugd.nix88
-rw-r--r--nixos/modules/services/networking/iodined.nix87
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/builder.sh31
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/control.in26
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/default.nix137
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/ircd.conf1051
-rw-r--r--nixos/modules/services/networking/minidlna.nix112
-rw-r--r--nixos/modules/services/networking/nat.nix104
-rw-r--r--nixos/modules/services/networking/networkmanager.nix193
-rw-r--r--nixos/modules/services/networking/ntpd.nix90
-rw-r--r--nixos/modules/services/networking/oidentd.nix44
-rw-r--r--nixos/modules/services/networking/openfire.nix70
-rw-r--r--nixos/modules/services/networking/openvpn.nix172
-rw-r--r--nixos/modules/services/networking/prayer.nix103
-rw-r--r--nixos/modules/services/networking/privoxy.nix95
-rw-r--r--nixos/modules/services/networking/quassel.nix96
-rw-r--r--nixos/modules/services/networking/radvd.nix77
-rw-r--r--nixos/modules/services/networking/rdnssd.nix48
-rw-r--r--nixos/modules/services/networking/rpcbind.nix81
-rw-r--r--nixos/modules/services/networking/sabnzbd.nix52
-rw-r--r--nixos/modules/services/networking/ssh/lshd.nix175
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix338
-rw-r--r--nixos/modules/services/networking/supybot.nix88
-rw-r--r--nixos/modules/services/networking/tcpcrypt.nix78
-rw-r--r--nixos/modules/services/networking/tftpd.nix43
-rw-r--r--nixos/modules/services/networking/unbound.nix118
-rw-r--r--nixos/modules/services/networking/vsftpd.nix137
-rw-r--r--nixos/modules/services/networking/wakeonlan.nix56
-rw-r--r--nixos/modules/services/networking/websockify.nix54
-rw-r--r--nixos/modules/services/networking/wicd.nix41
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix136
-rw-r--r--nixos/modules/services/networking/xinetd.nix158
-rw-r--r--nixos/modules/services/printing/cupsd.nix223
-rw-r--r--nixos/modules/services/scheduling/atd.nix111
-rw-r--r--nixos/modules/services/scheduling/cron.nix111
-rw-r--r--nixos/modules/services/scheduling/fcron.nix126
-rw-r--r--nixos/modules/services/search/elasticsearch.nix115
-rw-r--r--nixos/modules/services/security/clamav.nix80
-rw-r--r--nixos/modules/services/security/fail2ban.nix150
-rw-r--r--nixos/modules/services/security/fprot.nix88
-rw-r--r--nixos/modules/services/security/frandom.nix31
-rw-r--r--nixos/modules/services/security/tor.nix321
-rw-r--r--nixos/modules/services/security/torify.nix69
-rw-r--r--nixos/modules/services/security/torsocks.nix85
-rw-r--r--nixos/modules/services/system/dbus.nix194
-rw-r--r--nixos/modules/services/system/kerberos.nix71
-rw-r--r--nixos/modules/services/system/nscd.conf28
-rw-r--r--nixos/modules/services/system/nscd.nix71
-rw-r--r--nixos/modules/services/system/uptimed.nix68
-rw-r--r--nixos/modules/services/torrent/deluge.nix65
-rw-r--r--nixos/modules/services/torrent/transmission.nix173
-rw-r--r--nixos/modules/services/ttys/agetty.nix127
-rw-r--r--nixos/modules/services/ttys/gpm.nix51
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix662
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mediawiki.nix303
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mercurial.nix75
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/per-server-options.nix146
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix95
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/trac.nix121
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/zabbix.nix82
-rw-r--r--nixos/modules/services/web-servers/jboss/builder.sh72
-rw-r--r--nixos/modules/services/web-servers/jboss/default.nix83
-rw-r--r--nixos/modules/services/web-servers/lighttpd/cgit.nix65
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix178
-rw-r--r--nixos/modules/services/web-servers/lighttpd/gitweb.nix67
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix88
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix344
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix63
-rw-r--r--nixos/modules/services/web-servers/zope2.nix249
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix75
-rw-r--r--nixos/modules/services/x11/desktop-managers/e17.nix30
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome.nix42
-rw-r--r--nixos/modules/services/x11/desktop-managers/kde4.nix169
-rw-r--r--nixos/modules/services/x11/desktop-managers/none.nix7
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix90
-rw-r--r--nixos/modules/services/x11/desktop-managers/xterm.nix36
-rw-r--r--nixos/modules/services/x11/display-managers/auto.nix52
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix283
-rw-r--r--nixos/modules/services/x11/display-managers/kdm.nix151
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix119
-rw-r--r--nixos/modules/services/x11/display-managers/slim.nix113
-rw-r--r--nixos/modules/services/x11/hardware/multitouch.nix60
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix122
-rw-r--r--nixos/modules/services/x11/hardware/wacom.nix47
-rw-r--r--nixos/modules/services/x11/terminal-server.nix66
-rw-r--r--nixos/modules/services/x11/window-managers/awesome.nix42
-rw-r--r--nixos/modules/services/x11/window-managers/compiz.nix63
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix61
-rw-r--r--nixos/modules/services/x11/window-managers/i3.nix43
-rw-r--r--nixos/modules/services/x11/window-managers/icewm.nix42
-rw-r--r--nixos/modules/services/x11/window-managers/metacity.nix42
-rw-r--r--nixos/modules/services/x11/window-managers/none.nix12
-rw-r--r--nixos/modules/services/x11/window-managers/openbox.nix30
-rw-r--r--nixos/modules/services/x11/window-managers/twm.nix42
-rw-r--r--nixos/modules/services/x11/window-managers/wmii.nix47
-rw-r--r--nixos/modules/services/x11/window-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix30
-rw-r--r--nixos/modules/services/x11/xfs.conf15
-rw-r--r--nixos/modules/services/x11/xfs.nix46
-rw-r--r--nixos/modules/services/x11/xserver.nix641
-rw-r--r--nixos/modules/system/activation/activation-script.nix155
-rw-r--r--nixos/modules/system/activation/no-clone.nix11
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl362
-rw-r--r--nixos/modules/system/activation/top-level.nix194
-rw-r--r--nixos/modules/system/boot/kernel.nix304
-rw-r--r--nixos/modules/system/boot/kexec.nix21
-rw-r--r--nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh131
-rw-r--r--nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix98
-rw-r--r--nixos/modules/system/boot/loader/efi.nix49
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh106
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix63
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix261
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl265
-rw-r--r--nixos/modules/system/boot/loader/grub/memtest.nix39
-rw-r--r--nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.pngbin0 -> 74487 bytes
-rw-r--r--nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README6
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py114
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot.nix67
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script-builder.sh88
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script.nix50
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/builder.sh109
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix38
-rw-r--r--nixos/modules/system/boot/luksroot.nix176
-rw-r--r--nixos/modules/system/boot/modprobe.nix112
-rw-r--r--nixos/modules/system/boot/readonly-mountpoint.c20
-rw-r--r--nixos/modules/system/boot/shutdown.nix27
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh374
-rw-r--r--nixos/modules/system/boot/stage-1.nix343
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh173
-rw-r--r--nixos/modules/system/boot/stage-2.nix100
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix364
-rw-r--r--nixos/modules/system/boot/systemd.nix678
-rw-r--r--nixos/modules/system/etc/etc.nix117
-rw-r--r--nixos/modules/system/etc/make-etc.sh42
-rw-r--r--nixos/modules/system/etc/setup-etc.pl68
-rw-r--r--nixos/modules/system/upstart/upstart.nix286
-rw-r--r--nixos/modules/tasks/cpu-freq.nix51
-rw-r--r--nixos/modules/tasks/filesystems.nix216
-rw-r--r--nixos/modules/tasks/filesystems/btrfs.nix47
-rw-r--r--nixos/modules/tasks/filesystems/ext.nix22
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix94
-rw-r--r--nixos/modules/tasks/filesystems/reiserfs.nix25
-rw-r--r--nixos/modules/tasks/filesystems/unionfs-fuse.nix24
-rw-r--r--nixos/modules/tasks/filesystems/vfat.nix25
-rw-r--r--nixos/modules/tasks/filesystems/xfs.nix29
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix94
-rw-r--r--nixos/modules/tasks/kbd.nix73
-rw-r--r--nixos/modules/tasks/lvm.nix15
-rw-r--r--nixos/modules/tasks/network-interfaces.nix447
-rw-r--r--nixos/modules/tasks/scsi-link-power-management.nix44
-rw-r--r--nixos/modules/tasks/swraid.nix11
-rw-r--r--nixos/modules/tasks/tty-backgrounds-combine.sh32
-rw-r--r--nixos/modules/testing/minimal-kernel.nix28
-rw-r--r--nixos/modules/testing/test-instrumentation.nix91
-rw-r--r--nixos/modules/virtualisation/amazon-config.nix5
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix163
-rw-r--r--nixos/modules/virtualisation/ec2-data.nix99
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix148
-rw-r--r--nixos/modules/virtualisation/nova-config.nix5
-rw-r--r--nixos/modules/virtualisation/nova-image.nix115
-rw-r--r--nixos/modules/virtualisation/nova.nix174
-rw-r--r--nixos/modules/virtualisation/qemu-opts4
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix421
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix91
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix110
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix179
-rw-r--r--nixos/modules/virtualisation/xen-domU.nix19
387 files changed, 41143 insertions, 0 deletions
diff --git a/nixos/modules/config/fonts/corefonts.nix b/nixos/modules/config/fonts/corefonts.nix
new file mode 100644
index 00000000000..7de95200cfa
--- /dev/null
+++ b/nixos/modules/config/fonts/corefonts.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableCoreFonts = mkOption {
+        default = false;
+        description = ''
+          Whether to include Microsoft's proprietary Core Fonts.  These fonts
+          are redistributable, but only verbatim, among other restrictions.
+          See <link xlink:href="http://corefonts.sourceforge.net/eula.htm"/>
+          for details.
+       '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableCoreFonts {
+
+    fonts.extraFonts = [ pkgs.corefonts ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
new file mode 100644
index 00000000000..6e0fdaf4b74
--- /dev/null
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -0,0 +1,58 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableFontConfig = mkOption { # !!! should be enableFontconfig
+        default = true;
+        description = ''
+          If enabled, a Fontconfig configuration file will be built
+          pointing to a set of default fonts.  If you don't care about
+          running X11 applications or any other program that uses
+          Fontconfig, you can turn this option off and prevent a
+          dependency on all those fonts.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableFontConfig {
+
+    # Bring in the default (upstream) fontconfig configuration.
+    environment.etc."fonts/fonts.conf".source =
+      pkgs.makeFontsConf { fontDirectories = config.fonts.fonts; };
+
+    environment.etc."fonts/conf.d/00-nixos.conf".text =
+      ''
+        <?xml version='1.0'?>
+        <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
+        <fontconfig>
+
+          <!-- Set the default hinting style to "slight". -->
+          <match target="font">
+            <edit mode="assign" name="hintstyle">
+              <const>hintslight</const>
+            </edit>
+          </match>
+
+        </fontconfig>
+      '';
+
+    # FIXME: This variable is no longer needed, but we'll keep it
+    # around for a while for applications linked against old
+    # fontconfig builds.
+    environment.variables.FONTCONFIG_FILE = "/etc/fonts/fonts.conf";
+
+    environment.systemPackages = [ pkgs.fontconfig ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fontdir.nix b/nixos/modules/config/fonts/fontdir.nix
new file mode 100644
index 00000000000..a4f69809b2a
--- /dev/null
+++ b/nixos/modules/config/fonts/fontdir.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  fontDirs = config.fonts.fonts;
+
+  localDefs = with pkgs.builderDefs; pkgs.builderDefs.passthru.function rec {
+    src = "";/* put a fetchurl here */
+    buildInputs = [pkgs.xorg.mkfontdir pkgs.xorg.mkfontscale];
+    inherit fontDirs;
+    installPhase = fullDepEntry ("
+    list='';
+    for i in ${toString fontDirs} ; do
+      if [ -d \$i/ ]; then
+        list=\"\$list \$i\";
+      fi;
+    done
+    list=\$(find \$list -name fonts.dir -o -name '*.ttf' -o -name '*.otf');
+    fontDirs='';
+    for i in \$list ; do
+      fontDirs=\"\$fontDirs \$(dirname \$i)\";
+    done;
+    mkdir -p \$out/share/X11-fonts/;
+    find \$fontDirs -type f -o -type l | while read i; do
+      j=\"\${i##*/}\"
+      if ! test -e \"\$out/share/X11-fonts/\${j}\"; then
+        ln -s \"\$i\" \"\$out/share/X11-fonts/\${j}\";
+      fi;
+    done;
+    cd \$out/share/X11-fonts/
+    rm fonts.dir
+    rm fonts.scale
+    rm fonts.alias
+    mkfontdir
+    mkfontscale
+    cat \$( find ${pkgs.xorg.fontalias}/ -name fonts.alias) >fonts.alias
+  ") ["minInit" "addInputs"];
+  };
+
+  x11Fonts = with localDefs; stdenv.mkDerivation rec {
+    name = "X11-fonts";
+    builder = writeScript (name + "-builder")
+      (textClosure localDefs
+        [installPhase doForceShare doPropagate]);
+  };
+
+in
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableFontDir = mkOption {
+        default = false;
+        description = ''
+          Whether to create a directory with links to all fonts in
+          <filename>/run/current-system/sw/share/X11-fonts</filename>.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf config.fonts.enableFontDir {
+
+    environment.systemPackages = [ x11Fonts ];
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/fonts.nix b/nixos/modules/config/fonts/fonts.nix
new file mode 100644
index 00000000000..f43784f6d03
--- /dev/null
+++ b/nixos/modules/config/fonts/fonts.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      # TODO: find another name for it.
+      fonts = mkOption {
+        default = [
+          # - the user's .fonts directory
+          "~/.fonts"
+          # - the user's current profile
+          "~/.nix-profile/lib/X11/fonts"
+          "~/.nix-profile/share/fonts"
+          # - the default profile
+          "/nix/var/nix/profiles/default/lib/X11/fonts"
+          "/nix/var/nix/profiles/default/share/fonts"
+        ];
+        description = "List of primary font paths.";
+        apply = list: list ++ [
+          # - a few statically built locations
+          pkgs.xorg.fontbhttf
+          pkgs.xorg.fontbhlucidatypewriter100dpi
+          pkgs.xorg.fontbhlucidatypewriter75dpi
+          pkgs.ttf_bitstream_vera
+          pkgs.freefont_ttf
+          pkgs.liberation_ttf
+          pkgs.xorg.fontbh100dpi
+          pkgs.xorg.fontmiscmisc
+          pkgs.xorg.fontcursormisc
+        ]
+        ++ config.fonts.extraFonts;
+      };
+
+      extraFonts = mkOption {
+        default = [];
+        example = [ pkgs.dejavu_fonts ];
+        description = "List of packages with additional fonts.";
+      };
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/config/fonts/ghostscript.nix b/nixos/modules/config/fonts/ghostscript.nix
new file mode 100644
index 00000000000..9ef00396808
--- /dev/null
+++ b/nixos/modules/config/fonts/ghostscript.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    fonts = {
+
+      enableGhostscriptFonts = mkOption {
+        default = false;
+        description = ''
+          Whether to add the fonts provided by Ghostscript (such as
+          various URW fonts and the “Base-14” Postscript fonts) to the
+          list of system fonts, making them available to X11
+          applications.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.fonts.enableGhostscriptFonts {
+
+    fonts.extraFonts = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
+
+  };
+
+}
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix
new file mode 100644
index 00000000000..1a69083a206
--- /dev/null
+++ b/nixos/modules/config/gnu.nix
@@ -0,0 +1,45 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+    gnu = mkOption {
+      default = false;
+      description =
+        '' When enabled, GNU software is chosen by default whenever a there is
+           a choice between GNU and non-GNU software (e.g., GNU lsh
+           vs. OpenSSH).
+        '';
+    };
+  };
+
+  config = mkIf config.gnu {
+
+    environment.systemPackages = with pkgs;
+      # TODO: Adjust `requiredPackages' from `system-path.nix'.
+      # TODO: Add Inetutils once it has the new `ifconfig'.
+      [ parted
+        #fdisk  # XXX: GNU fdisk currently fails to build and it's redundant
+                # with the `parted' command.
+        nano zile
+        texinfo # for the stand-alone Info reader
+      ]
+      ++ stdenv.lib.optional (!stdenv.isArm) grub2;
+
+
+    # GNU GRUB, where available.
+    boot.loader.grub.enable = !pkgs.stdenv.isArm;
+    boot.loader.grub.version = 2;
+
+    # GNU lsh.
+    services.openssh.enable = false;
+    services.lshd.enable = true;
+    services.xserver.startOpenSSHAgent = false;
+    services.xserver.startGnuPGAgent = true;
+
+    # TODO: GNU dico.
+    # TODO: GNU Inetutils' inetd.
+    # TODO: GNU Pies.
+  };
+}
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
new file mode 100644
index 00000000000..c3e39717258
--- /dev/null
+++ b/nixos/modules/config/i18n.nix
@@ -0,0 +1,84 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  glibcLocales = pkgs.glibcLocales.override {
+    allLocales = any (x: x == "all") config.i18n.supportedLocales;
+    locales = config.i18n.supportedLocales;
+  };
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    i18n = {
+      defaultLocale = mkOption {
+        default = "en_US.UTF-8";
+        example = "nl_NL.UTF-8";
+        description = "
+          The default locale.  It determines the language for program
+          messages, the format for dates and times, sort order, and so on.
+          It also determines the character set, such as UTF-8.
+        ";
+      };
+
+      supportedLocales = mkOption {
+        default = ["all"];
+        example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
+        description = ''
+          List of locales that the system should support.  The value
+          <literal>"all"</literal> means that all locales supported by
+          Glibc will be installed.  A full list of supported locales
+          can be found at <link
+          xlink:href="http://sourceware.org/cgi-bin/cvsweb.cgi/libc/localedata/SUPPORTED?cvsroot=glibc"/>.
+        '';
+      };
+
+      consoleFont = mkOption {
+        default = "lat9w-16";
+        example = "LatArCyrHeb-16";
+        description = "
+          The font used for the virtual consoles.  Leave empty to use
+          whatever the <command>setfont</command> program considers the
+          default font.
+        ";
+      };
+
+      consoleKeyMap = mkOption {
+        default = "us";
+        example = "fr";
+        description = "
+          The keyboard mapping table for the virtual consoles.
+        ";
+        type = types.uniq types.string;
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ glibcLocales ];
+
+    environment.variables.LANG = config.i18n.defaultLocale;
+
+    # ‘/etc/locale.conf’ is used by systemd.
+    environment.etc = singleton
+      { target = "locale.conf";
+        source = pkgs.writeText "locale.conf"
+          ''
+            LANG=${config.i18n.defaultLocale}
+          '';
+      };
+
+  };
+}
diff --git a/nixos/modules/config/krb5.nix b/nixos/modules/config/krb5.nix
new file mode 100644
index 00000000000..3323046ac5b
--- /dev/null
+++ b/nixos/modules/config/krb5.nix
@@ -0,0 +1,204 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.krb5;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    krb5 = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Kerberos V.";
+      };
+
+      defaultRealm = mkOption {
+        default = "ATENA.MIT.EDU";
+        description = "Default realm.";
+      };
+
+      domainRealm = mkOption {
+        default = "atena.mit.edu";
+        description = "Default domain realm.";
+      };
+
+      kdc = mkOption {
+        default = "kerberos.mit.edu";
+        description = "Kerberos Domain Controller";
+      };
+
+      kerberosAdminServer = mkOption {
+        default = "kerberos.mit.edu";
+        description = "Kerberos Admin Server";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.krb5.enable {
+
+    environment.systemPackages = [ pkgs.krb5 ];
+
+    environment.etc."krb5.conf".text =
+      ''
+        [libdefaults]
+            default_realm = ${cfg.defaultRealm}
+            encrypt = true
+
+        # The following krb5.conf variables are only for MIT Kerberos.
+            krb4_config = /etc/krb.conf
+            krb4_realms = /etc/krb.realms
+            kdc_timesync = 1
+            ccache_type = 4
+            forwardable = true
+            proxiable = true
+
+        # The following encryption type specification will be used by MIT Kerberos
+        # if uncommented.  In general, the defaults in the MIT Kerberos code are
+        # correct and overriding these specifications only serves to disable new
+        # encryption types as they are added, creating interoperability problems.
+
+        #   default_tgs_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+        #   default_tkt_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+        #   permitted_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
+
+        # The following libdefaults parameters are only for Heimdal Kerberos.
+            v4_instance_resolve = false
+            v4_name_convert = {
+                host = {
+                    rcmd = host
+                    ftp = ftp
+                }
+                plain = {
+                    something = something-else
+                }
+            }
+            fcc-mit-ticketflags = true
+
+        [realms]
+            ${cfg.defaultRealm} = {
+                kdc = ${cfg.kdc}
+                admin_server = ${cfg.kerberosAdminServer}
+                #kpasswd_server = ${cfg.kerberosAdminServer}
+            }
+            ATHENA.MIT.EDU = {
+                kdc = kerberos.mit.edu:88
+                kdc = kerberos-1.mit.edu:88
+                kdc = kerberos-2.mit.edu:88
+                admin_server = kerberos.mit.edu
+                default_domain = mit.edu
+            }
+            MEDIA-LAB.MIT.EDU = {
+                kdc = kerberos.media.mit.edu
+                admin_server = kerberos.media.mit.edu
+            }
+            ZONE.MIT.EDU = {
+                kdc = casio.mit.edu
+                kdc = seiko.mit.edu
+                admin_server = casio.mit.edu
+            }
+            MOOF.MIT.EDU = {
+                kdc = three-headed-dogcow.mit.edu:88
+                kdc = three-headed-dogcow-1.mit.edu:88
+                admin_server = three-headed-dogcow.mit.edu
+            }
+            CSAIL.MIT.EDU = {
+                kdc = kerberos-1.csail.mit.edu
+                kdc = kerberos-2.csail.mit.edu
+                admin_server = kerberos.csail.mit.edu
+                default_domain = csail.mit.edu
+                krb524_server = krb524.csail.mit.edu
+            }
+            IHTFP.ORG = {
+                kdc = kerberos.ihtfp.org
+                admin_server = kerberos.ihtfp.org
+            }
+            GNU.ORG = {
+                kdc = kerberos.gnu.org
+                kdc = kerberos-2.gnu.org
+                kdc = kerberos-3.gnu.org
+                admin_server = kerberos.gnu.org
+            }
+            1TS.ORG = {
+                kdc = kerberos.1ts.org
+                admin_server = kerberos.1ts.org
+            }
+            GRATUITOUS.ORG = {
+                kdc = kerberos.gratuitous.org
+                admin_server = kerberos.gratuitous.org
+            }
+            DOOMCOM.ORG = {
+                kdc = kerberos.doomcom.org
+                admin_server = kerberos.doomcom.org
+            }
+            ANDREW.CMU.EDU = {
+                kdc = vice28.fs.andrew.cmu.edu
+                kdc = vice2.fs.andrew.cmu.edu
+                kdc = vice11.fs.andrew.cmu.edu
+                kdc = vice12.fs.andrew.cmu.edu
+                admin_server = vice28.fs.andrew.cmu.edu
+                default_domain = andrew.cmu.edu
+            }
+            CS.CMU.EDU = {
+                kdc = kerberos.cs.cmu.edu
+                kdc = kerberos-2.srv.cs.cmu.edu
+                admin_server = kerberos.cs.cmu.edu
+            }
+            DEMENTIA.ORG = {
+                kdc = kerberos.dementia.org
+                kdc = kerberos2.dementia.org
+                admin_server = kerberos.dementia.org
+            }
+            stanford.edu = {
+                kdc = krb5auth1.stanford.edu
+                kdc = krb5auth2.stanford.edu
+                kdc = krb5auth3.stanford.edu
+                admin_server = krb5-admin.stanford.edu
+                default_domain = stanford.edu
+            }
+
+        [domain_realm]
+            .${cfg.domainRealm} = ${cfg.defaultRealm}
+            ${cfg.domainRealm} = ${cfg.defaultRealm}
+            .mit.edu = ATHENA.MIT.EDU
+            mit.edu = ATHENA.MIT.EDU
+            .media.mit.edu = MEDIA-LAB.MIT.EDU
+            media.mit.edu = MEDIA-LAB.MIT.EDU
+            .csail.mit.edu = CSAIL.MIT.EDU
+            csail.mit.edu = CSAIL.MIT.EDU
+            .whoi.edu = ATHENA.MIT.EDU
+            whoi.edu = ATHENA.MIT.EDU
+            .stanford.edu = stanford.edu
+
+        [logging]
+            kdc = SYSLOG:INFO:DAEMON
+            admin_server = SYSLOG:INFO:DAEMON
+            default = SYSLOG:INFO:DAEMON
+            krb4_convert = true
+            krb4_get_tickets = false
+
+        [appdefaults]
+            pam = {
+                debug = false
+                ticket_lifetime = 36000
+                renew_lifetime = 36000
+                max_timeout = 30
+                timeout_shift = 2
+                initial_timeout = 1
+            }
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/config/ldap.nix b/nixos/modules/config/ldap.nix
new file mode 100644
index 00000000000..113f5d8bcbd
--- /dev/null
+++ b/nixos/modules/config/ldap.nix
@@ -0,0 +1,246 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+
+  cfg = config.users.ldap;
+
+  # Careful: OpenLDAP seems to be very picky about the indentation of
+  # this file.  Directives HAVE to start in the first column!
+  ldapConfig = {
+    target = "ldap.conf";
+    source = writeText "ldap.conf" ''
+      uri ${config.users.ldap.server}
+      base ${config.users.ldap.base}
+      timelimit ${toString config.users.ldap.timeLimit}
+      bind_timelimit ${toString config.users.ldap.bind.timeLimit}
+      bind_policy ${config.users.ldap.bind.policy}
+      ${optionalString config.users.ldap.useTLS ''
+        ssl start_tls
+        tls_checkpeer no
+      ''}
+      ${optionalString (config.users.ldap.bind.distinguishedName != "") ''
+        binddn ${config.users.ldap.bind.distinguishedName}
+      ''}
+      ${optionalString (cfg.extraConfig != "") cfg.extraConfig }
+    '';
+  };
+
+  nslcdConfig = {
+    target = "nslcd.conf";
+    source = writeText "nslcd.conf" ''
+      uid nslcd
+      gid nslcd
+      uri ${cfg.server}
+      base ${cfg.base}
+      timelimit ${toString cfg.timeLimit}
+      bind_timelimit ${toString cfg.bind.timeLimit}
+      ${optionalString (cfg.bind.distinguishedName != "")
+        "binddn ${cfg.bind.distinguishedName}" }
+      ${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
+    '';
+  };
+
+  insertLdapPassword = !config.users.ldap.daemon.enable &&
+    config.users.ldap.bind.distinguishedName != "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.ldap = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable authentication against an LDAP server.";
+      };
+
+      server = mkOption {
+        example = "ldap://ldap.example.org/";
+        description = "The URL of the LDAP server.";
+      };
+
+      base = mkOption {
+        example = "dc=example,dc=org";
+        description = "The distinguished name of the search base.";
+      };
+
+      useTLS = mkOption {
+        default = false;
+        description = ''
+          If enabled, use TLS (encryption) over an LDAP (port 389)
+          connection.  The alternative is to specify an LDAPS server (port
+          636) in <option>users.ldap.server</option> or to forego
+          security.
+        '';
+      };
+
+      timeLimit = mkOption {
+        default = 0;
+        type = types.int;
+        description = ''
+          Specifies the time limit (in seconds) to use when performing
+          searches. A value of zero (0), which is the default, is to
+          wait indefinitely for searches to be completed.
+        '';
+      };
+
+      daemon = {
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to let the nslcd daemon (nss-pam-ldapd) handle the
+            LDAP lookups for NSS and PAM. This can improve performance,
+            and if you need to bind to the LDAP server with a password,
+            it increases security, since only the nslcd user needs to
+            have access to the bindpw file, not everyone that uses NSS
+            and/or PAM. If this option is enabled, a local nscd user is
+            created automatically, and the nslcd service is started
+            automatically when the network get up.
+          '';
+        };
+
+        extraConfig = mkOption {
+          default =  "";
+          type = types.string;
+          description = ''
+            Extra configuration options that will be added verbatim at
+            the end of the nslcd configuration file (nslcd.conf).
+          '' ;
+        } ;
+      };
+
+      bind = {
+        distinguishedName = mkOption {
+          default = "";
+          example = "cn=admin,dc=example,dc=com";
+          type = types.string;
+          description = ''
+            The distinguished name to bind to the LDAP server with. If this
+            is not specified, an anonymous bind will be done.
+          '';
+        };
+
+        password = mkOption {
+          default = "/etc/ldap/bind.password";
+          type = types.string;
+          description = ''
+            The path to a file containing the credentials to use when binding
+            to the LDAP server (if not binding anonymously).
+          '';
+        };
+
+        timeLimit = mkOption {
+          default = 30;
+          type = types.int;
+          description = ''
+            Specifies the time limit (in seconds) to use when connecting
+            to the directory server. This is distinct from the time limit
+            specified in <literal>users.ldap.timeLimit</literal> and affects
+            the initial server connection only.
+          '';
+        };
+
+        policy = mkOption {
+          default = "hard_open";
+          type = types.string;
+          description = ''
+            Specifies the policy to use for reconnecting to an unavailable
+            LDAP server. The default is <literal>hard_open</literal>, which
+            reconnects if opening the connection to the directory server
+            failed. By contrast, <literal>hard_init</literal> reconnects if
+            initializing the connection failed. Initializing may not
+            actually contact the directory server, and it is possible that
+            a malformed configuration file will trigger reconnection. If
+            <literal>soft</literal> is specified, then
+            <literal>nss_ldap</literal> will return immediately on server
+            failure. All hard reconnect policies block with exponential
+            backoff before retrying.
+          '';
+        };
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Extra configuration options that will be added verbatim at
+          the end of the ldap configuration file (ldap.conf).
+          If <literal>users.ldap.daemon</literal> is enabled, this
+          configuration will not be used. In that case, use
+          <literal>users.ldap.daemon.extraConfig</literal> instead.
+        '' ;
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc = if cfg.daemon.enable then [nslcdConfig] else [ldapConfig];
+
+    system.activationScripts = mkIf insertLdapPassword {
+      ldap = stringAfter [ "etc" "groups" "users" ] ''
+        if test -f "${cfg.bind.password}" ; then
+          echo "bindpw "$(cat ${cfg.bind.password})"" | cat ${ldapConfig} - > /etc/ldap.conf.bindpw
+          mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf
+          chmod 600 /etc/ldap.conf
+        fi
+      '';
+    };
+
+    system.nssModules = singleton (
+      if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
+    );
+
+    users = mkIf cfg.daemon.enable {
+      extraGroups.nslcd = {
+        gid = config.ids.gids.nslcd;
+      };
+
+      extraUsers.nslcd = {
+        uid = config.ids.uids.nslcd;
+        description = "nslcd user.";
+        group = "nslcd";
+      };
+    };
+
+    systemd.services = mkIf cfg.daemon.enable {
+
+      nslcd = {
+        wantedBy = [ "nss-user-lookup.target" ];
+        before = [ "nss-user-lookup.target" ];
+        after = [ "network.target" ];
+
+        preStart = ''
+          mkdir -p /run/nslcd
+          rm -f /run/nslcd/nslcd.pid;
+          chown nslcd.nslcd /run/nslcd
+          ${optionalString (cfg.bind.distinguishedName != "") ''
+            if test -s "${cfg.bind.password}" ; then
+              ln -sfT "${cfg.bind.password}" /run/nslcd/bindpw
+            fi
+          ''}
+        '';
+
+        serviceConfig = {
+          ExecStart = "${nss_pam_ldapd}/sbin/nslcd";
+          Type = "forking";
+          PIDFile = "/run/nslcd/nslcd.pid";
+          Restart = "always";
+        };
+      };
+
+    };
+
+  };
+}
diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix
new file mode 100644
index 00000000000..f1bdfd01b24
--- /dev/null
+++ b/nixos/modules/config/networking.nix
@@ -0,0 +1,88 @@
+# /etc files related to networking, such as /etc/services.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking;
+
+in
+
+{
+
+  options = {
+
+    networking.extraHosts = pkgs.lib.mkOption {
+      default = "";
+      example = "192.168.0.1 lanlocalhost";
+      description = ''
+        Additional entries to be appended to <filename>/etc/hosts</filename>.
+      '';
+    };
+
+    networking.dnsSingleRequest = pkgs.lib.mkOption {
+      default = false;
+      description = ''
+        Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
+        address queries at the same time, from the same port. Sometimes upstream
+        routers will systemically drop the ipv4 queries. The symptom of this problem is
+        that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
+        workaround for this is to specify the option 'single-request' in
+        /etc/resolv.conf. This option enables that.
+      '';
+    };
+
+  };
+
+  config = {
+
+    environment.etc =
+      { # /etc/services: TCP/UDP port assignments.
+        "services".source = pkgs.iana_etc + "/etc/services";
+
+        # /etc/protocols: IP protocol numbers.
+        "protocols".source  = pkgs.iana_etc + "/etc/protocols";
+
+        # /etc/rpc: RPC program numbers.
+        "rpc".source = pkgs.glibc + "/etc/rpc";
+
+        # /etc/hosts: Hostname-to-IP mappings.
+        "hosts".text =
+          ''
+            127.0.0.1 localhost
+            ${optionalString cfg.enableIPv6 ''
+              ::1 localhost
+            ''}
+            ${cfg.extraHosts}
+          '';
+
+        # /etc/resolvconf.conf: Configuration for openresolv.
+        "resolvconf.conf".text =
+            ''
+              # This is the default, but we must set it here to prevent
+              # a collision with an apparently unrelated environment
+              # variable with the same name exported by dhcpcd.
+              interface_order='lo lo[0-9]*'
+            '' + optionalString config.services.nscd.enable ''
+              # Invalidate the nscd cache whenever resolv.conf is
+              # regenerated.
+              libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service'
+            '' + optionalString cfg.dnsSingleRequest ''
+              # only send one DNS request at a time
+              resolv_conf_options='single-request'
+            '' + optionalString config.services.bind.enable ''
+              # This hosts runs a full-blown DNS resolver.
+              name_servers='127.0.0.1'
+            '';
+      };
+
+    # The ‘ip-up’ target is started when we have IP connectivity.  So
+    # services that depend on IP connectivity (like ntpd) should be
+    # pulled in by this target.
+    systemd.targets.ip-up.description = "Services Requiring IP Connectivity";
+
+  };
+
+}
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
new file mode 100644
index 00000000000..77890b49c67
--- /dev/null
+++ b/nixos/modules/config/no-x-libs.nix
@@ -0,0 +1,23 @@
+{ config, pkgs, ... }:
+
+{
+  options = {
+    environment.noXlibs = pkgs.lib.mkOption {
+      default = false;
+      example = true;
+      description = ''
+        Switch off the options in the default configuration that require X libraries.
+        Currently this includes: ssh X11 forwarding, dbus, fonts.enableCoreFonts,
+        fonts.enableFontConfig
+      '';
+    };
+  };
+
+  config = pkgs.lib.mkIf config.environment.noXlibs {
+    programs.ssh.setXAuthLocation = false;
+    fonts = {
+      enableCoreFonts = false;
+      enableFontConfig = false;
+    };
+  };
+}
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
new file mode 100644
index 00000000000..ad62b5597be
--- /dev/null
+++ b/nixos/modules/config/nsswitch.nix
@@ -0,0 +1,63 @@
+# Configuration for the Name Service Switch (/etc/nsswitch.conf).
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (config.services.avahi) nssmdns;
+  inherit (config.services.samba) nsswins;
+
+in
+
+{
+  options = {
+
+    # NSS modules.  Hacky!
+    system.nssModules = mkOption {
+      internal = true;
+      default = [];
+      description = ''
+        Search path for NSS (Name Service Switch) modules.  This allows
+        several DNS resolution methods to be specified via
+        <filename>/etc/nsswitch.conf</filename>.
+      '';
+      merge = mergeListOption;
+      apply = list:
+        {
+          inherit list;
+          path = makeLibraryPath list;
+        };
+    };
+
+  };
+
+  config = {
+
+    environment.etc =
+      [ # Name Service Switch configuration file.  Required by the C library.
+        # !!! Factor out the mdns stuff.  The avahi module should define
+        # an option used by this module.
+        { source = pkgs.writeText "nsswitch.conf"
+            ''
+              passwd:    files ldap
+              group:     files ldap
+              shadow:    files ldap
+              hosts:     files ${optionalString nssmdns "mdns_minimal [NOTFOUND=return]"} dns ${optionalString nssmdns "mdns"} ${optionalString nsswins "wins"} myhostname
+              networks:  files dns
+              ethers:    files
+              services:  files
+              protocols: files
+            '';
+          target = "nsswitch.conf";
+        }
+      ];
+
+    # Use nss-myhostname to ensure that our hostname always resolves to
+    # a valid IP address.  It returns all locally configured IP
+    # addresses, or ::1 and 127.0.0.2 as fallbacks.
+    system.nssModules = [ pkgs.systemd ];
+
+  };
+}
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix
new file mode 100644
index 00000000000..fec2c886818
--- /dev/null
+++ b/nixos/modules/config/power-management.nix
@@ -0,0 +1,108 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.powerManagement;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    powerManagement = {
+
+      enable = mkOption {
+        default = true;
+        description =
+          ''
+            Whether to enable power management.  This includes support
+            for suspend-to-RAM and powersave features on laptops.
+          '';
+      };
+
+      resumeCommands = mkOption {
+        default = "";
+        description = "Commands executed after the system resumes from suspend-to-RAM.";
+      };
+
+      powerUpCommands = mkOption {
+        default = "";
+        example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
+        description =
+          ''
+            Commands executed when the machine powers up.  That is,
+            they're executed both when the system first boots and when
+            it resumes from suspend or hibernation.
+          '';
+      };
+
+      powerDownCommands = mkOption {
+        default = "";
+        example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
+        description =
+          ''
+            Commands executed when the machine powers down.  That is,
+            they're executed both when the system shuts down and when
+            it goes to suspend or hibernation.
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  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"
+      ];
+
+    powerManagement.cpuFreqGovernor = mkDefault "ondemand";
+    powerManagement.scsiLinkPolicy = mkDefault "min_power";
+
+    systemd.targets.post-resume = {
+      description = "Post-Resume Actions";
+      requires = [ "post-resume.service" ];
+      after = [ "post-resume.service" ];
+      wantedBy = [ "sleep.target" ];
+      unitConfig.StopWhenUnneeded = true;
+    };
+
+    # Service executed before suspending/hibernating.
+    systemd.services."pre-sleep" =
+      { description = "Pre-Sleep Actions";
+        wantedBy = [ "sleep.target" ];
+        before = [ "sleep.target" ];
+        script =
+          ''
+            ${cfg.powerDownCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+    systemd.services."post-resume" =
+      { description = "Post-Resume Actions";
+        after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ];
+        script =
+          ''
+            ${cfg.resumeCommands}
+            ${cfg.powerUpCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
new file mode 100644
index 00000000000..26060f5b2d1
--- /dev/null
+++ b/nixos/modules/config/pulseaudio.nix
@@ -0,0 +1,148 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+
+  cfg = config.hardware.pulseaudio;
+
+  uid = config.ids.uids.pulseaudio;
+  gid = config.ids.gids.pulseaudio;
+
+  pulseRuntimePath = "/var/run/pulse";
+
+  # Create pulse/client.conf even if PulseAudio is disabled so
+  # that we can disable the autospawn feature in programs that
+  # are built with PulseAudio support (like KDE).
+  clientConf = writeText "client.conf" ''
+    autospawn=${if (cfg.enable && !cfg.systemWide) then "yes" else "no"}
+    ${optionalString (cfg.enable && !cfg.systemWide)
+      "daemon-binary=${cfg.package}/bin/pulseaudio"}
+  '';
+
+  # Write an /etc/asound.conf that causes all ALSA applications to
+  # be re-routed to the PulseAudio server through ALSA's Pulse
+  # plugin.
+  alsaConf = writeText "asound.conf" ''
+    pcm_type.pulse {
+      lib ${alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so
+    }
+    pcm.!default {
+      type pulse
+      hint.description "Default Audio Device (via PulseAudio)"
+    }
+    ctl_type.pulse {
+      lib ${alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so
+    }
+    ctl.!default {
+      type pulse
+    }
+  '';
+
+in {
+
+  options = {
+
+    hardware.pulseaudio = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the PulseAudio sound server.
+        '';
+      };
+
+      systemWide = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If false, a PulseAudio server is launched automatically for
+          each user that tries to use the sound system. The server runs
+          with user privileges. This is the recommended and most secure
+          way to use PulseAudio. If true, one system-wide PulseAudio
+          server is launched on boot, running as the user "pulse".
+          Please read the PulseAudio documentation for more details.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.uniq types.path;
+        default = "${pulseaudio}/etc/pulse/default.pa";
+        description = ''
+          The path to the configuration the PulseAudio server
+          should use. By default, the "default.pa" configuration
+          from the PulseAudio distribution is used.
+        ''; 
+      };
+  
+      package = mkOption {
+        default = pulseaudio;
+        example = "pulseaudio.override { jackaudioSupport = true; }";
+        description = ''
+          The PulseAudio derivation to use.  This can be used to enable
+          features (such as JACK support) that are not enabled in the
+          default PulseAudio in Nixpkgs.
+        '';
+      };
+    };
+
+  };
+
+
+  config = mkMerge [
+    {
+      environment.etc = singleton {
+        target = "pulse/client.conf";
+        source = clientConf;
+      };
+    }
+
+    (mkIf cfg.enable {
+      environment.systemPackages = [ cfg.package ];
+
+      environment.etc = singleton {
+        target = "asound.conf";
+        source = alsaConf;
+      };
+
+      # Allow PulseAudio to get realtime priority using rtkit.
+      security.rtkit.enable = true;
+    })
+
+    (mkIf (cfg.enable && !cfg.systemWide) {
+      environment.etc = singleton {
+        target = "pulse/default.pa";
+        source = cfg.configFile;
+      };
+    })
+
+    (mkIf (cfg.enable && cfg.systemWide) {
+      users.extraUsers.pulse = {
+        # For some reason, PulseAudio wants UID == GID.
+        uid = assert uid == gid; uid;
+        group = "pulse";
+        extraGroups = [ "audio" ];
+        description = "PulseAudio system service user";
+        home = pulseRuntimePath;
+      };
+  
+      users.extraGroups.pulse.gid = gid;
+  
+      systemd.services.pulseaudio = {
+        description = "PulseAudio system-wide server";
+        wantedBy = [ "sound.target" ];
+        before = [ "sound.target" ];
+        path = [ cfg.package ];
+        environment.PULSE_RUNTIME_PATH = pulseRuntimePath;
+        preStart = ''
+          mkdir -p --mode 755 ${pulseRuntimePath}
+          chown -R pulse:pulse ${pulseRuntimePath}
+        '';
+        script = ''
+          exec pulseaudio --system -n --file="${cfg.configFile}"
+        '';
+      };
+    })
+  ];
+
+}
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
new file mode 100644
index 00000000000..4f7447f435b
--- /dev/null
+++ b/nixos/modules/config/shells-environment.nix
@@ -0,0 +1,179 @@
+# This module defines a global environment configuration and
+# a common configuration for all shells.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  options = {
+
+    environment.variables = mkOption {
+      default = {};
+      description = ''
+        A set of environment variables used in the global environment.
+        The value of each variable can be either a string or a list of
+        strings.  The latter is concatenated, interspersed with colon
+        characters.
+      '';
+      type = types.attrsOf (mkOptionType {
+        name = "a string or a list of strings";
+        merge = xs:
+          let xs' = evalProperties xs; in
+          if isList (head xs') then concatLists xs'
+          else if builtins.lessThan 1 (length xs') then abort "variable in ‘environment.variables’ has multiple values"
+          else if !builtins.isString (head xs') then abort "variable in ‘environment.variables’ does not have a string value"
+          else head xs';
+      });
+      apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
+    };
+
+    environment.profiles = mkOption {
+      default = [];
+      description = ''
+        A list of profiles used to setup the global environment.
+      '';
+      type = types.listOf types.string;
+    };
+
+    environment.profileVariables = mkOption {
+      default = (p: {});
+      description = ''
+        A function which given a profile path should give back
+        a set of environment variables for that profile.
+      '';
+      # !!! this should be of the following type:
+      #type = types.functionTo (types.attrsOf (types.optionSet envVar));
+      # and envVar should be changed to something more like environOpts.
+      # Having unique `value' _or_ multiple `list' is much more useful
+      # than just sticking everything together with ':' unconditionally.
+      # Anyway, to have this type mentioned above
+      # types.optionSet needs to be transformed into a type constructor
+      # (it has a !!! mark on that in nixpkgs)
+      # for now we hack all this to be
+      type = types.functionTo (types.attrsOf (types.listOf types.string));
+    };
+
+    # !!! isn't there a better way?
+    environment.extraInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during global environment initialisation
+        after all variables and profileVariables have been set.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.loginShellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during login shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.interactiveShellInit = mkOption {
+      default = "";
+      description = ''
+        Shell script code called during interactive shell initialisation.
+        This code is asumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellAliases = mkOption {
+      default = {};
+      example = { ll = "ls -l"; };
+      description = ''
+        An attribute set that maps aliases (the top level attribute names in
+        this option) to command strings or directly to build outputs. The
+        aliases are added to all users' shells.
+      '';
+      type = types.attrs; # types.attrsOf types.stringOrPath;
+    };
+
+    environment.binsh = mkOption {
+      default = "${config.system.build.binsh}/bin/sh";
+      example = "\${pkgs.dash}/bin/dash";
+      type = types.path;
+      description = ''
+        The shell executable that is linked system-wide to
+        <literal>/bin/sh</literal>. Please note that NixOS assumes all
+        over the place that shell to be Bash, so override the default
+        setting only if you know exactly what you're doing.
+      '';
+    };
+
+    environment.shells = mkOption {
+      default = [];
+      example = [ "/run/current-system/sw/bin/zsh" ];
+      description = ''
+        A list of permissible login shells for user accounts.
+        No need to mention <literal>/bin/sh</literal>
+        here, it is placed into this list implicitly.
+      '';
+      type = types.listOf types.path;
+    };
+
+  };
+
+  config = {
+
+    system.build.binsh = pkgs.bashInteractive;
+
+    environment.etc."shells".text =
+      ''
+        ${concatStringsSep "\n" cfg.shells}
+        /bin/sh
+      '';
+
+    system.build.setEnvironment = pkgs.writeText "set-environment"
+       ''
+         ${concatStringsSep "\n" (
+           (mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'')
+             # This line is a kind of a hack because of !!! note above
+             (zipAttrsWith (const concatLists) ([ (mapAttrs (n: v: [ v ]) cfg.variables) ] ++ map cfg.profileVariables cfg.profiles))))}
+
+         ${cfg.extraInit}
+
+         # The setuid wrappers override other bin directories.
+         export PATH="${config.security.wrapperDir}:$PATH"
+
+         # ~/bin if it exists overrides other bin directories.
+         export PATH="$HOME/bin:$PATH"
+       '';
+
+    system.activationScripts.binsh = stringAfter [ "stdio" ]
+      ''
+        # Create the required /bin/sh symlink; otherwise lots of things
+        # (notably the system() function) won't work.
+        mkdir -m 0755 -p /bin
+        ln -sfn "${cfg.binsh}" /bin/.sh.tmp
+        mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
new file mode 100644
index 00000000000..7d4654ae287
--- /dev/null
+++ b/nixos/modules/config/swap.nix
@@ -0,0 +1,124 @@
+{ config, pkgs, utils, ... }:
+
+with pkgs.lib;
+with utils;
+
+{
+
+  ###### interface
+
+  options = {
+
+    swapDevices = mkOption {
+      default = [];
+      example = [
+        { device = "/dev/hda7"; }
+        { device = "/var/swapfile"; }
+        { label = "bigswap"; }
+      ];
+      description = ''
+        The swap devices and swap files.  These must have been
+        initialised using <command>mkswap</command>.  Each element
+        should be an attribute set specifying either the path of the
+        swap device or file (<literal>device</literal>) or the label
+        of the swap device (<literal>label</literal>, see
+        <command>mkswap -L</command>).  Using a label is
+        recommended.
+      '';
+
+      type = types.listOf types.optionSet;
+
+      options = {config, options, ...}: {
+
+        options = {
+
+          device = mkOption {
+            example = "/dev/sda3";
+            type = types.uniq types.string;
+            description = "Path of the device.";
+          };
+
+          label = mkOption {
+            example = "swap";
+            type = types.uniq types.string;
+            description = ''
+              Label of the device.  Can be used instead of <varname>device</varname>.
+            '';
+          };
+
+          size = mkOption {
+            default = null;
+            example = 2048;
+            type = types.nullOr types.int;
+            description = ''
+              If this option is set, ‘device’ is interpreted as the
+              path of a swapfile that will be created automatically
+              with the indicated size (in megabytes) if it doesn't
+              exist.
+            '';
+          };
+
+          priority = mkOption {
+            default = null;
+            example = 2048;
+            type = types.nullOr types.int;
+            description = ''
+              Specify the priority of the swap device. Priority is a value between 0 and 32767.
+              Higher numbers indicate higher priority.
+              null lets the kernel choose a priority, which will show up as a negative value.
+            '';
+          };
+
+        };
+
+        config = {
+          device =
+            if options.label.isDefined then
+              "/dev/disk/by-label/${config.label}"
+            else
+              mkNotdef;
+        };
+
+      };
+
+    };
+
+  };
+
+  config = mkIf ((length config.swapDevices) != 0) {
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "SWAP")
+    ];
+
+    # Create missing swapfiles.
+    # FIXME: support changing the size of existing swapfiles.
+    systemd.services =
+      let
+
+        createSwapDevice = sw:
+          assert sw.device != "";
+          let device' = escapeSystemdPath sw.device; in
+          nameValuePair "mkswap-${escapeSystemdPath sw.device}"
+          { description = "Initialisation of Swapfile ${sw.device}";
+            wantedBy = [ "${device'}.swap" ];
+            before = [ "${device'}.swap" ];
+            path = [ pkgs.utillinux ];
+            script =
+              ''
+                if [ ! -e "${sw.device}" ]; then
+                  fallocate -l ${toString sw.size}M "${sw.device}" ||
+                    dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
+                  mkswap ${sw.device}
+                fi
+              '';
+            unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
+            unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+            serviceConfig.Type = "oneshot";
+          };
+
+      in listToAttrs (map createSwapDevice (filter (sw: sw.size != null) config.swapDevices));
+
+  };
+
+}
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
new file mode 100644
index 00000000000..6b52fd38fde
--- /dev/null
+++ b/nixos/modules/config/sysctl.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  sysctlOption = mkOptionType {
+    name = "sysctl option value";
+    check = x: builtins.isBool x || builtins.isString x || builtins.isInt x;
+    merge = xs: last xs; # FIXME: hacky way to allow overriding in configuration.nix.
+  };
+
+in
+
+{
+
+  options = {
+
+    boot.kernel.sysctl = mkOption {
+      default = {};
+      example = {
+        "net.ipv4.tcp_syncookies" = false;
+        "vm.swappiness" = 60;
+      };
+      type = types.attrsOf sysctlOption;
+      description = ''
+        Runtime parameters of the Linux kernel, as set by
+        <citerefentry><refentrytitle>sysctl</refentrytitle>
+        <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.
+      '';
+    };
+
+  };
+
+  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);
+
+    systemd.services.systemd-sysctl =
+      { description = "Apply Kernel Variables";
+        before = [ "sysinit.target" "shutdown.target" ];
+        wantedBy = [ "sysinit.target" "multi-user.target" ];
+        restartTriggers = [ config.environment.etc."sysctl.d/nixos.conf".source ];
+        unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${config.systemd.package}/lib/systemd/systemd-sysctl";
+        };
+      };
+
+    # Enable hardlink and symlink restrictions.  See
+    # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=800179c9b8a1e796e441674776d11cd4c05d61d7
+    # for details.
+    boot.kernel.sysctl."fs.protected_hardlinks" = true;
+    boot.kernel.sysctl."fs.protected_symlinks" = true;
+
+    # 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;
+
+  };
+
+}
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
new file mode 100644
index 00000000000..e65be03afac
--- /dev/null
+++ b/nixos/modules/config/system-path.nix
@@ -0,0 +1,142 @@
+# This module defines the packages that appear in
+# /run/current-system/sw.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+  extraManpages = pkgs.runCommand "extra-manpages" { buildInputs = [ pkgs.help2man ]; }
+    ''
+      mkdir -p $out/share/man/man1
+      help2man ${pkgs.gnutar}/bin/tar > $out/share/man/man1/tar.1
+    '';
+
+  requiredPackages =
+    [ config.environment.nix
+      pkgs.acl
+      pkgs.attr
+      pkgs.bashInteractive # bash with ncurses support
+      pkgs.bzip2
+      pkgs.coreutils
+      pkgs.cpio
+      pkgs.curl
+      pkgs.diffutils
+      pkgs.eject # HAL depends on it anyway
+      pkgs.findutils
+      pkgs.gawk
+      pkgs.glibc # for ldd, getent
+      pkgs.gnugrep
+      pkgs.gnupatch
+      pkgs.gnused
+      pkgs.gnutar
+      pkgs.gzip
+      pkgs.xz
+      pkgs.less
+      pkgs.libcap
+      pkgs.man
+      pkgs.nano
+      pkgs.ncurses
+      pkgs.netcat
+      pkgs.openssh
+      pkgs.pciutils
+      pkgs.perl
+      pkgs.procps
+      pkgs.rsync
+      pkgs.strace
+      pkgs.sysvtools
+      pkgs.time
+      pkgs.usbutils
+      pkgs.utillinux
+      extraManpages
+    ];
+
+in
+
+{
+  options = {
+
+    environment = {
+
+      systemPackages = mkOption {
+        default = [];
+        example = "[ pkgs.icecat3 pkgs.thunderbird ]";
+        description = ''
+          The set of packages that appear in
+          /run/current-system/sw.  These packages are
+          automatically available to all users, and are
+          automatically updated every time you rebuild the system
+          configuration.  (The latter is the main difference with
+          installing them in the default profile,
+          <filename>/nix/var/nix/profiles/default</filename>.
+        '';
+      };
+
+      pathsToLink = mkOption {
+        # Note: We need `/lib' to be among `pathsToLink' for NSS modules
+        # to work.
+        default = [];
+        example = ["/"];
+        description = "List of directories to be symlinked in `/run/current-system/sw'.";
+      };
+    };
+
+    system = {
+
+      path = mkOption {
+        default = cfg.systemPackages;
+        description = ''
+          The packages you want in the boot environment.
+        '';
+
+        apply = list: pkgs.buildEnv {
+          name = "system-path";
+          paths = list;
+          inherit (cfg) pathsToLink;
+          ignoreCollisions = true;
+          # !!! Hacky, should modularise.
+          postBuild =
+            ''
+              if [ -x $out/bin/update-mime-database -a -w $out/share/mime/packages ]; then
+                  $out/bin/update-mime-database -V $out/share/mime
+              fi
+
+              if [ -x $out/bin/gtk-update-icon-cache -a -f $out/share/icons/hicolor/index.theme ]; then
+                  $out/bin/gtk-update-icon-cache $out/share/icons/hicolor
+              fi
+
+              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
+            '';
+        };
+
+      };
+
+    };
+
+  };
+
+  config = {
+
+    environment.systemPackages = requiredPackages;
+
+    environment.pathsToLink =
+      [ "/bin"
+        "/etc/xdg"
+        "/info"
+        "/lib"
+        "/man"
+        "/sbin"
+        "/share/emacs"
+        "/share/org"
+        "/share/info"
+        "/share/terminfo"
+        "/share/man"
+      ];
+
+  };
+}
diff --git a/nixos/modules/config/timezone.nix b/nixos/modules/config/timezone.nix
new file mode 100644
index 00000000000..e185584846a
--- /dev/null
+++ b/nixos/modules/config/timezone.nix
@@ -0,0 +1,36 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+
+    time = {
+
+      timeZone = mkOption {
+        default = "CET";
+        type = with types; uniq string;
+        example = "America/New_York";
+        description = "The time zone used when displaying times and dates.";
+      };
+
+      hardwareClockInLocalTime = mkOption {
+        default = false;
+        description = "If set, keep the hardware clock in local time instead of UTC.";
+      };
+
+    };
+  };
+
+  config = {
+
+    environment.variables.TZDIR = "/etc/zoneinfo";
+    environment.variables.TZ = config.time.timeZone;
+
+    environment.etc.localtime.source = "${pkgs.tzdata}/share/zoneinfo/${config.time.timeZone}";
+
+    environment.etc.zoneinfo.source = "${pkgs.tzdata}/share/zoneinfo";
+
+  };
+
+}
diff --git a/nixos/modules/config/unix-odbc-drivers.nix b/nixos/modules/config/unix-odbc-drivers.nix
new file mode 100644
index 00000000000..0f608469058
--- /dev/null
+++ b/nixos/modules/config/unix-odbc-drivers.nix
@@ -0,0 +1,34 @@
+{ config, pkgs, ... }:
+
+with pkgs.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
+
+{
+  ###### interface
+
+  options = {
+    environment.unixODBCDrivers = mkOption {
+      default = [];
+      example = literalExample "map (x : x.ini) (with pkgs.unixODBCDrivers; [ mysql psql psqlng ] )";
+      description = ''
+        Specifies Unix ODBC drivers to be registered in
+        <filename>/etc/odbcinst.ini</filename>.  You may also want to
+        add <literal>pkgs.unixODBC</literal> to the system path to get
+        a command line client to connnect to ODBC databases.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (config.environment.unixODBCDrivers != []) {
+
+    environment.etc."odbcinst.ini".text =
+      let inis = config.environment.unixODBCDrivers;
+      in pkgs.lib.concatStringsSep "\n" inis;
+
+  };
+
+}
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
new file mode 100644
index 00000000000..5f32dc350df
--- /dev/null
+++ b/nixos/modules/config/users-groups.nix
@@ -0,0 +1,315 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  ids = config.ids;
+  users = config.users;
+
+  userOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = with types; uniq string;
+        description = "The name of the user account. If undefined, the name of the attribute set will be used.";
+      };
+
+      description = mkOption {
+        type = with types; uniq string;
+        default = "";
+        description = "A short description of the user account.";
+      };
+
+      uid = mkOption {
+        type = with types; uniq (nullOr int);
+        default = null;
+        description = "The account UID. If undefined, NixOS will select a free UID.";
+      };
+
+      group = mkOption {
+        type = with types; uniq string;
+        default = "nogroup";
+        description = "The user's primary group.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        description = "The user's auxiliary groups.";
+      };
+
+      home = mkOption {
+        type = with types; uniq string;
+        default = "/var/empty";
+        description = "The user's home directory.";
+      };
+
+      shell = mkOption {
+        type = with types; uniq string;
+        default = "/run/current-system/sw/sbin/nologin";
+        description = "The path to the user's shell.";
+      };
+
+      createHome = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If true, the home directory will be created automatically.";
+      };
+
+      useDefaultShell = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
+      };
+
+      password = mkOption {
+        type = with types; uniq (nullOr string);
+        default = null;
+        description = "The user's password. If undefined, no password is set for the user.  Warning: do not set confidential information here because this data would be readable by all.  This option should only be used for public account such as guest.";
+      };
+
+      isSystemUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Indicates if the user is a system user or not.";
+      };
+
+      createUser = mkOption {
+        type = types.bool;
+        default = true;
+        description = "
+          Indicates if the user should be created automatically as a local user.
+          Set this to false if the user for instance is an LDAP user. NixOS will
+          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);
+    };
+
+  };
+
+  groupOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = with types; uniq string;
+        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);
+        default = null;
+        description = "The GID of the group. If undefined, NixOS will select a free GID.";
+      };
+
+    };
+
+    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" (
+    let
+      p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
+    in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.extraUsers = mkOption {
+      default = {};
+      type = types.loaOf types.optionSet;
+      example = {
+        alice = {
+          uid = 1234;
+          description = "Alice";
+          home = "/home/alice";
+          createHome = true;
+          group = "users";
+          extraGroups = ["wheel"];
+          shell = "/bin/sh";
+          password = "foobar";
+        };
+      };
+      description = ''
+        Additional user accounts to be created automatically by the system.
+        This can also be used to set options for root.
+      '';
+      options = [ userOpts ];
+    };
+
+    users.extraGroups = mkOption {
+      default = {};
+      example =
+        { students.gid = 1001;
+          hackers = { };
+        };
+      type = types.loaOf types.optionSet;
+      description = ''
+        Additional groups to be created automatically by the system.
+      '';
+      options = [ groupOpts ];
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    users.extraUsers = {
+      root = {
+        description = "System administrator";
+        home = "/root";
+        shell = config.users.defaultUserShell;
+        group = "root";
+      };
+      nobody = {
+        description = "Unprivileged account (don't use!)";
+      };
+    };
+
+    users.extraGroups = {
+      root = { };
+      wheel = { };
+      disk = { };
+      kmem = { };
+      tty = { };
+      floppy = { };
+      uucp = { };
+      lp = { };
+      cdrom = { };
+      tape = { };
+      audio = { };
+      video = { };
+      dialout = { };
+      nogroup = { };
+      users = { };
+      nixbld = { };
+      utmp = { };
+      adm = { }; # expected by journald
+    };
+
+    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::::::::" >> /etc/shadow
+        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.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}'
+        '')}
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
new file mode 100644
index 00000000000..16b6a862593
--- /dev/null
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -0,0 +1,26 @@
+{pkgs, config, ...}:
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.enableAllFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want to enable all the firmware shipped with Debian/Ubuntu.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.hardware.enableAllFirmware {
+    hardware.firmware = [ "${pkgs.firmwareLinuxNonfree}/lib/firmware" ];
+  };
+
+}
diff --git a/nixos/modules/hardware/cpu/amd-microcode.nix b/nixos/modules/hardware/cpu/amd-microcode.nix
new file mode 100644
index 00000000000..5720a63834f
--- /dev/null
+++ b/nixos/modules/hardware/cpu/amd-microcode.nix
@@ -0,0 +1,29 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.cpu.amd.updateMicrocode = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Update the CPU microcode for AMD processors.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.hardware.cpu.amd.updateMicrocode {
+    hardware.firmware = [ "${pkgs.amdUcode}/lib/firmware" ];
+    boot.kernelModules = [ "microcode" ];
+  };
+
+}
diff --git a/nixos/modules/hardware/cpu/intel-microcode.nix b/nixos/modules/hardware/cpu/intel-microcode.nix
new file mode 100644
index 00000000000..9046ddf83bb
--- /dev/null
+++ b/nixos/modules/hardware/cpu/intel-microcode.nix
@@ -0,0 +1,29 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.cpu.intel.updateMicrocode = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Update the CPU microcode for Intel processors.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.hardware.cpu.intel.updateMicrocode {
+    hardware.firmware = [ "${pkgs.microcodeIntel}/lib/firmware" ];
+    boot.kernelModules = [ "microcode" ];
+  };
+
+}
diff --git a/nixos/modules/hardware/network/b43.nix b/nixos/modules/hardware/network/b43.nix
new file mode 100644
index 00000000000..8f45bd4d3f1
--- /dev/null
+++ b/nixos/modules/hardware/network/b43.nix
@@ -0,0 +1,32 @@
+{pkgs, config, ...}:
+
+let kernelVersion = config.boot.kernelPackages.kernel.version; in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableB43Firmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the NICs supported by the b43 module.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableB43Firmware {
+    assertions = [ {
+      assertion = builtins.lessThan 0 (builtins.compareVersions kernelVersion "3.2");
+      message = "b43 firmware for kernels older than 3.2 not packaged yet!";
+    } ];
+    hardware.firmware = [ pkgs.b43Firmware_5_1_138 ];
+  };
+
+}
diff --git a/nixos/modules/hardware/network/broadcom-43xx.nix b/nixos/modules/hardware/network/broadcom-43xx.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/broadcom-43xx.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-2030.nix b/nixos/modules/hardware/network/intel-2030.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-2030.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-2100bg.nix b/nixos/modules/hardware/network/intel-2100bg.nix
new file mode 100644
index 00000000000..1e0033eb414
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-2100bg.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, ... }:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableIntel2100BGFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the Intel
+        PRO/Wireless 2100BG to be loaded automatically.  This is
+        required if you want to use this device.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableIntel2100BGFirmware {
+
+    hardware.enableAllFirmware = true;
+
+  };
+
+}
diff --git a/nixos/modules/hardware/network/intel-2200bg.nix b/nixos/modules/hardware/network/intel-2200bg.nix
new file mode 100644
index 00000000000..ae5b69b7981
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-2200bg.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, ... }:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableIntel2200BGFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the Intel
+        PRO/Wireless 2200BG to be loaded automatically.  This is
+        required if you want to use this device.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableIntel2200BGFirmware {
+
+    hardware.enableAllFirmware = true;
+
+  };
+
+}
diff --git a/nixos/modules/hardware/network/intel-3945abg.nix b/nixos/modules/hardware/network/intel-3945abg.nix
new file mode 100644
index 00000000000..80baf260ab9
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-3945abg.nix
@@ -0,0 +1,29 @@
+{ config, pkgs, ... }:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableIntel3945ABGFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        This option enables automatic loading of the firmware for the Intel
+        PRO/Wireless 3945ABG.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableIntel3945ABGFirmware {
+
+    hardware.enableAllFirmware = true;
+
+  };
+
+}
diff --git a/nixos/modules/hardware/network/intel-4965agn.nix b/nixos/modules/hardware/network/intel-4965agn.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-4965agn.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-5000.nix b/nixos/modules/hardware/network/intel-5000.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-5000.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-5150.nix b/nixos/modules/hardware/network/intel-5150.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-5150.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-6000.nix b/nixos/modules/hardware/network/intel-6000.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-6000.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-6000g2a.nix b/nixos/modules/hardware/network/intel-6000g2a.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-6000g2a.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-6000g2b.nix b/nixos/modules/hardware/network/intel-6000g2b.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-6000g2b.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/ralink.nix b/nixos/modules/hardware/network/ralink.nix
new file mode 100644
index 00000000000..92f34d8643b
--- /dev/null
+++ b/nixos/modules/hardware/network/ralink.nix
@@ -0,0 +1,26 @@
+{pkgs, config, ...}:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableRalinkFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the RT73 NIC.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableRalinkFirmware {
+    hardware.enableAllFirmware = true;
+  };
+
+}
diff --git a/nixos/modules/hardware/network/rtl8192c.nix b/nixos/modules/hardware/network/rtl8192c.nix
new file mode 100644
index 00000000000..3aefb7bdd60
--- /dev/null
+++ b/nixos/modules/hardware/network/rtl8192c.nix
@@ -0,0 +1,26 @@
+{pkgs, config, ...}:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableRTL8192cFirmware = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the RTL8192c (and related) NICs.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.networking.enableRTL8192cFirmware {
+    hardware.enableAllFirmware = true;
+  };
+
+}
diff --git a/nixos/modules/hardware/network/smc-2632w/default.nix b/nixos/modules/hardware/network/smc-2632w/default.nix
new file mode 100644
index 00000000000..318131be749
--- /dev/null
+++ b/nixos/modules/hardware/network/smc-2632w/default.nix
@@ -0,0 +1,9 @@
+{pkgs, config, ...}:
+
+{
+  hardware = {
+    pcmcia = {
+      firmware = [ (pkgs.lib.cleanSource ./firmware) ];
+    };
+  };
+}
diff --git a/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis b/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis
new file mode 100644
index 00000000000..5f13088c373
--- /dev/null
+++ b/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis
@@ -0,0 +1,8 @@
+  vers_1 5.0, "SMC", "SMC2632W", "Version 01.02", ""
+  manfid 0x0156, 0x0002
+  funcid network_adapter
+  cftable_entry 0x01 [default]
+    Vcc Vmin 3000mV Vmax 3300mV Iavg 300mA Ipeak 300mA
+    Idown 10mA
+    io 0x0000-0x003f [lines=6] [16bit]
+    irq mask 0xffff [level] [pulse]
diff --git a/nixos/modules/hardware/network/zydas-zd1211.nix b/nixos/modules/hardware/network/zydas-zd1211.nix
new file mode 100644
index 00000000000..c8428a7241b
--- /dev/null
+++ b/nixos/modules/hardware/network/zydas-zd1211.nix
@@ -0,0 +1,5 @@
+{pkgs, config, ...}:
+
+{
+  hardware.firmware = [ pkgs.zd1211fw ];
+}
diff --git a/nixos/modules/hardware/pcmcia.nix b/nixos/modules/hardware/pcmcia.nix
new file mode 100644
index 00000000000..0dba59734ca
--- /dev/null
+++ b/nixos/modules/hardware/pcmcia.nix
@@ -0,0 +1,59 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  pcmciaUtils = pkgs.pcmciaUtils.passthru.function {
+    inherit (config.hardware.pcmcia) firmware config;
+  };
+
+in
+
+
+{
+  ###### interface
+
+  options = {
+
+    hardware.pcmcia = {
+      enable = mkOption {
+        default = false;
+        merge = mergeEnableOption;
+        description = ''
+          Enable this option to support PCMCIA card.
+        '';
+      };
+
+      firmware = mkOption {
+        default = [];
+        merge = mergeListOption;
+        description = ''
+          List of firmware used to handle specific PCMCIA card.
+        '';
+      };
+
+      config = mkOption {
+        default = null;
+        description = ''
+          Path to the configuration file which map the memory, irq
+          and ports used by the PCMCIA hardware.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.pcmcia.enable {
+
+    boot.kernelModules = [ "pcmcia" ];
+
+    services.udev.packages = [ pcmciaUtils ];
+
+    environment.systemPackages = [ pcmciaUtils ];
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/encoder/wis-go7007.nix b/nixos/modules/hardware/video/encoder/wis-go7007.nix
new file mode 100644
index 00000000000..c0eb2b814b3
--- /dev/null
+++ b/nixos/modules/hardware/video/encoder/wis-go7007.nix
@@ -0,0 +1,15 @@
+{pkgs, config, ...}:
+
+let
+  wis_go7007 = config.boot.kernelPackages.wis_go7007;
+in
+
+{
+  boot.extraModulePackages = [wis_go7007];
+
+  environment.systemPackages = [wis_go7007];
+
+  hardware.firmware = ["${wis_go7007}/firmware"];
+
+  services.udev.packages = [wis_go7007];
+}
diff --git a/nixos/modules/hardware/video/radeon.nix b/nixos/modules/hardware/video/radeon.nix
new file mode 100644
index 00000000000..8fecdae36bf
--- /dev/null
+++ b/nixos/modules/hardware/video/radeon.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix
new file mode 100644
index 00000000000..8126ce46dd9
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/channel.nix
@@ -0,0 +1,43 @@
+# Provide an initial copy of the NixOS channel so that the user
+# doesn't need to run "nix-channel --update" first.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  # We need a copy of the Nix expressions for Nixpkgs and NixOS on the
+  # CD.  These are installed into the "nixos" channel of the root
+  # user, as expected by nixos-rebuild/nixos-install.
+  channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}"
+    { expr = builtins.readFile ../../../lib/channel-expr.nix; }
+    ''
+      mkdir -p $out/nixos
+      cp -prd ${cleanSource ../../..} $out/nixos/nixos
+      cp -prd ${cleanSource <nixpkgs>} $out/nixos/nixpkgs
+      chmod -R u+w $out/nixos/nixos
+      echo -n ${config.system.nixosVersion} > $out/nixos/nixos/.version
+      echo -n "" > $out/nixos/nixos/.version-suffix
+      echo "$expr" > $out/nixos/default.nix
+    '';
+
+in
+
+{
+  # Provide the NixOS/Nixpkgs sources in /etc/nixos.  This is required
+  # for nixos-install.
+  boot.postBootCommands =
+    ''
+      if ! [ -e /var/lib/nixos/did-channel-init ]; then
+        echo "unpacking the NixOS/Nixpkgs sources..."
+        mkdir -p /nix/var/nix/profiles/per-user/root
+        ${config.environment.nix}/bin/nix-env -p /nix/var/nix/profiles/per-user/root/channels \
+          -i ${channelSources} --quiet --option use-substitutes false
+        mkdir -m 0700 -p /root/.nix-defexpr
+        ln -s /nix/var/nix/profiles/per-user/root/channels /root/.nix-defexpr/channels
+        mkdir -m 0755 -p /var/lib/nixos
+        touch /var/lib/nixos/did-channel-init
+      fi
+    '';
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
new file mode 100644
index 00000000000..999871ab074
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -0,0 +1,37 @@
+# This module contains the basic configuration for building a NixOS
+# installation CD.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  imports =
+    [ ./channel.nix
+      ./iso-image.nix
+
+      # Profiles of this basic installation CD.
+      ../../profiles/all-hardware.nix
+      ../../profiles/base.nix
+      ../../profiles/installation-device.nix
+    ];
+
+  # ISO naming.
+  isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixosVersion}-${pkgs.stdenv.system}.iso";
+
+  isoImage.volumeID = substring 0 32 "NIXOS_${config.system.nixosVersion}";
+
+  # Make the installer more likely to succeed in low memory
+  # environments.  The kernel's overcommit heustistics bite us
+  # fairly often, preventing processes such as nix-worker or
+  # download-using-manifests.pl from forking even if there is
+  # plenty of free memory.
+  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 ];
+
+  # Add Memtest86+ to the CD.
+  boot.loader.grub.memtest86 = true;
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-efi.nix b/nixos/modules/installer/cd-dvd/installation-cd-efi.nix
new file mode 100644
index 00000000000..4aa788feeae
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-efi.nix
@@ -0,0 +1,14 @@
+{ config, pkgs, ... }:
+
+{
+  # Move into base image once using 3.10 or later
+
+  require = [ ./installation-cd-minimal.nix ];
+
+  boot.kernelPackages = pkgs.linuxPackages_3_10;
+
+  # Get a console as soon as the initrd loads fbcon on EFI boot
+  boot.initrd.kernelModules = [ "fbcon" ];
+
+  isoImage.makeEfiBootable = true;
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix
new file mode 100644
index 00000000000..debf3e7db90
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix
@@ -0,0 +1,30 @@
+# This module defines a NixOS installation CD that contains X11 and
+# KDE 4.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  imports = [ ./installation-cd-base.nix ../../profiles/graphical.nix ];
+
+  # Provide wicd for easy wireless configuration.
+  #networking.wicd.enable = true;
+
+  # KDE complains if power management is disabled (to be precise, if
+  # there is no power management backend such as upower).
+  powerManagement.enable = true;
+
+  # Don't start the X server by default.
+  services.xserver.autorun = mkForce false;
+
+  # Auto-login as root.
+  services.xserver.displayManager.kdm.extraConfig =
+    ''
+      [X-*-Core]
+      AllowRootLogin=true
+      AutoLoginEnable=true
+      AutoLoginUser=root
+      AutoLoginPass=""
+    '';
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix b/nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix
new file mode 100644
index 00000000000..38d02ffd162
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix
@@ -0,0 +1,8 @@
+{ config, pkgs, ... }:
+
+{
+  imports = [ ./installation-cd-minimal.nix ];
+
+  boot.kernelPackages = pkgs.linuxPackages_3_10;
+  boot.vesa = false;
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
new file mode 100644
index 00000000000..a7498906a86
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
@@ -0,0 +1,11 @@
+# This module defines a small NixOS installation CD.  It does not
+# contain any graphical stuff.
+
+{ config, pkgs, ... }:
+
+{
+  imports =
+    [ ./installation-cd-base.nix
+      ../../profiles/minimal.nix
+    ];
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-new-kernel.nix b/nixos/modules/installer/cd-dvd/installation-cd-new-kernel.nix
new file mode 100644
index 00000000000..93bcbf00b25
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/installation-cd-new-kernel.nix
@@ -0,0 +1,8 @@
+{ config, pkgs, ... }:
+
+{
+  imports = [ ./installation-cd-graphical.nix ];
+
+  boot.kernelPackages = pkgs.linuxPackages_3_10;
+  boot.vesa = false;
+}
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
new file mode 100644
index 00000000000..de9728d677c
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -0,0 +1,315 @@
+# This module creates a bootable ISO image containing the given NixOS
+# configuration.  The derivation for the ISO image will be placed in
+# config.system.build.isoImage.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  # The Grub image.
+  grubImage = pkgs.runCommand "grub_eltorito" {}
+    ''
+      ${pkgs.grub2}/bin/grub-mkimage -O i386-pc -o tmp biosdisk iso9660 help linux linux16 chain png jpeg echo gfxmenu reboot
+      cat ${pkgs.grub2}/lib/grub/*/cdboot.img tmp > $out
+    ''; # */
+
+
+  # The configuration file for Grub.
+  grubCfg =
+    ''
+      set default=${builtins.toString config.boot.loader.grub.default}
+      set timeout=${builtins.toString config.boot.loader.grub.timeout}
+
+      if loadfont /boot/grub/unicode.pf2; then
+        set gfxmode=640x480
+        insmod gfxterm
+        insmod vbe
+        terminal_output gfxterm
+
+        insmod png
+        if background_image /boot/grub/splash.png; then
+          set color_normal=white/black
+          set color_highlight=black/white
+        else
+          set menu_color_normal=cyan/blue
+          set menu_color_highlight=white/blue
+        fi
+
+      fi
+
+      ${config.boot.loader.grub.extraEntries}
+    '';
+
+
+  # The efi boot image
+  efiImg = pkgs.runCommand "efi-image_eltorito" { buildInputs = [ pkgs.mtools ]; }
+    ''
+      #Let's hope 10M is enough
+      dd bs=2048 count=5120 if=/dev/zero of="$out"
+      ${pkgs.dosfstools}/sbin/mkfs.vfat "$out"
+      mmd -i "$out" efi
+      mmd -i "$out" efi/boot
+      mmd -i "$out" efi/nixos
+      mmd -i "$out" loader
+      mmd -i "$out" loader/entries
+      mcopy -v -i "$out" \
+        ${pkgs.gummiboot}/lib/gummiboot/gummiboot${targetArch}.efi \
+        ::efi/boot/boot${targetArch}.efi
+      mcopy -v -i "$out" \
+        ${config.boot.kernelPackages.kernel}/bzImage ::bzImage
+      mcopy -v -i "$out" \
+        ${config.system.build.initialRamdisk}/initrd ::efi/nixos/initrd
+      echo "title NixOS LiveCD" > boot-params
+      echo "linux /bzImage" >> boot-params
+      echo "initrd /efi/nixos/initrd" >> boot-params
+      echo "options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}" >> boot-params
+      mcopy -v -i "$out" boot-params ::loader/entries/nixos-livecd.conf
+      echo "default nixos-livecd" > boot-params
+      echo "timeout 5" >> boot-params
+      mcopy -v -i "$out" boot-params ::loader/loader.conf
+    '';
+
+  targetArch = if pkgs.stdenv.isi686 then
+    "ia32"
+  else if pkgs.stdenv.isx86_64 then
+    "x64"
+  else
+    throw "Unsupported architecture";
+
+in
+
+{
+  options = {
+
+    isoImage.isoName = mkOption {
+      default = "${config.isoImage.isoName}.iso";
+      description = ''
+        Name of the generated ISO image file.
+      '';
+    };
+
+    isoImage.isoBaseName = mkOption {
+      default = "nixos";
+      description = ''
+        Prefix of the name of the generated ISO image file.
+      '';
+    };
+
+    isoImage.compressImage = mkOption {
+      default = false;
+      description = ''
+        Whether the ISO image should be compressed using
+        <command>bzip2</command>.
+      '';
+    };
+
+    isoImage.volumeID = mkOption {
+      default = "NIXOS_BOOT_CD";
+      description = ''
+        Specifies the label or volume ID of the generated ISO image.
+        Note that the label is used by stage 1 of the boot process to
+        mount the CD, so it should be reasonably distinctive.
+      '';
+    };
+
+    isoImage.contents = mkOption {
+      example =
+        [ { source = pkgs.memtest86 + "/memtest.bin";
+            target = "boot/memtest.bin";
+          }
+        ];
+      description = ''
+        This option lists files to be copied to fixed locations in the
+        generated ISO image.
+      '';
+    };
+
+    isoImage.storeContents = mkOption {
+      example = [pkgs.stdenv];
+      description = ''
+        This option lists additional derivations to be included in the
+        Nix store in the generated ISO image.
+      '';
+    };
+
+    isoImage.includeSystemBuildDependencies = mkOption {
+      default = false;
+      example = true;
+      description = ''
+        Set this option to include all the needed sources etc in the
+        image. It significantly increases image size. Use that when
+        you want to be able to keep all the sources needed to build your
+        system or when you are going to install the system on a computer
+        with slow on non-existent network connection.
+      '';
+    };
+
+    isoImage.makeEfiBootable = mkOption {
+      default = false;
+      description = ''
+        Whether the ISO image should be an efi-bootable volume.
+      '';
+    };
+
+
+  };
+
+
+  config = {
+
+    boot.loader.grub.version = 2;
+
+    # Don't build the GRUB menu builder script, since we don't need it
+    # here and it causes a cyclic dependency.
+    boot.loader.grub.enable = false;
+
+    # !!! Hack - attributes expected by other modules.
+    system.boot.loader.kernelFile = "bzImage";
+    environment.systemPackages = [ pkgs.grub2 ];
+
+    # In stage 1 of the boot, mount the CD as the root FS by label so
+    # that we don't need to know its device.  We pass the label of the
+    # root filesystem on the kernel command line, rather than in
+    # `fileSystems' below.  This allows CD-to-USB converters such as
+    # UNetbootin to rewrite the kernel command line to pass the label or
+    # UUID of the USB stick.  It would be nicer to write
+    # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't
+    # recognise that.
+    boot.kernelParams = [ "root=LABEL=${config.isoImage.volumeID}" ];
+
+    # Note that /dev/root is a symlink to the actual root device
+    # specified on the kernel command line, created in the stage 1 init
+    # script.
+    fileSystems."/".device = "/dev/root";
+
+    fileSystems."/nix/store" =
+      { fsType = "squashfs";
+        device = "/nix-store.squashfs";
+        options = "loop";
+      };
+
+    boot.initrd.availableKernelModules = [ "squashfs" "iso9660" ];
+
+    boot.initrd.kernelModules = [ "loop" ];
+
+    # In stage 1, mount a tmpfs on top of / (the ISO image) and
+    # /nix/store (the squashfs image) to make this a live CD.
+    boot.initrd.postMountCommands =
+      ''
+        mkdir -p /unionfs-chroot/ro-root
+        mount --rbind $targetRoot /unionfs-chroot/ro-root
+
+        mkdir /unionfs-chroot/rw-root
+        mount -t tmpfs -o "mode=755" none /unionfs-chroot/rw-root
+        mkdir /mnt-root-union
+        unionfs -o allow_other,cow,chroot=/unionfs-chroot,max_files=32768 /rw-root=RW:/ro-root=RO /mnt-root-union
+        oldTargetRoot=$targetRoot
+        targetRoot=/mnt-root-union
+
+        mkdir /unionfs-chroot/rw-store
+        mount -t tmpfs -o "mode=755" none /unionfs-chroot/rw-store
+        mkdir -p $oldTargetRoot/nix/store
+        unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-store=RW:/ro-root/nix/store=RO /mnt-root-union/nix/store
+      '';
+
+    # Closures to be copied to the Nix store on the CD, namely the init
+    # script and the top-level system configuration directory.
+    isoImage.storeContents =
+      [ config.system.build.toplevel ] ++
+      optional config.isoImage.includeSystemBuildDependencies
+        config.system.build.toplevel.drvPath;
+
+    # Create the squashfs image that contains the Nix store.
+    system.build.squashfsStore = import ../../../lib/make-squashfs.nix {
+      inherit (pkgs) stdenv squashfsTools perl pathsFromGraph;
+      storeContents = config.isoImage.storeContents;
+    };
+
+    # Individual files to be included on the CD, outside of the Nix
+    # store on the CD.
+    isoImage.contents =
+      [ { source = grubImage;
+          target = "/boot/grub/grub_eltorito";
+        }
+        { source = pkgs.substituteAll  {
+            name = "grub.cfg";
+            src = pkgs.writeText "grub.cfg-in" grubCfg;
+            bootRoot = "/boot";
+          };
+          target = "/boot/grub/grub.cfg";
+        }
+        { source = config.boot.kernelPackages.kernel + "/bzImage";
+          target = "/boot/bzImage";
+        }
+        { source = config.system.build.initialRamdisk + "/initrd";
+          target = "/boot/initrd";
+        }
+        { source = "${pkgs.grub2}/share/grub/unicode.pf2";
+          target = "/boot/grub/unicode.pf2";
+        }
+        { source = config.boot.loader.grub.splashImage;
+          target = "/boot/grub/splash.png";
+        }
+        { source = config.system.build.squashfsStore;
+          target = "/nix-store.squashfs";
+        }
+        { # Quick hack: need a mount point for the store.
+          source = pkgs.runCommand "empty" {} "mkdir -p $out";
+          target = "/nix/store";
+        }
+      ] ++ optionals config.isoImage.makeEfiBootable [
+        { source = efiImg;
+          target = "/boot/efi.img";
+        }
+      ] ++ mapAttrsToList (n: v: { source = v; target = "/boot/${n}"; }) config.boot.loader.grub.extraFiles;
+
+    # The Grub menu.
+    boot.loader.grub.extraEntries =
+      ''
+        menuentry "NixOS ${config.system.nixosVersion} Installer" {
+          linux /boot/bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+          initrd /boot/initrd
+        }
+
+        menuentry "Boot from hard disk" {
+          set root=(hd0)
+          chainloader +1
+        }
+      '';
+
+    boot.loader.grub.timeout = 10;
+
+    # Create the ISO image.
+    system.build.isoImage = import ../../../lib/make-iso9660-image.nix ({
+      inherit (pkgs) stdenv perl cdrkit pathsFromGraph;
+
+      inherit (config.isoImage) isoName compressImage volumeID contents;
+
+      bootable = true;
+      bootImage = "/boot/grub/grub_eltorito";
+    } // optionalAttrs config.isoImage.makeEfiBootable {
+      efiBootable = true;
+      efiBootImage = "boot/efi.img";
+    });
+
+    boot.postBootCommands =
+      ''
+        # After booting, register the contents of the Nix store on the
+        # CD in the Nix database in the tmpfs.
+        ${config.environment.nix}/bin/nix-store --load-db < /nix/store/nix-path-registration
+
+        # nixos-rebuild also requires a "system" profile and an
+        # /etc/NIXOS tag.
+        touch /etc/NIXOS
+        ${config.environment.nix}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
+      '';
+
+    # Add vfat support to the initrd to enable people to copy the
+    # contents of the CD to a bootable USB stick. Need unionfs-fuse for union mounts
+    boot.initrd.supportedFilesystems = [ "vfat" "unionfs-fuse" ];
+
+  };
+
+}
diff --git a/nixos/modules/installer/cd-dvd/live-dvd.nix b/nixos/modules/installer/cd-dvd/live-dvd.nix
new file mode 100644
index 00000000000..e57be6d442e
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/live-dvd.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, ... }:
+
+{
+  imports = [ ./installation-cd-base.nix ];
+
+  # Build the build-time dependencies of this configuration on the DVD
+  # to speed up installation.
+  isoImage.storeContents = [ config.system.build.toplevel.drvPath ];
+
+  # Include lots of packages.
+  environment.systemPackages =
+    [ pkgs.utillinuxCurses
+      pkgs.upstartJobControl
+      pkgs.iproute
+      pkgs.bc
+      pkgs.fuse
+      pkgs.zsh
+      pkgs.sqlite
+      pkgs.gnupg
+      pkgs.manpages
+      pkgs.pinentry
+      pkgs.screen
+      pkgs.patch
+      pkgs.which
+      pkgs.diffutils
+      pkgs.file
+      pkgs.irssi
+      pkgs.mcabber
+      pkgs.mutt
+      pkgs.emacs
+      pkgs.vimHugeX
+      pkgs.bvi
+      pkgs.ddrescue
+      pkgs.cdrkit
+      pkgs.btrfsProgs
+      pkgs.xfsprogs
+      pkgs.jfsutils
+      pkgs.jfsrec
+      pkgs.ntfs3g
+      pkgs.subversion16
+      pkgs.monotone
+      pkgs.git
+      pkgs.darcs
+      pkgs.mercurial
+      pkgs.bazaar
+      pkgs.cvs
+      pkgs.pciutils
+      pkgs.hddtemp
+      pkgs.sdparm
+      pkgs.hdparm
+      pkgs.usbutils
+      pkgs.openssh
+      pkgs.lftp
+      pkgs.w3m
+      pkgs.openssl
+      pkgs.ncat
+      pkgs.lynx
+      pkgs.wget
+      pkgs.elinks
+      pkgs.socat
+      pkgs.squid
+      pkgs.unrar
+      pkgs.zip
+      pkgs.unzip
+      pkgs.lzma
+      pkgs.cabextract
+      pkgs.cpio
+      pkgs.lsof
+      pkgs.ltrace
+      pkgs.perl
+      pkgs.python
+      pkgs.ruby
+      pkgs.guile
+      pkgs.clisp
+      pkgs.tcl
+    ];
+
+}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
new file mode 100644
index 00000000000..85356118ce6
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
@@ -0,0 +1,164 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  # A dummy /etc/nixos/configuration.nix in the booted CD that
+  # rebuilds the CD's configuration (and allows the configuration to
+  # be modified, of course, providing a true live CD).  Problem is
+  # that we don't really know how the CD was built - the Nix
+  # expression language doesn't allow us to query the expression being
+  # evaluated.  So we'll just hope for the best.
+  dummyConfiguration = pkgs.writeText "configuration.nix"
+    ''
+      { config, pkgs, ... }:
+
+      { # Add your own options below, e.g.:
+        #   services.openssh.enable = true;
+        nixpkgs.config.platform = pkgs.platforms.fuloong2f_n32;
+      }
+    '';
+
+
+  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
+
+  # A clue for the kernel loading
+  kernelParams = pkgs.writeText "kernel-params.txt" ''
+    Kernel Parameters:
+      init=/boot/init systemConfig=/boot/init ${toString config.boot.kernelParams}
+  '';
+
+  # System wide nixpkgs config
+  nixpkgsUserConfig = pkgs.writeText "config.nix" ''
+    pkgs:
+    {
+      platform = pkgs.platforms.fuloong2f_n32;
+    }
+  '';
+
+in
+
+{
+  imports = [ ./system-tarball.nix ];
+
+  # Disable some other stuff we don't need.
+  security.sudo.enable = false;
+
+  # Include only the en_US locale.  This saves 75 MiB or so compared to
+  # the full glibcLocales package.
+  i18n.supportedLocales = ["en_US.UTF-8/UTF-8" "en_US/ISO-8859-1"];
+
+  # Include some utilities that are useful for installing or repairing
+  # the system.
+  environment.systemPackages =
+    [ pkgs.subversion # for nixos-checkout
+      pkgs.w3m # needed for the manual anyway
+      pkgs.testdisk # useful for repairing boot problems
+      pkgs.mssys # for writing Microsoft boot sectors / MBRs
+      pkgs.parted
+      pkgs.ddrescue
+      pkgs.ccrypt
+      pkgs.cryptsetup # needed for dm-crypt volumes
+
+      # Some networking tools.
+      pkgs.sshfsFuse
+      pkgs.socat
+      pkgs.screen
+      pkgs.wpa_supplicant # !!! should use the wpa module
+
+      # Hardware-related tools.
+      pkgs.sdparm
+      pkgs.hdparm
+      pkgs.dmraid
+
+      # Tools to create / manipulate filesystems.
+      pkgs.ntfsprogs # for resizing NTFS partitions
+      pkgs.btrfsProgs
+      pkgs.jfsutils
+      pkgs.jfsrec
+
+      # Some compression/archiver tools.
+      pkgs.unrar
+      pkgs.unzip
+      pkgs.zip
+      pkgs.xz
+      pkgs.dar # disk archiver
+
+      # Some editors.
+      pkgs.nvi
+      pkgs.bvi # binary editor
+      pkgs.joe
+    ];
+
+  # The initrd has to contain any module that might be necessary for
+  # mounting the CD/DVD.
+  boot.initrd.availableKernelModules =
+    [ "vfat" "reiserfs" ];
+
+  boot.kernelPackages = pkgs.linuxPackages_3_10;
+  boot.kernelParams = [ "console=tty1" ];
+
+  boot.postBootCommands =
+    ''
+      mkdir -p /mnt
+
+      cp ${dummyConfiguration} /etc/nixos/configuration.nix
+    '';
+
+  # Some more help text.
+  services.mingetty.helpLine =
+    ''
+
+      Log in as "root" with an empty password.  ${
+        if config.services.xserver.enable then
+          "Type `start xserver' to start\nthe graphical user interface."
+        else ""
+      }
+    '';
+
+  # Include the firmware for various wireless cards.
+  networking.enableRalinkFirmware = true;
+  networking.enableIntel2200BGFirmware = true;
+
+  # To speed up further installation of packages, include the complete stdenv
+  # in the Nix store of the tarball.
+  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ]
+    ++ [
+      {
+        object = config.system.build.bootStage2;
+        symlink = "/boot/init";
+      }
+      {
+        object = config.system.build.toplevel;
+        symlink = "/boot/system";
+      }
+    ];
+
+  tarball.contents = [
+    { source = kernelParams;
+      target = "/kernelparams.txt";
+    }
+    { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
+      target = "/boot/" + config.system.boot.loader.kernelFile;
+    }
+    { source = nixpkgsUserConfig;
+      target = "/root/.nixpkgs/config.nix";
+    }
+  ];
+
+  # Allow sshd to be started manually through "start sshd".  It should
+  # not be started by default on the installation CD because the
+  # default root password is empty.
+  services.openssh.enable = true;
+
+  jobs.openssh.startOn = pkgs.lib.mkOverrideTemplate 50 {} "";
+
+  boot.loader.grub.enable = false;
+  boot.loader.generationsDir.enable = false;
+  system.boot.loader.kernelFile = "vmlinux";
+
+  nixpkgs.config = {
+    platform = pkgs.platforms.fuloong2f_n32;
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt b/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt
new file mode 100644
index 00000000000..8f0a8d355c6
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt
@@ -0,0 +1,89 @@
+Let all the files in the system tarball sit in a directory served by NFS (the
+NFS root) like this in exportfs:
+  /home/pcroot    192.168.1.0/24(rw,no_root_squash,no_all_squash)
+
+Run "exportfs -a" after editing /etc/exportfs, for the nfs server to be aware
+of the changes.
+
+Use a tftp server serving the root of boot/ (from the system tarball).
+
+In order to have PXE boot, use the boot/dhcpd.conf-example file for your dhcpd
+server, as it will point your PXE clients to pxelinux.0 from the tftp server.
+Adapt the configuration to your network.
+
+Adapt the pxelinux configuration (boot/pxelinux.cfg/default) to set the path to
+your nfrroot. If you use ip=dhcp in the kernel, the nfs server ip will be taken
+from dhcp and so you don't have to specify it.
+
+The linux in bzImage includes network drivers for some usual cards.
+
+
+QEMU Testing
+---------------
+
+You can test qemu pxe boot without having a DHCP server adapted, but having
+nfsroot, like this:
+  qemu-system-x86_64 -tftp /home/pcroot/boot -net nic -net user,bootfile=pxelinux.0 -boot n
+
+I don't know how to use NFS through the qemu '-net user' though.
+
+
+QEMU Testing with NFS root and bridged network
+-------------------------------------------------
+
+This allows testing with qemu as any other host in your LAN.
+
+Testing with the real dhcpd server requires setting up a bridge and having a
+tap device.
+  tunctl -t tap0
+  brctl addbr br0
+  brctl addif br0 eth0
+  brctl addif tap0 eth0
+  ifconfig eth0 0.0.0.0 up
+  ifconfig tap0 0.0.0.0 up
+  ifconfig br0 up # With your ip configuration
+
+Then you can run qemu:
+  qemu-system-x86_64 -boot n -net tap,ifname=tap0,script=no -net nic,model=e1000
+
+
+Using the system-tarball-pc in a chroot
+--------------------------------------------------
+
+Installation:
+  mkdir nixos-chroot && cd nixos-chroot
+  tar xf your-system-tarball.tar.xz
+  mkdir sys dev proc tmp root var run
+  mount --bind /sys sys
+  mount --bind /dev dev
+  mount --bind /proc proc
+
+Activate the system: look for a directory in nix/store similar to:
+    "/nix/store/y0d1lcj9fppli0hl3x0m0ba5g1ndjv2j-nixos-feb97bx-53f008"
+Having found it, activate that nixos system *twice*:
+  chroot . /nix/store/SOMETHING-nixos-SOMETHING/activate
+  chroot . /nix/store/SOMETHING-nixos-SOMETHING/activate
+  
+This runs a 'hostname' command. Restore your old hostname with:
+  hostname OLDHOSTNAME
+
+Copy your system resolv.conf to the /etc/resolv.conf inside the chroot:
+  cp /etc/resolv.conf etc
+
+Then you can get an interactive shell in the nixos chroot. '*' means
+to run inside the chroot interactive shell
+  chroot . /bin/sh
+*  source /etc/profile
+
+Populate the nix database: that should be done in the init script if you
+had booted this nixos. Run:
+*  `grep local-cmds run/current-system/init`
+
+Then you can proceed normally subscribing to a nixos channel:
+  nix-channel --add http://nixos.org/channels/nixos-unstable
+  nix-channel --update
+
+Testing:
+  nix-env -i hello
+  which hello
+  hello
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-pc.nix b/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
new file mode 100644
index 00000000000..7619f074b74
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
@@ -0,0 +1,164 @@
+# 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, ... }:
+
+with pkgs.lib;
+
+let
+
+  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
+
+  # For PXE kernel loading
+  pxeconfig = pkgs.writeText "pxeconfig-default" ''
+    default menu.c32
+    prompt 0
+
+    label bootlocal
+      menu default
+      localboot 0
+      timeout 80
+      TOTALTIMEOUT 9000
+
+    label nixos
+      MENU LABEL ^NixOS using nfsroot
+      KERNEL bzImage
+      append ip=dhcp nfsroot=/home/pcroot systemConfig=${config.system.build.toplevel} init=${config.system.build.toplevel}/init rw
+
+    # I don't know how to make this boot with nfsroot (using the initrd)
+    label nixos_initrd
+      MENU LABEL NixOS booting the poor ^initrd.
+      KERNEL bzImage
+      append initrd=initrd ip=dhcp nfsroot=/home/pcroot systemConfig=${config.system.build.toplevel} init=${config.system.build.toplevel}/init rw
+
+    label memtest
+      MENU LABEL ^${pkgs.memtest86.name}
+      KERNEL memtest
+  '';
+
+  dhcpdExampleConfig = pkgs.writeText "dhcpd.conf-example" ''
+    # Example configuration for booting PXE.
+    allow booting;
+    allow bootp;
+
+    # Adapt this to your network configuration.
+    option domain-name "local";
+    option subnet-mask 255.255.255.0;
+    option broadcast-address 192.168.1.255;
+    option domain-name-servers 192.168.1.1;
+    option routers 192.168.1.1;
+
+    # PXE-specific configuration directives...
+    # Some BIOS don't accept slashes for paths inside the tftp servers,
+    # and will report Access Violation if they see slashes.
+    filename "pxelinux.0";
+    # For the TFTP and NFS root server. Set the IP of your server.
+    next-server 192.168.1.34;
+
+    subnet 192.168.1.0 netmask 255.255.255.0 {
+      range 192.168.1.50 192.168.1.55;
+    }
+  '';
+
+  readme = ./system-tarball-pc-readme.txt;
+
+in
+
+{
+  imports =
+    [ ./system-tarball.nix
+
+      # Profiles of this basic installation.
+      ../../profiles/all-hardware.nix
+      ../../profiles/base.nix
+      ../../profiles/installation-device.nix
+    ];
+
+  # To speed up further installation of packages, include the complete stdenv
+  # in the Nix store of the tarball.
+  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ];
+
+  tarball.contents =
+    [ { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
+        target = "/boot/" + config.system.boot.loader.kernelFile;
+      }
+      { source = "${pkgs.syslinux}/share/syslinux/pxelinux.0";
+        target = "/boot/pxelinux.0";
+      }
+      { source = "${pkgs.syslinux}/share/syslinux/menu.c32";
+        target = "/boot/menu.c32";
+      }
+      { source = pxeconfig;
+        target = "/boot/pxelinux.cfg/default";
+      }
+      { source = readme;
+        target = "/readme.txt";
+      }
+      { source = dhcpdExampleConfig;
+        target = "/boot/dhcpd.conf-example";
+      }
+      { source = "${pkgs.memtest86}/memtest.bin";
+        # We can't leave '.bin', because pxelinux interprets this specially,
+        # and it would not load the image fine.
+        # http://forum.canardpc.com/threads/46464-0104-when-launched-via-pxe
+        target = "/boot/memtest";
+      }
+    ];
+
+  # Allow sshd to be started manually through "start sshd".  It should
+  # not be started by default on the installation CD because the
+  # default root password is empty.
+  services.openssh.enable = true;
+  jobs.openssh.startOn = pkgs.lib.mkOverrideTemplate 50 {} "";
+
+  # To be able to use the systemTarball to catch troubles.
+  boot.crashDump = {
+    enable = true;
+    kernelPackages = pkgs.linuxPackages_3_4;
+  };
+
+  # No grub for the tarball.
+  boot.loader.grub.enable = false;
+
+  /* fake entry, just to have a happy stage-1. Users
+     may boot without having stage-1 though */
+  fileSystems = [
+    { mountPoint = "/";
+      device = "/dev/something";
+      }
+  ];
+
+  nixpkgs.config = {
+    packageOverrides = p: rec {
+      linux_3_4 = p.linux_3_4.override {
+        extraConfig = ''
+          # Enable drivers in kernel for most NICs.
+          E1000 y
+          # E1000E y
+          # ATH5K y
+          8139TOO y
+          NE2K_PCI y
+          ATL1 y
+          ATL1E y
+          ATL1C y
+          VORTEX y
+          VIA_RHINE y
+          R8169 y
+
+          # Enable nfs root boot
+          UNIX y # http://www.linux-mips.org/archives/linux-mips/2006-11/msg00113.html
+          IP_PNP y
+          IP_PNP_DHCP y
+          FSCACHE y
+          NFS_FS y
+          NFS_FSCACHE y
+          ROOT_NFS y
+
+          # Enable devtmpfs
+          DEVTMPFS y
+          DEVTMPFS_MOUNT y
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
new file mode 100644
index 00000000000..20fe4de2cd8
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
@@ -0,0 +1,176 @@
+# This module contains the basic configuration for building a NixOS
+# tarball for the sheevaplug.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  # A dummy /etc/nixos/configuration.nix in the booted CD that
+  # rebuilds the CD's configuration (and allows the configuration to
+  # be modified, of course, providing a true live CD).  Problem is
+  # that we don't really know how the CD was built - the Nix
+  # expression language doesn't allow us to query the expression being
+  # evaluated.  So we'll just hope for the best.
+  dummyConfiguration = pkgs.writeText "configuration.nix"
+    ''
+      { config, pkgs, ... }:
+
+      {
+        # Add your own options below and run "nixos-rebuild switch".
+        # E.g.,
+        #   services.openssh.enable = true;
+      }
+    '';
+
+
+  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
+
+  # A clue for the kernel loading
+  kernelParams = pkgs.writeText "kernel-params.txt" ''
+    Kernel Parameters:
+      init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
+  '';
+
+
+in
+
+{
+  imports = [ ./system-tarball.nix ];
+
+  # Disable some other stuff we don't need.
+  security.sudo.enable = false;
+
+  # Include only the en_US locale.  This saves 75 MiB or so compared to
+  # the full glibcLocales package.
+  i18n.supportedLocales = ["en_US.UTF-8/UTF-8" "en_US/ISO-8859-1"];
+
+  # Include some utilities that are useful for installing or repairing
+  # the system.
+  environment.systemPackages =
+    [ pkgs.subversion # for nixos-checkout
+      pkgs.w3m # needed for the manual anyway
+      pkgs.ddrescue
+      pkgs.ccrypt
+      pkgs.cryptsetup # needed for dm-crypt volumes
+
+      # Some networking tools.
+      pkgs.sshfsFuse
+      pkgs.socat
+      pkgs.screen
+      pkgs.wpa_supplicant # !!! should use the wpa module
+
+      # Hardware-related tools.
+      pkgs.sdparm
+      pkgs.hdparm
+      pkgs.dmraid
+
+      # Tools to create / manipulate filesystems.
+      pkgs.btrfsProgs
+
+      # Some compression/archiver tools.
+      pkgs.unrar
+      pkgs.unzip
+      pkgs.zip
+      pkgs.xz
+      pkgs.dar # disk archiver
+
+      # Some editors.
+      pkgs.nvi
+      pkgs.bvi # binary editor
+      pkgs.joe
+    ];
+
+  boot.loader.grub.enable = false;
+  boot.loader.generationsDir.enable = false;
+  system.boot.loader.kernelFile = "uImage";
+
+  boot.initrd.availableKernelModules =
+    [ "mvsdio" "mmc_block" "reiserfs" "ext3" "ums-cypress" "rtc_mv"
+      "ext4" ];
+
+  boot.postBootCommands =
+    ''
+      mkdir -p /mnt
+
+      cp ${dummyConfiguration} /etc/nixos/configuration.nix
+    '';
+
+  boot.initrd.extraUtilsCommands =
+    ''
+      cp ${pkgs.utillinux}/sbin/hwclock $out/bin
+    '';
+
+  boot.initrd.postDeviceCommands =
+    ''
+      hwclock -s
+    '';
+
+  boot.kernelParams =
+    [
+      "selinux=0"
+      "console=tty1"
+      # "console=ttyS0,115200n8"  # serial console
+    ];
+
+  boot.kernelPackages = pkgs.linuxPackages_3_4;
+
+  boot.supportedFilesystems = [ "reiserfs" ];
+
+  /* fake entry, just to have a happy stage-1. Users
+     may boot without having stage-1 though */
+  fileSystems = [
+    { mountPoint = "/";
+      device = "/dev/something";
+      }
+  ];
+
+  services.mingetty = {
+    # Some more help text.
+    helpLine = ''
+      Log in as "root" with an empty password.  ${
+        if config.services.xserver.enable then
+          "Type `start xserver' to start\nthe graphical user interface."
+        else ""
+      }
+    '';
+  };
+
+  # Setting vesa, we don't get the nvidia driver, which can't work in arm.
+  services.xserver.videoDriver = "vesa";
+  services.xserver.videoDrivers = [];
+  services.nixosManual.enable = false;
+
+  # Include the firmware for various wireless cards.
+  networking.enableRalinkFirmware = true;
+  networking.enableIntel2200BGFirmware = true;
+
+  # To speed up further installation of packages, include the complete stdenv
+  # in the Nix store of the tarball.
+  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ];
+  tarball.contents = [
+    { source = kernelParams;
+      target = "/kernelparams.txt";
+    }
+    { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
+      target = "/boot/" + config.system.boot.loader.kernelFile;
+    }
+    { source = pkgs.ubootSheevaplug;
+      target = "/boot/uboot";
+    }
+  ];
+
+  # Allow sshd to be started manually through "start sshd".  It should
+  # not be started by default on the installation CD because the
+  # default root password is empty.
+  services.openssh.enable = true;
+  jobs.openssh.startOn = pkgs.lib.mkOverrideTemplate 50 {} "";
+
+  # cpufrequtils fails to build on non-pc
+  powerManagement.enable = false;
+
+  nixpkgs.config = {
+    platform = pkgs.platforms.sheevaplug;
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix
new file mode 100644
index 00000000000..6bf8eebdac5
--- /dev/null
+++ b/nixos/modules/installer/cd-dvd/system-tarball.nix
@@ -0,0 +1,92 @@
+# This module creates a bootable ISO image containing the given NixOS
+# configuration.  The derivation for the ISO image will be placed in
+# config.system.build.tarball.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  versionFile = pkgs.writeText "nixos-version" config.system.nixosVersion;
+
+in
+
+{
+  options = {
+    tarball.contents = mkOption {
+      example =
+        [ { source = pkgs.memtest86 + "/memtest.bin";
+            target = "boot/memtest.bin";
+          }
+        ];
+      description = ''
+        This option lists files to be copied to fixed locations in the
+        generated ISO image.
+      '';
+    };
+
+    tarball.storeContents = mkOption {
+      example = [pkgs.stdenv];
+      description = ''
+        This option lists additional derivations to be included in the
+        Nix store in the generated ISO image.
+      '';
+    };
+
+  };
+
+  config = {
+
+    # In stage 1 of the boot, mount the CD/DVD as the root FS by label
+    # so that we don't need to know its device.
+    fileSystems = [ ];
+
+    # boot.initrd.availableKernelModules = [ "mvsdio" "mmc_block" "reiserfs" "ext3" "ext4" ];
+
+    # boot.initrd.kernelModules = [ "rtc_mv" ];
+
+    # Closures to be copied to the Nix store on the CD, namely the init
+    # script and the top-level system configuration directory.
+    tarball.storeContents =
+      [ { object = config.system.build.toplevel;
+          symlink = "/run/current-system";
+        }
+      ];
+
+    # Individual files to be included on the CD, outside of the Nix
+    # store on the CD.
+    tarball.contents =
+      [ { source = config.system.build.initialRamdisk + "/initrd";
+          target = "/boot/initrd";
+        }
+        { source = versionFile;
+          target = "/nixos-version.txt";
+        }
+      ];
+
+    # Create the tarball
+    system.build.tarball = import ../../../lib/make-system-tarball.nix {
+      inherit (pkgs) stdenv perl xz pathsFromGraph;
+
+      inherit (config.tarball) contents storeContents;
+    };
+
+    boot.postBootCommands =
+      ''
+        # After booting, register the contents of the Nix store on the
+        # CD in the Nix database in the tmpfs.
+        if [ -f /nix-path-registration ]; then
+          ${config.environment.nix}/bin/nix-store --load-db < /nix-path-registration &&
+          rm /nix-path-registration
+        fi
+
+        # nixos-rebuild also requires a "system" profile and an
+        # /etc/NIXOS tag.
+        touch /etc/NIXOS
+        ${config.environment.nix}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/installer/scan/detected.nix b/nixos/modules/installer/scan/detected.nix
new file mode 100644
index 00000000000..09d04608e68
--- /dev/null
+++ b/nixos/modules/installer/scan/detected.nix
@@ -0,0 +1,13 @@
+# List all devices which are detected by nixos-hardware-scan.
+# Common devices are enabled by default.
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+{
+  config = mkDefault {
+    # Wireless card firmware
+    networking.enableIntel2200BGFirmware = true;
+    networking.enableIntel3945ABGFirmware = true;
+  };
+}
diff --git a/nixos/modules/installer/scan/not-detected.nix b/nixos/modules/installer/scan/not-detected.nix
new file mode 100644
index 00000000000..814858fdffd
--- /dev/null
+++ b/nixos/modules/installer/scan/not-detected.nix
@@ -0,0 +1,9 @@
+# List all devices which are _not_ detected by nixos-hardware-scan.
+# Common devices are enabled by default.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  hardware.enableAllFirmware = true;
+}
diff --git a/nixos/modules/installer/tools/get-version-suffix b/nixos/modules/installer/tools/get-version-suffix
new file mode 100644
index 00000000000..76cec8d5dae
--- /dev/null
+++ b/nixos/modules/installer/tools/get-version-suffix
@@ -0,0 +1,29 @@
+getVersion() {
+    local dir="$1"
+    rev=
+    if [ -e "$dir/.git" ]; then
+        if [ -z "$(type -P git)" ]; then
+            echo "warning: Git not found; cannot figure out revision of $dir" >&2
+            return
+        fi
+        cd "$dir"
+        rev=$(git rev-parse --short HEAD)
+        if git describe --always --dirty | grep -q dirty; then
+            rev+=M
+        fi
+    fi
+}
+
+if nixos=$(nix-instantiate --find-file nixos "$@"); then
+    getVersion $nixos
+    if [ -n "$rev" ]; then
+        suffix="pre-$rev"
+        if nixpkgs=$(nix-instantiate --find-file nixpkgs "$@"); then
+            getVersion $nixpkgs
+            if [ -n "$rev" ]; then
+                suffix+="-$rev"
+            fi
+        fi
+        echo $suffix
+    fi
+fi
diff --git a/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
new file mode 100644
index 00000000000..5e77b701ff5
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-build-vms/build-vms.nix
@@ -0,0 +1,9 @@
+{ system ? builtins.currentSystem
+, networkExpr
+}:
+
+let nodes = import networkExpr; in
+
+with import ../../../../lib/testing.nix { inherit system; };
+
+(complete { inherit nodes; testScript = ""; }).driver
diff --git a/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh b/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh
new file mode 100644
index 00000000000..0a6e8b920a1
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-build-vms/nixos-build-vms.sh
@@ -0,0 +1,62 @@
+#! @shell@ -e
+
+# Shows the usage of this command to the user
+
+showUsage()
+{
+    echo "Usage: $0 network_expr"
+    echo "Options:"
+    echo
+    echo "--no-out-link   Do not create a 'result' symlink"
+    echo "--show-trace    Shows the output trace"
+    echo "-h,--help       Shows the usage of this command"
+}
+
+# Parse valid argument options
+
+PARAMS=`getopt -n $0 -o h -l no-out-link,show-trace,help -- "$@"`
+
+if [ $? != 0 ]
+then
+    showUsage
+    exit 1
+fi
+
+eval set -- "$PARAMS"
+
+# Evaluate valid options
+
+while [ "$1" != "--" ]
+do
+    case "$1" in
+	--no-out-link)
+	    noOutLinkArg="--no-out-link"
+	    ;;
+	--show-trace)
+	    showTraceArg="--show-trace"
+	    ;;
+	-h|--help)
+	    showUsage
+	    exit 0
+	    ;;
+    esac
+    
+    shift
+done
+
+shift
+
+# Validate the given options
+
+if [ "$1" = "" ]
+then
+    echo "ERROR: A network expression must be specified!" >&2
+    exit 1
+else
+    networkExpr=$(readlink -f $1)
+fi
+
+# Build a network of VMs
+
+nix-build '<nixos/modules/installer/tools/nixos-build-vms/build-vms.nix>' \
+    --argstr networkExpr $networkExpr $noOutLinkArg $showTraceArg
diff --git a/nixos/modules/installer/tools/nixos-checkout.nix b/nixos/modules/installer/tools/nixos-checkout.nix
new file mode 100644
index 00000000000..1a734ca5eeb
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-checkout.nix
@@ -0,0 +1,55 @@
+# This module generates the nixos-checkout script, which replaces the
+# NixOS and Nixpkgs source trees in /etc/nixos/{nixos,nixpkgs} with
+# Git checkouts.
+
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+
+  nixosCheckout = pkgs.substituteAll {
+    name = "nixos-checkout";
+    dir = "bin";
+    isExecutable = true;
+    src = pkgs.writeScript "nixos-checkout"
+      ''
+        #! ${pkgs.stdenv.shell} -e
+
+        if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
+          echo "Usage: `basename $0` [PREFIX]. See NixOS Manual for more info."
+          exit 0
+        fi        
+
+        prefix="$1"
+        if [ -z "$prefix" ]; then prefix=/etc/nixos; fi
+        mkdir -p "$prefix"
+        cd "$prefix"
+
+        if [ -z "$(type -P git)" ]; then
+            echo "installing Git..."
+            nix-env -iA nixos.pkgs.git || nix-env -i git
+        fi
+
+        # Move any old nixos or nixpkgs directories out of the way.
+        backupTimestamp=$(date "+%Y%m%d%H%M%S")
+
+        if [ -e nixos -a ! -e nixos/.git ]; then
+            mv nixos nixos-$backupTimestamp
+        fi
+
+        if [ -e nixpkgs -a ! -e nixpkgs/.git ]; then
+            mv nixpkgs nixpkgs-$backupTimestamp
+        fi
+
+        # Check out the NixOS and Nixpkgs sources.
+        git clone git://github.com/NixOS/nixos.git nixos
+        git clone git://github.com/NixOS/nixpkgs.git nixpkgs
+      '';
+   };
+
+in
+
+{
+  environment.systemPackages = [ nixosCheckout ];
+}
diff --git a/nixos/modules/installer/tools/nixos-gen-seccure-keys.sh b/nixos/modules/installer/tools/nixos-gen-seccure-keys.sh
new file mode 100644
index 00000000000..a97eef672f7
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-gen-seccure-keys.sh
@@ -0,0 +1,13 @@
+#! @shell@ -e
+
+mkdir -p /var/elliptic-keys
+chmod 0755 /var/elliptic-keys
+cd /var/elliptic-keys
+touch private
+chmod 0700 private
+dd if=/dev/urandom bs=128 count=1 of=private
+chmod 0500 private
+public=$(seccure-key -F private 2>&1)
+echo ${public#*The public key is: } > public
+chmod 0555 public
+
diff --git a/nixos/modules/installer/tools/nixos-hardware-scan.pl b/nixos/modules/installer/tools/nixos-hardware-scan.pl
new file mode 100644
index 00000000000..3204f3d4051
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-hardware-scan.pl
@@ -0,0 +1,248 @@
+#! @perl@/bin/perl -w
+
+use File::Spec;
+use File::Basename;
+
+
+my @attrs = ();
+my @kernelModules = ();
+my @initrdKernelModules = ();
+my @modulePackages = ();
+my @imports = ("<nixos/modules/installer/scan/not-detected.nix>");
+
+
+sub debug {
+    return unless defined $ENV{"DEBUG"};
+    print STDERR @_;
+}
+
+
+# Read a file, returning undef if the file cannot be opened.
+sub readFile {
+    my $filename = shift;
+    my $res;
+    if (open FILE, "<$filename") {
+        my $prev = $/;
+        undef $/;
+        $res = <FILE>;
+        $/ = $prev;
+        close FILE;
+        chomp $res;
+    }
+    return $res;
+}
+
+
+my $cpuinfo = readFile "/proc/cpuinfo";
+
+
+sub hasCPUFeature {
+    my $feature = shift;
+    return $cpuinfo =~ /^flags\s*:.* $feature( |$)/m;
+}
+
+
+# Detect the number of CPU cores.
+my $cpus = scalar (grep {/^processor\s*:/} (split '\n', $cpuinfo));
+
+
+# Virtualization support?
+push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
+push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
+
+
+# Look at the PCI devices and add necessary modules.  Note that most
+# modules are auto-detected so we don't need to list them here.
+# However, some are needed in the initrd to boot the system.
+
+my $videoDriver;
+
+sub pciCheck {
+    my $path = shift;
+    my $vendor = readFile "$path/vendor";
+    my $device = readFile "$path/device";
+    my $class = readFile "$path/class";
+    
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+    
+    debug "$path: $vendor $device $class";
+    debug " $module" if defined $module;
+    debug "\n";
+
+    if (defined $module) {
+        # See the bottom of http://pciids.sourceforge.net/pci.ids for
+        # device classes.
+        if (# Mass-storage controller.  Definitely important.
+            $class =~ /^0x01/ ||
+
+            # Firewire controller.  A disk might be attached.
+            $class =~ /^0x0c00/ ||
+
+            # USB controller.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            $class =~ /^0x0c03/
+            )
+        {
+            push @initrdKernelModules, $module;
+        }
+    }
+
+    # broadcom STA driver (wl.ko)
+    # list taken from http://www.broadcom.com/docs/linux_sta/README.txt
+    if ($vendor eq "0x14e4" &&
+        ($device eq "0x4311" || $device eq "0x4312" || $device eq "0x4313" ||
+         $device eq "0x4315" || $device eq "0x4327" || $device eq "0x4328" ||
+         $device eq "0x4329" || $device eq "0x432a" || $device eq "0x432b" ||
+         $device eq "0x432c" || $device eq "0x432d" || $device eq "0x4353" ||
+         $device eq "0x4357" || $device eq "0x4358" || $device eq "0x4359" ) )
+     {
+        push @modulePackages, "config.boot.kernelPackages.broadcom_sta";
+        push @kernelModules, "wl";
+     }
+
+    # Can't rely on $module here, since the module may not be loaded
+    # due to missing firmware.  Ideally we would check modules.pcimap
+    # here.
+    push @attrs, "networking.enableIntel2200BGFirmware = true;" if
+        $vendor eq "0x8086" &&
+        ($device eq "0x1043" || $device eq "0x104f" || $device eq "0x4220" ||
+         $device eq "0x4221" || $device eq "0x4223" || $device eq "0x4224");
+
+    push @attrs, "networking.enableIntel3945ABGFirmware = true;" if
+        $vendor eq "0x8086" &&
+        ($device eq "0x4229" || $device eq "0x4230" ||
+         $device eq "0x4222" || $device eq "0x4227");
+
+    # Assume that all NVIDIA cards are supported by the NVIDIA driver.
+    # There may be exceptions (e.g. old cards).
+    $videoDriver = "nvidia" if $vendor eq "0x10de" && $class =~ /^0x03/;
+}
+
+foreach my $path (glob "/sys/bus/pci/devices/*") {
+    pciCheck $path;
+}
+
+
+# Idem for USB devices.
+
+sub usbCheck {
+    my $path = shift;
+    my $class = readFile "$path/bInterfaceClass";
+    my $subclass = readFile "$path/bInterfaceSubClass";
+    my $protocol = readFile "$path/bInterfaceProtocol";
+
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+    
+    debug "$path: $class $subclass $protocol";
+    debug " $module" if defined $module;
+    debug "\n";
+ 
+    if (defined $module) {
+        if (# Mass-storage controller.  Definitely important.
+            $class eq "08" ||
+
+            # Keyboard.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            ($class eq "03" && $protocol eq "01")
+            )
+        {
+            push @initrdKernelModules, $module;
+        }
+    }
+}
+
+foreach my $path (glob "/sys/bus/usb/devices/*") {
+    if (-e "$path/bInterfaceClass") {
+        usbCheck $path;
+    }
+}
+
+
+# Add the modules for all block devices.
+
+foreach my $path (glob "/sys/class/block/*") {
+    my $module;
+    if (-e "$path/device/driver/module") {
+        $module = basename `readlink -f $path/device/driver/module`;
+        chomp $module;
+        push @initrdKernelModules, $module;
+    }
+}
+
+
+if ($videoDriver) {
+    push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];";
+}
+
+
+# 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;"
+}
+
+
+# Generate the configuration file.
+
+sub removeDups {
+    my %seen;
+    my @res = ();
+    foreach my $s (@_) {
+        if (!defined $seen{$s}) {
+            $seen{$s} = "";
+            push @res, $s;
+        }
+    }
+    return @res;
+}
+
+sub toNixExpr {
+    my $res = "";
+    foreach my $s (@_) {
+        $res .= " \"$s\"";
+    }
+    return $res;
+}
+
+sub multiLineList {
+    my $indent = shift;
+    my $res = "";
+    $res = "\n" if scalar @_ > 0;
+    foreach my $s (@_) {
+        $res .= "$indent$s\n";
+    }
+    return $res;
+}
+
+my $initrdKernelModules = toNixExpr(removeDups @initrdKernelModules);
+my $kernelModules = toNixExpr(removeDups @kernelModules);
+my $modulePackages = toNixExpr(removeDups @modulePackages);
+my $attrs = multiLineList("  ", removeDups @attrs);
+my $imports = multiLineList("    ", removeDups @imports);
+
+
+print <<EOF ;
+# This is a generated file.  Do not modify!
+# Make changes to /etc/nixos/configuration.nix instead.
+{ config, pkgs, ... }:
+
+{
+  imports = [$imports  ];
+
+  boot.initrd.kernelModules = [$initrdKernelModules ];
+  boot.kernelModules = [$kernelModules ];
+  boot.extraModulePackages = [$modulePackages ];
+
+  nix.maxJobs = $cpus;
+$attrs}
+EOF
+# workaround for a bug in substituteAll
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
new file mode 100644
index 00000000000..a4ac5b68dd7
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -0,0 +1,238 @@
+#! @shell@
+
+# - [mount target device] <- currently disabled
+# - make Nix store etc.
+# - copy closure of Nix to target device
+# - register validity
+# - with a chroot to the target device:
+#   * nix-env -p /nix/var/nix/profiles/system -i <nix-expr for the configuration>
+#   * run the activation script of the configuration (also installs Grub)
+
+# Parse the command line for the -I flag
+extraBuildFlags=()
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+      -I)
+        given_path="$1"; shift 1
+        absolute_path=$(readlink -m $given_path)
+        extraBuildFlags+=("$i" "/mnt$absolute_path")
+        ;;
+      *)
+        echo "$0: unknown option \`$i'"
+        exit 1
+        ;;
+    esac
+done
+
+set -e
+shopt -s nullglob
+
+if test -z "$mountPoint"; then
+    mountPoint=/mnt
+fi
+
+if test -z "$NIXOS_CONFIG"; then
+    NIXOS_CONFIG=/etc/nixos/configuration.nix
+fi
+
+if ! test -e "$mountPoint"; then
+    echo "mount point $mountPoint doesn't exist"
+    exit 1
+fi
+
+if ! grep -F -q " $mountPoint " /proc/mounts; then
+    echo "$mountPoint doesn't appear to be a mount point"
+    exit 1
+fi
+
+if ! test -e "$mountPoint/$NIXOS_CONFIG"; then
+    echo "configuration file $mountPoint/$NIXOS_CONFIG doesn't exist"
+    exit 1
+fi
+
+
+
+# Mount some stuff in the target root directory.  We bind-mount /etc
+# into the chroot because we need networking and the nixbld user
+# accounts in /etc/passwd.  But we do need the target's /etc/nixos.
+mkdir -m 0755 -p $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/mnt $mountPoint/mnt2 $mountPoint/mnt-nixos $mountPoint/mnt-nixpkgs $mountPoint/etc /etc/nixos
+mount --make-private / # systemd makes / shared, which is annoying
+mount --bind / $mountPoint/mnt
+mount --bind /nix $mountPoint/mnt/nix
+mount --bind /nix/store $mountPoint/mnt/nix/store
+mount --bind /dev $mountPoint/dev
+mount --bind /dev/shm $mountPoint/dev/shm
+mount --bind /proc $mountPoint/proc
+mount --bind /sys $mountPoint/sys
+mount --bind /sys/firmware/efi/efivars $mountPoint/sys/firmware/efi/efivars &>/dev/null || true
+mount --bind $mountPoint/etc/nixos $mountPoint/mnt2
+mount --bind /etc $mountPoint/etc
+mount --bind $mountPoint/mnt2 $mountPoint/etc/nixos
+
+cleanup() {
+    set +e
+    mountpoint -q $mountPoint/etc/nixos && umount $mountPoint/etc/nixos
+    mountpoint -q $mountPoint/etc && umount $mountPoint/etc
+    umount $mountPoint/mnt2
+    umount $mountPoint/mnt-nixos
+    umount $mountPoint/mnt-nixpkgs
+    umount $mountPoint/sys/firmware/efi/efivars &>/dev/null || true
+    umount $mountPoint/sys
+    umount $mountPoint/proc
+    umount $mountPoint/dev/shm
+    umount $mountPoint/dev
+    umount $mountPoint/mnt/nix/store
+    umount $mountPoint/mnt/nix
+    umount $mountPoint/mnt
+    rmdir $mountPoint/mnt $mountPoint/mnt2 $mountPoint/mnt-nixos $mountPoint/mnt-nixpkgs
+}
+
+trap "cleanup" EXIT
+
+mkdir -m 01777 -p $mountPoint/tmp
+mkdir -m 0755 -p $mountPoint/var
+
+
+# Create the necessary Nix directories on the target device, if they
+# don't already exist.
+mkdir -m 0755 -p \
+    $mountPoint/nix/var/nix/gcroots \
+    $mountPoint/nix/var/nix/temproots \
+    $mountPoint/nix/var/nix/manifests \
+    $mountPoint/nix/var/nix/userpool \
+    $mountPoint/nix/var/nix/profiles \
+    $mountPoint/nix/var/nix/db \
+    $mountPoint/nix/var/log/nix/drvs
+
+mkdir -m 1775 -p $mountPoint/nix/store
+build_users_group=$(@perl@/bin/perl -I @nix@/lib/perl5/site_perl/*/* -e 'use Nix::Config; Nix::Config::readConfig; print $Nix::Config::config{"build-users-group"};')
+if test -n "$build_users_group"; then
+    chown root:"$build_users_group" $mountPoint/nix/store
+else
+    chown root $mountPoint/nix/store
+fi
+
+
+# Get the store paths to copy from the references graph.
+storePaths=$(@perl@/bin/perl @pathsFromGraph@ @nixClosure@)
+
+
+# Copy Nix to the Nix store on the target device.
+echo "copying Nix to $mountPoint...."
+for i in $storePaths; do
+    echo "  $i"
+    chattr -R -i $mountPoint/$i 2> /dev/null || true # clear immutable bit
+    rsync -a $i $mountPoint/nix/store/
+done
+
+
+# We don't have locale-archive in the chroot, so clear $LANG.
+export LANG=
+export LC_ALL=
+export LC_TIME=
+
+
+# There is no daemon in the chroot
+unset NIX_REMOTE
+
+
+# Create a temporary Nix config file that causes the nixbld users to
+# be used.
+if test -n "$build_users_group"; then
+    echo "build-users-group = $build_users_group" > $mountPoint/tmp/nix.conf
+fi
+binary_caches=$(@perl@/bin/perl -I @nix@/lib/perl5/site_perl/*/* -e 'use Nix::Config; Nix::Config::readConfig; print $Nix::Config::config{"binary-caches"};')
+if test -n "$binary_caches"; then
+    echo "binary-caches = $binary_caches" >> $mountPoint/tmp/nix.conf
+fi
+export NIX_CONF_DIR=/tmp
+
+
+# Register the paths in the Nix closure as valid.  This is necessary
+# to prevent them from being deleted the first time we install
+# something.  (I.e., Nix will see that, e.g., the glibc path is not
+# valid, delete it to get it out of the way, but as a result nothing
+# will work anymore.)
+chroot $mountPoint @nix@/bin/nix-store --register-validity < @nixClosure@
+
+
+# Create the required /bin/sh symlink; otherwise lots of things
+# (notably the system() function) won't work.
+mkdir -m 0755 -p $mountPoint/bin
+# !!! assuming that @shell@ is in the closure
+ln -sf @shell@ $mountPoint/bin/sh
+
+
+if test -n "$NIXOS_PREPARE_CHROOT_ONLY"; then
+    echo "User requested only to prepare chroot. Exiting."
+    exit 0
+fi
+
+
+# Make the build below copy paths from the CD if possible.  Note that
+# /mnt in the chroot is the root of the CD.
+export NIX_OTHER_STORES=/mnt/nix:$NIX_OTHER_STORES
+
+p=@nix@/libexec/nix/substituters
+export NIX_SUBSTITUTERS=$p/copy-from-other-stores.pl:$p/download-from-binary-cache.pl
+
+
+# Make manifests available in the chroot.
+rm -f $mountPoint/nix/var/nix/manifests/*
+for i in /nix/var/nix/manifests/*.nixmanifest; do
+    chroot $mountPoint @nix@/bin/nix-store -r "$(readlink -f "$i")" > /dev/null
+    cp -pd "$i" $mountPoint/nix/var/nix/manifests/
+done
+
+
+# Get the absolute path to the NixOS/Nixpkgs sources.
+mount --bind $(readlink -f $(nix-instantiate --find-file nixpkgs)) $mountPoint/mnt-nixpkgs
+mount --bind $(readlink -f $(nix-instantiate --find-file nixos)) $mountPoint/mnt-nixos
+
+
+# Build the specified Nix expression in the target store and install
+# it into the system configuration profile.
+echo "building the system configuration..."
+NIX_PATH="nixpkgs=/mnt-nixpkgs:nixos=/mnt-nixos:nixos-config=$NIXOS_CONFIG" NIXOS_CONFIG= \
+    chroot $mountPoint @nix@/bin/nix-env \
+    "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/system -f '<nixos>' --set -A system --show-trace
+
+
+# Copy the NixOS/Nixpkgs sources to the target as the initial contents
+# of the NixOS channel.
+mkdir -m 0755 -p $mountPoint/nix/var/nix/profiles
+mkdir -m 1777 -p $mountPoint/nix/var/nix/profiles/per-user
+mkdir -m 0755 -p $mountPoint/nix/var/nix/profiles/per-user/root
+srcs=$(nix-env "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")
+if test -n "$srcs"; then
+    echo "copying NixOS/Nixpkgs sources..."
+    chroot $mountPoint @nix@/bin/nix-env \
+        "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/per-user/root/channels -i "$srcs" --quiet
+fi
+mkdir -m 0700 -p $mountPoint/root/.nix-defexpr
+ln -sfn /nix/var/nix/profiles/per-user/root/channels $mountPoint/root/.nix-defexpr/channels
+
+
+# We're done building/downloading, so we don't need the /etc bind
+# mount anymore.  In fact, below we want to modify the target's /etc.
+umount $mountPoint/etc/nixos
+umount $mountPoint/etc
+
+
+# Grub needs an mtab.
+ln -sfn /proc/mounts $mountPoint/etc/mtab
+
+
+# Mark the target as a NixOS installation, otherwise
+# switch-to-configuration will chicken out.
+touch $mountPoint/etc/NIXOS
+
+
+# Switch to the new system configuration.  This will install Grub with
+# a menu default pointing at the kernel/initrd/etc of the new
+# configuration.
+echo "finalising the installation..."
+NIXOS_INSTALL_GRUB=1 chroot $mountPoint \
+    /nix/var/nix/profiles/system/bin/switch-to-configuration boot
diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh
new file mode 100644
index 00000000000..7f008d62c24
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-option.sh
@@ -0,0 +1,395 @@
+#! @shell@ -e
+
+# Allow the location of NixOS sources and the system configuration
+# file to be overridden.
+
+: ${mountPoint=/mnt}
+: ${NIXOS_CONFIG=/etc/nixos/configuration.nix}
+export NIXOS_CONFIG
+
+usage () {
+  echo 1>&2 "
+Usage: $0 [-v] [-d] [-l] [--xml] OPTION_NAME
+       $0 --install
+
+This program allows you to inspect the current value of NixOS
+configuration options.  It can also generate a basic NixOS
+configuration file.
+
+Options:
+
+  -i | --install        Write a template NixOS configuration file to
+                        ${mountPoint:+$mountPoint/}$NIXOS_CONFIG.
+  -v | --value          Display the current value, based on your
+                        configuration.
+  -d | --description    Display the default value, the example and the
+                        description.
+  -l | --lookup         Display where the option is defined and where it
+                        is declared.
+  --xml                 Print an XML representation of the result.
+                        Implies -vdl options.
+  --help                Show this message.
+
+Environment variables affecting $0:
+
+  \$mountPoint          Path to the target file system.
+  \$NIXOS_CONFIG        Path to your configuration file.
+
+"
+
+  exit 1;
+}
+
+#####################
+# Process Arguments #
+#####################
+
+desc=false
+defs=false
+value=false
+xml=false
+install=false
+verbose=false
+
+option=""
+
+argfun=""
+for arg; do
+  if test -z "$argfun"; then
+    case $arg in
+      -*)
+        longarg=""
+        sarg="$arg"
+        while test "$sarg" != "-"; do
+          case $sarg in
+            --*) longarg=$arg; sarg="--";;
+            -d*) longarg="$longarg --description";;
+            -v*) longarg="$longarg --value";;
+            -l*) longarg="$longarg --lookup";;
+            -i*) longarg="$longarg --install";;
+            -*) usage;;
+          esac
+          # remove the first letter option
+          sarg="-${sarg#??}"
+        done
+        ;;
+      *) longarg=$arg;;
+    esac
+    for larg in $longarg; do
+      case $larg in
+        --description) desc=true;;
+        --value) value=true;;
+        --lookup) defs=true;;
+        --xml) xml=true;;
+        --install) install=true;;
+        --verbose) verbose=true;;
+        --help) usage;;
+        -*) usage;;
+        *) if test -z "$option"; then
+             option="$larg"
+           else
+             usage
+           fi;;
+      esac
+    done
+  else
+    case $argfun in
+      set_*)
+        var=$(echo $argfun | sed 's,^set_,,')
+        eval $var=$arg
+        ;;
+    esac
+    argfun=""
+  fi
+done
+
+if $xml; then
+  value=true
+  desc=true
+  defs=true
+fi
+
+# --install cannot be used with -d -v -l without option name.
+if $value || $desc || $defs && $install && test -z "$option"; then
+  usage
+fi
+
+generate=false
+if ! $defs && ! $desc && ! $value && $install && test -z "$option"; then
+  generate=true
+fi
+
+if ! $defs && ! $desc; then
+  value=true
+fi
+
+if $verbose; then
+  set -x
+else
+  set +x
+fi
+
+#############################
+# Process the configuration #
+#############################
+
+evalNix(){
+  nix-instantiate - --eval-only "$@"
+}
+
+evalAttr(){
+  local prefix=$1
+  local suffix=$2
+  local strict=$3
+  echo "(import <nixos> {}).$prefix${option:+.$option}${suffix:+.$suffix}" |
+    evalNix ${strict:+--strict}
+}
+
+evalOpt(){
+  evalAttr "eval.options" "$@"
+}
+
+evalCfg(){
+  evalAttr "config" "$@"
+}
+
+findSources(){
+  local suffix=$1
+  echo "builtins.map (f: f.source) (import <nixos> {}).eval.options${option:+.$option}.$suffix" |
+    evalNix --strict
+}
+
+# Given a result from nix-instantiate, recover the list of attributes it
+# contains.
+attrNames() {
+  local attributeset=$1
+  # sed is used to replace un-printable subset by 0s, and to remove most of
+  # the inner-attribute set, which reduce the likelyhood to encounter badly
+  # pre-processed input.
+  echo "builtins.attrNames $attributeset" | \
+    sed 's,<[A-Z]*>,0,g; :inner; s/{[^\{\}]*};/0;/g; t inner;' | \
+    evalNix --strict
+}
+
+# map a simple list which contains strings or paths.
+nixMap() {
+  local fun="$1"
+  local list="$2"
+  local elem
+  for elem in $list; do
+    test $elem = '[' -o $elem = ']' && continue;
+    $fun $elem
+  done
+}
+
+if $install; then
+  NIXOS_CONFIG="$mountPoint$NIXOS_CONFIG"
+fi
+
+if $generate; then
+  mkdir -p $(dirname "$NIXOS_CONFIG")
+
+  # Scan the hardware and add the result to /etc/nixos/hardware-scan.nix.
+  hardware_config="${NIXOS_CONFIG%/configuration.nix}/hardware-configuration.nix"
+  if test -e "$hardware_config"; then
+    echo "A hardware configuration file exists, generation skipped."
+  else
+    echo "Generating a hardware configuration file in $hardware_config..."
+    nixos-hardware-scan > "$hardware_config"
+  fi
+
+  if test -e "$NIXOS_CONFIG"; then
+    echo 1>&2 "error: Cannot generate a template configuration because a configuration file exists."
+    exit 1
+  fi
+
+  nl="
+"
+  if test -e /sys/firmware/efi/efivars; then
+    l1="  # Use the gummiboot efi boot loader."
+    l2="  boot.loader.grub.enable = false;"
+    l3="  boot.loader.gummiboot.enable = true;"
+    l4="  boot.loader.efi.canTouchEfiVariables = true;"
+    # !!! Remove me when nixos is on 3.10 or greater by default
+    l5="  # EFI booting requires kernel >= 3.10"
+    l6="  boot.kernelPackages = pkgs.linuxPackages_3_10;"
+    bootloader_config="$l1$nl$l2$nl$l3$nl$l4$nl$nl$l5$nl$l6"
+  else
+    l1="  # Use the Grub2 boot loader."
+    l2="  boot.loader.grub.enable = true;"
+    l3="  boot.loader.grub.version = 2;"
+    l4="  # Define on which hard drive you want to install Grub."
+    l5='  # boot.loader.grub.device = "/dev/sda";'
+    bootloader_config="$l1$nl$l2$nl$l3$nl$nl$l4$nl$l5"
+  fi
+
+  echo "Generating a basic configuration file in $NIXOS_CONFIG..."
+
+  # Generate a template configuration file where the user has to
+  # fill the gaps.
+  cat <<EOF > "$NIXOS_CONFIG"
+# Edit this configuration file to define what should be installed on
+# the system.  Help is available in the configuration.nix(5) man page
+# or the NixOS manual available on virtual console 8 (Alt+F8).
+
+{ config, pkgs, ... }:
+
+{
+  imports =
+    [ # Include the results of the hardware scan.
+      ./hardware-configuration.nix
+    ];
+
+  boot.initrd.kernelModules =
+    [ # Specify all kernel modules that are necessary for mounting the root
+      # filesystem.
+      # "xfs" "ata_piix"
+      # fbcon # Uncomment this when EFI booting to see the console before the root partition is mounted
+    ];
+    
+$bootloader_config
+
+  # networking.hostName = "nixos"; # Define your hostname.
+  # networking.wireless.enable = true;  # Enables Wireless.
+
+  # Add filesystem entries for each partition that you want to see
+  # mounted at boot time.  This should include at least the root
+  # filesystem.
+
+  # fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+  # fileSystems."/data" =     # where you want to mount the device
+  #   { device = "/dev/sdb";  # the device
+  #     fsType = "ext3";      # the type of the partition
+  #     options = "data=journal";
+  #   };
+
+  # List swap partitions activated at boot time.
+  swapDevices =
+    [ # { device = "/dev/disk/by-label/swap"; }
+    ];
+
+  # Select internationalisation properties.
+  # i18n = {
+  #   consoleFont = "lat9w-16";
+  #   consoleKeyMap = "us";
+  #   defaultLocale = "en_US.UTF-8";
+  # };
+
+  # List services that you want to enable:
+
+  # Enable the OpenSSH daemon.
+  # services.openssh.enable = true;
+
+  # Enable CUPS to print documents.
+  # services.printing.enable = true;
+
+  # Enable the X11 windowing system.
+  # services.xserver.enable = true;
+  # services.xserver.layout = "us";
+  # services.xserver.xkbOptions = "eurosign:e";
+
+  # Enable the KDE Desktop Environment.
+  # services.xserver.displayManager.kdm.enable = true;
+  # services.xserver.desktopManager.kde4.enable = true;
+}
+EOF
+
+  exit 0
+fi;
+
+# This duplicates the work made below, but it is useful for processing
+# the output of nixos-option with other tools such as nixos-gui.
+if $xml; then
+  evalNix --xml --no-location <<EOF
+let
+  reach = attrs: attrs${option:+.$option};
+  nixos = import <nixos> {};
+  nixpkgs = import <nixpkgs> {};
+  sources = builtins.map (f: f.source);
+  opt = reach nixos.eval.options;
+  cfg = reach nixos.config;
+in
+
+with nixpkgs.lib;
+
+let
+  optStrict = v:
+    let
+      traverse = x :
+        if isAttrs x then
+          if x ? outPath then true
+          else all id (mapAttrsFlatten (n: traverseNoAttrs) x)
+        else traverseNoAttrs x;
+      traverseNoAttrs = x:
+        # do not continue in attribute sets
+        if isAttrs x then true
+        else if isList x then all id (map traverse x)
+        else true;
+    in assert traverse v; v;
+in
+
+if isOption opt then
+  optStrict ({}
+  // optionalAttrs (opt ? default) { inherit (opt) default; }
+  // optionalAttrs (opt ? example) { inherit (opt) example; }
+  // optionalAttrs (opt ? description) { inherit (opt) description; }
+  // optionalAttrs (opt ? type) { typename = opt.type.name; }
+  // optionalAttrs (opt ? options) { inherit (opt) options; }
+  // {
+    # to disambiguate the xml output.
+    _isOption = true;
+    declarations = sources opt.declarations;
+    definitions = sources opt.definitions;
+    value = cfg;
+  })
+else
+  opt
+EOF
+  exit $?
+fi
+
+if test "$(evalOpt "_type" 2> /dev/null)" = '"option"'; then
+  $value && evalCfg;
+
+  if $desc; then
+    $value && echo;
+
+    if default=$(evalOpt "default" - 2> /dev/null); then
+      echo "Default: $default"
+    else
+      echo "Default: <None>"
+    fi
+    if example=$(evalOpt "example" - 2> /dev/null); then
+      echo "Example: $example"
+    fi
+    echo "Description:"
+    eval printf $(evalOpt "description")
+  fi
+
+  if $defs; then
+    $desc || $value && echo;
+
+    printPath () { echo "  $1"; }
+
+    echo "Declared by:"
+    nixMap printPath "$(findSources "declarations")"
+    echo ""
+    echo "Defined by:"
+    nixMap printPath "$(findSources "definitions")"
+    echo ""
+  fi
+
+else
+  # echo 1>&2 "Warning: This value is not an option."
+
+  result=$(evalCfg)
+  if names=$(attrNames "$result" 2> /dev/null); then
+    echo 1>&2 "This attribute set contains:"
+    escapeQuotes () { eval echo "$1"; }
+    nixMap escapeQuotes "$names"
+  else
+    echo 1>&2 "An error occured 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
new file mode 100644
index 00000000000..8734cb273d4
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -0,0 +1,222 @@
+#! @shell@ -e
+
+showSyntax() {
+    # !!! more or less cut&paste from
+    # system/switch-to-configuration.sh (which we call, of course).
+    cat <<EOF
+Usage: $0 [OPTIONS...] OPERATION
+
+The operation is one of the following:
+
+  switch:   make the configuration the boot default and activate now
+  boot:     make the configuration the boot default
+  test:     activate the configuration, but don't make it the boot default
+  build:    build the configuration, but don't make it the default or
+            activate it
+  build-vm: build a virtual machine containing the configuration
+            (useful for testing)
+  build-vm-with-bootloader:
+            like build-vm, but include a boot loader in the VM
+  dry-run:  just show what store paths would be built/downloaded
+
+Options:
+
+  --upgrade              fetch the latest version of NixOS before rebuilding
+  --install-grub         (re-)install the Grub bootloader
+  --no-build-nix         don't build the latest Nix from Nixpkgs before
+                           building NixOS
+  --rollback             restore the previous NixOS configuration (only
+                           with switch, boot, test, build)
+  --profile-name / -p    install in the specified system profile
+  --fast                 same as --no-build-nix --show-trace
+
+Various nix-build options are also accepted, in particular:
+
+  --show-trace           show a detailed stack trace for evaluation errors
+
+Environment variables affecting nixos-rebuild:
+
+  \$NIX_PATH              Nix expression search path
+  \$NIXOS_CONFIG          path to the NixOS system configuration specification
+EOF
+    exit 1
+}
+
+
+# Parse the command line.
+extraBuildFlags=()
+action=
+buildNix=1
+rollback=
+upgrade=
+repair=
+profile=/nix/var/nix/profiles/system
+
+while [ "$#" -gt 0 ]; do
+    i="$1"; shift 1
+    case "$i" in
+      --help)
+        showSyntax
+        ;;
+      switch|boot|test|build|dry-run|build-vm|build-vm-with-bootloader)
+        action="$i"
+        ;;
+      --install-grub)
+        export NIXOS_INSTALL_GRUB=1
+        ;;
+      --no-build-nix)
+        buildNix=
+        ;;
+      --rollback)
+        rollback=1
+        ;;
+      --upgrade)
+        upgrade=1
+        ;;
+      --repair)
+        repair=1
+        extraBuildFlags+=("$i")
+        ;;
+      --show-trace|--no-build-hook|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair)
+        extraBuildFlags+=("$i")
+        ;;
+      --max-jobs|-j|--cores|-I)
+        j="$1"; shift 1
+        extraBuildFlags+=("$i" "$j")
+        ;;
+      --option)
+        j="$1"; shift 1
+        k="$1"; shift 1
+        extraBuildFlags+=("$i" "$j" "$k")
+        ;;
+      --fast)
+        buildNix=
+        extraBuildFlags+=(--show-trace)
+        ;;
+      --profile-name|-p)
+        if [ -z "$1" ]; then
+            echo "$0: ‘--profile-name’ requires an argument"
+            exit 1
+        fi
+        if [ "$1" != system ]; then
+            profile="/nix/var/nix/profiles/system-profiles/$1"
+            mkdir -p -m 0755 "$(dirname "$profile")"
+        fi
+        shift 1
+        ;;
+      *)
+        echo "$0: unknown option \`$i'"
+        exit 1
+        ;;
+    esac
+done
+
+if [ -z "$action" ]; then showSyntax; fi
+
+if [ -n "$rollback" ]; then
+    buildNix=
+fi
+
+
+tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX)
+trap 'rm -rf "$tmpDir"' EXIT
+
+
+# If the Nix daemon is running, then use it.  This allows us to use
+# the latest Nix from Nixpkgs (below) for expression evaluation, while
+# still using the old Nix (via the daemon) for actual store access.
+# This matters if the new Nix in Nixpkgs has a schema change.  It
+# would upgrade the schema, which should only happen once we actually
+# switch to the new configuration.
+# If --repair is given, don't try to use the Nix daemon, because the
+# flag can only be used directly.
+if [ -z "$repair" ] && systemctl show nix-daemon.socket nix-daemon.service | grep -q ActiveState=active; then
+    export NIX_REMOTE=${NIX_REMOTE:-daemon}
+fi
+
+
+# If ‘--upgrade’ is given, run ‘nix-channel --update nixos’.
+if [ -n "$upgrade" ]; then
+    nix-channel --update nixos
+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
+    echo "building Nix..." >&2
+    if ! nix-build '<nixos>' -A config.environment.nix -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then
+        if ! nix-build '<nixos>' -A nixFallback -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then
+            nix-build '<nixpkgs>' -A nixUnstable -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null
+        fi
+    fi
+    PATH=$tmpDir/nix/bin:$PATH
+fi
+
+
+# Update the version suffix if we're building from Git (so that
+# nixos-version shows something useful).
+if nixos=$(nix-instantiate --find-file nixos "${extraBuildFlags[@]}"); then
+    suffix=$(@shell@ $nixos/modules/installer/tools/get-version-suffix "${extraBuildFlags[@]}")
+    if [ -n "$suffix" ]; then
+        echo -n "$suffix" > "$nixos/.version-suffix" || true
+    fi
+fi
+
+
+if [ "$action" = dry-run ]; then
+    extraBuildFlags+=(--dry-run)
+fi
+
+
+# Either upgrade the configuration in the system profile (for "switch"
+# or "boot"), or just build it and create a symlink "result" in the
+# current directory (for "build" and "test").
+if [ -z "$rollback" ]; then
+    echo "building the system configuration..." >&2
+    if [ "$action" = switch -o "$action" = boot ]; then
+        nix-env "${extraBuildFlags[@]}" -p "$profile" -f '<nixos>' --set -A system
+        pathToConfig="$profile"
+    elif [ "$action" = test -o "$action" = build -o "$action" = dry-run ]; then
+        nix-build '<nixos>' -A system -K -k "${extraBuildFlags[@]}" > /dev/null
+        pathToConfig=./result
+    elif [ "$action" = build-vm ]; then
+        nix-build '<nixos>' -A vm -K -k "${extraBuildFlags[@]}" > /dev/null
+        pathToConfig=./result
+    elif [ "$action" = build-vm-with-bootloader ]; then
+        nix-build '<nixos>' -A vmWithBootLoader -K -k "${extraBuildFlags[@]}" > /dev/null
+        pathToConfig=./result
+    else
+        showSyntax
+    fi
+else # [ -n "$rollback" ]
+    if [ "$action" = switch -o "$action" = boot ]; then
+        nix-env --rollback -p "$profile"
+        pathToConfig="$profile"
+    elif [ "$action" = test -o "$action" = build ]; then
+        systemNumber=$(
+            nix-env -p "$profile" --list-generations |
+            sed -n '/current/ {g; p;}; s/ *\([0-9]*\).*/\1/; h'
+        )
+        ln -sT "$profile"-${systemNumber}-link ./result
+        pathToConfig=./result
+    else
+        showSyntax
+    fi
+fi
+
+
+# If we're not just building, then make the new configuration the boot
+# default and/or activate it now.
+if [ "$action" = switch -o "$action" = boot -o "$action" = test ]; then
+    $pathToConfig/bin/switch-to-configuration "$action"
+fi
+
+
+if [ "$action" = build-vm ]; then
+    cat >&2 <<EOF
+
+Done.  The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm).
+EOF
+fi
diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh
new file mode 100644
index 00000000000..5dbf277fe4c
--- /dev/null
+++ b/nixos/modules/installer/tools/nixos-version.sh
@@ -0,0 +1,2 @@
+#! @shell@
+echo "@nixosVersion@ (@nixosCodeName@)"
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
new file mode 100644
index 00000000000..c761d74a890
--- /dev/null
+++ b/nixos/modules/installer/tools/tools.nix
@@ -0,0 +1,112 @@
+# This module generates nixos-install, nixos-rebuild,
+# nixos-hardware-scan, etc.
+
+{ config, pkgs, modulesPath, ... }:
+
+let
+  ### implementation
+  cfg = config.installer;
+
+  makeProg = args: pkgs.substituteAll (args // {
+    dir = "bin";
+    isExecutable = true;
+  });
+
+  nixosBuildVMS = makeProg {
+    name = "nixos-build-vms";
+    src = ./nixos-build-vms/nixos-build-vms.sh;
+  };
+
+  nixosInstall = makeProg {
+    name = "nixos-install";
+    src = ./nixos-install.sh;
+
+    inherit (pkgs) perl pathsFromGraph;
+    nix = config.environment.nix;
+
+    nixClosure = pkgs.runCommand "closure"
+      { exportReferencesGraph = ["refs" config.environment.nix]; }
+      "cp refs $out";
+  };
+
+  nixosRebuild = makeProg {
+    name = "nixos-rebuild";
+    src = ./nixos-rebuild.sh;
+  };
+
+  /*
+  nixosGenSeccureKeys = makeProg {
+    name = "nixos-gen-seccure-keys";
+    src = ./nixos-gen-seccure-keys.sh;
+  };
+  */
+
+  nixosHardwareScan = makeProg {
+    name = "nixos-hardware-scan";
+    src = ./nixos-hardware-scan.pl;
+    inherit (pkgs) perl dmidecode;
+  };
+
+  nixosOption = makeProg {
+    name = "nixos-option";
+    src = ./nixos-option.sh;
+  };
+
+  nixosVersion = makeProg {
+    name = "nixos-version";
+    src = ./nixos-version.sh;
+    inherit (config.system) nixosVersion nixosCodeName;
+  };
+
+  nixosGui = pkgs.xulrunnerWrapper {
+    launcher = "nixos-gui";
+    application = pkgs.stdenv.mkDerivation {
+      name = "nixos-gui";
+      buildCommand = ''
+        cp -r "$gui" "$out"
+
+        # Do not force the copy if the file exists in the sources (this
+        # happens for developpers)
+        test -e "$out/chrome/content/jquery-1.5.2.js" ||
+          cp -f "$jquery" "$out/chrome/content/jquery-1.5.2.js"
+      '';
+      gui = pkgs.lib.cleanSource "${modulesPath}/../gui";
+      jquery = pkgs.fetchurl {
+        url = http://code.jquery.com/jquery-1.5.2.min.js;
+        sha256 = "8f0a19ee8c606b35a10904951e0a27da1896eafe33c6e88cb7bcbe455f05a24a";
+      };
+    };
+  };
+
+in
+
+{
+  options = {
+
+    installer.enableGraphicalTools = pkgs.lib.mkOption {
+      default = false;
+      type = with pkgs.lib.types; bool;
+      example = true;
+      description = ''
+        Enable the installation of graphical tools.
+      '';
+    };
+
+  };
+
+  config = {
+    environment.systemPackages =
+      [ nixosBuildVMS
+        nixosInstall
+        nixosRebuild
+        nixosHardwareScan
+        #nixosGenSeccureKeys
+        nixosOption
+        nixosVersion
+      ] ++ pkgs.lib.optional cfg.enableGraphicalTools nixosGui;
+
+    system.build = {
+      inherit nixosInstall nixosHardwareScan nixosOption;
+    };
+  };
+}
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
new file mode 100644
index 00000000000..76cc29a1fac
--- /dev/null
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -0,0 +1,19 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  imports =
+    [ ../virtualisation/virtualbox-image.nix
+      ../installer/cd-dvd/channel.nix
+      ../profiles/demo.nix
+      ../profiles/clone-config.nix
+    ];
+
+  # Allow mounting of shared folders.
+  users.extraUsers.demo.extraGroups = [ "vboxsf" ];
+
+  # 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" ];
+}
diff --git a/nixos/modules/misc/assertions.nix b/nixos/modules/misc/assertions.nix
new file mode 100644
index 00000000000..9cd58550adc
--- /dev/null
+++ b/nixos/modules/misc/assertions.nix
@@ -0,0 +1,37 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
+
+in
+
+{
+
+  options = {
+
+    assertions = mkOption {
+      default = [];
+      example = [ { assertion = false; message = "you can't enable this for that reason"; } ];
+      merge = pkgs.lib.mergeListOption;
+      description = ''
+        This option allows modules to express conditions that must
+        hold for the evaluation of the system configuration to
+        succeed, along with associated error messages for the user.
+      '';
+    };
+
+  };
+
+  config = {
+
+    # This option is evaluated always. Thus the assertions are checked as well. hacky!
+    environment.systemPackages =
+      if [] == failed then []
+      else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}";
+
+  };
+
+}
diff --git a/nixos/modules/misc/check-config.nix b/nixos/modules/misc/check-config.nix
new file mode 100644
index 00000000000..28f36ad9ae5
--- /dev/null
+++ b/nixos/modules/misc/check-config.nix
@@ -0,0 +1,13 @@
+{pkgs, ...}:
+
+{
+  options = {
+    environment.checkConfigurationOptions = pkgs.lib.mkOption {
+      default = true;
+      example = false;
+      description = ''
+        Whether to check the validity of the entire configuration.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/misc/crashdump.nix b/nixos/modules/misc/crashdump.nix
new file mode 100644
index 00000000000..6e6bc9dec0f
--- /dev/null
+++ b/nixos/modules/misc/crashdump.nix
@@ -0,0 +1,77 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+  crashdump = config.boot.crashDump;
+
+  kernelParams = concatStringsSep " " crashdump.kernelParams;
+
+in
+###### interface
+{
+  options = {
+    boot = {
+      crashDump = {
+        enable = mkOption {
+          default = false;
+          example = true;
+          description = ''
+            If enabled, NixOS will set up a kernel that will
+            boot on crash, and leave the user to a stage1 debug1devices
+            interactive shell to be able to save the crashed kernel dump.
+            It also activates the NMI watchdog.
+          '';
+        };
+        kernelPackages = mkOption {
+          default = pkgs.linuxPackages;
+          # We don't want to evaluate all of linuxPackages for the manual
+          # - some of it might not even evaluate correctly.
+          defaultText = "pkgs.linuxPackages";
+          example = "pkgs.linuxPackages_2_6_25";
+          description = ''
+            This will override the boot.kernelPackages, and will add some
+            kernel configuration parameters for the crash dump to work.
+          '';
+        };
+        kernelParams = mkOption {
+          default = [ "debug1devices" ];
+          description = ''
+            Parameters that will be passed to the kernel kexec-ed on crash.
+          '';
+        };
+      };
+    };
+  };
+
+###### implementation
+
+  config = mkIf crashdump.enable {
+    boot = {
+      postBootCommands = ''
+        ${pkgs.kexectools}/sbin/kexec -p /run/current-system/kernel \
+        --initrd=/run/current-system/initrd \
+        --append="init=$(readlink -f /run/current-system/init) system=$(readlink -f /run/current-system) irqpoll maxcpus=1 reset_devices ${kernelParams}" --reset-vga --console-vga
+      '';
+      kernelParams = [
+       "crashkernel=64M"
+       "nmi_watchdog=panic"
+       "softlockup_panic=1"
+       "idle=poll"
+      ];
+      kernelPackages = mkOverride 50 (crashdump.kernelPackages // {
+        kernel = crashdump.kernelPackages.kernel.override 
+          (attrs: {
+            extraConfig = (optionalString (attrs ? extraConfig) attrs.extraConfig) +
+              ''
+                CRASH_DUMP y
+                DEBUG_INFO y
+                PROC_VMCORE y
+                LOCKUP_DETECTOR y
+                HARDLOCKUP_DETECTOR y
+              '';
+          });
+      });
+    };
+  };
+}
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
new file mode 100644
index 00000000000..adaa2b0d9ae
--- /dev/null
+++ b/nixos/modules/misc/ids.nix
@@ -0,0 +1,201 @@
+# This module defines the global list of uids and gids.  We keep a
+# central list to prevent id collisions.
+
+{ config, pkgs, ... }:
+
+{
+  options = {
+
+    ids.uids = pkgs.lib.mkOption {
+      description = ''
+        The user IDs used in NixOS.
+      '';
+    };
+
+    ids.gids = pkgs.lib.mkOption {
+      description = ''
+        The group IDs used in NixOS.
+      '';
+    };
+
+  };
+
+
+  config = {
+
+    ids.uids = {
+      root = 0;
+      nscd = 1;
+      sshd = 2;
+      ntp = 3;
+      messagebus = 4; # D-Bus
+      haldaemon = 5;
+      nagios = 6;
+      vsftpd = 7;
+      ftp = 8;
+      bitlbee = 9;
+      avahi = 10;
+      atd = 12;
+      zabbix = 13;
+      postfix = 14;
+      dovecot = 15;
+      tomcat = 16;
+      pulseaudio = 22; # must match `pulseaudio' GID
+      gpsd = 23;
+      polkituser = 28;
+      uptimed = 29;
+      ddclient = 30;
+      davfs2 = 31;
+      privoxy = 32;
+      osgi = 34;
+      tor = 35;
+      cups = 36;
+      foldingAtHome = 37;
+      sabnzbd = 38;
+      kdm = 39;
+      ghostOne = 40;
+      git = 41;
+      fourStore = 42;
+      fourStoreEndpoint = 43;
+      virtuoso = 44;
+      rtkit = 45;
+      dovecot2 = 46;
+      dovenull2 = 47;
+      unbound = 48;
+      prayer = 49;
+      mpd = 50;
+      clamav = 51;
+      fprot = 52;
+      bind = 53;
+      wwwrun = 54;
+      spamd = 56;
+      nslcd = 58;
+      nginx = 60;
+      chrony = 61;
+      smtpd = 63;
+      smtpq = 64;
+      supybot = 65;
+      iodined = 66;
+      graphite = 68;
+      statsd = 69;
+      transmission = 70;
+      postgres = 71;
+      smbguest = 74;
+      varnish = 75;
+      dd-agent = 76;
+      lighttpd = 77;
+      lightdm = 78;
+      freenet = 79;
+      ircd = 80;
+      bacula = 81;
+      almir = 82;
+      deluge = 83;
+      mysql = 84;
+      rabbitmq = 85;
+      activemq = 86;
+      gnunet = 87;
+      oidentd = 88;
+      quassel = 89;
+      amule = 90;
+      minidlna = 91;
+      elasticsearch = 92;
+      tcpcryptd = 93; # tcpcryptd uses a hard-coded uid. We patch it in Nixpkgs to match this choice.
+      zope2 = 94;
+      firebird = 95;
+
+      # When adding a uid, make sure it doesn't match an existing gid.
+
+      nixbld = 30000; # start of range of uids
+      nobody = 65534;
+    };
+
+    ids.gids = {
+      root = 0;
+      wheel = 1;
+      kmem = 2;
+      tty = 3;
+      messagebus = 4; # D-Bus
+      haldaemon = 5;
+      disk = 6;
+      vsftpd = 7;
+      ftp = 8;
+      bitlbee = 9;
+      avahi = 10;
+      atd = 12;
+      postfix = 13;
+      postdrop = 14;
+      dovecot = 15;
+      audio = 17;
+      floppy = 18;
+      uucp = 19;
+      lp = 20;
+      tomcat = 21;
+      pulseaudio = 22; # must match `pulseaudio' UID
+      gpsd = 23;
+      cdrom = 24;
+      tape = 25;
+      video = 26;
+      dialout = 27;
+      polkituser = 28;
+      utmp = 29;
+      davfs2 = 31;
+      privoxy = 32;
+      disnix = 33;
+      osgi = 34;
+      ghostOne = 40;
+      git = 41;
+      fourStore = 42;
+      fourStoreEndpoint = 43;
+      virtuoso = 44;
+      dovecot2 = 46;
+      prayer = 49;
+      mpd = 50;
+      clamav = 51;
+      fprot = 52;
+      wwwrun = 54;
+      adm = 55;
+      spamd = 56;
+      networkmanager = 57;
+      nslcd = 58;
+      scanner = 59;
+      nginx = 60;
+      systemd-journal = 62;
+      smtpd = 63;
+      smtpq = 64;
+      supybot = 65;
+      iodined = 66;
+      libvirtd = 67;
+      graphite = 68;
+      transmission = 70;
+      postgres = 71;
+      vboxusers = 72;
+      vboxsf = 73;
+      smbguest = 74;
+      varnish = 75;
+      dd-agent = 76;
+      lighttpd = 77;
+      lightdm = 78;
+      freenet = 79;
+      ircd = 80;
+      bacula = 81;
+      almir = 82;
+      deluge = 83;
+      mysql = 84;
+      rabbitmq = 85;
+      activemq = 86;
+      gnunet = 87;
+      oidentd = 88;
+      quassel = 89;
+      amule = 90;
+      minidlna = 91;
+
+      # When adding a gid, make sure it doesn't match an existing uid.
+
+      users = 100;
+      nixbld = 30000;
+      nogroup = 65534;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/misc/lib.nix b/nixos/modules/misc/lib.nix
new file mode 100644
index 00000000000..18fc68a6988
--- /dev/null
+++ b/nixos/modules/misc/lib.nix
@@ -0,0 +1,15 @@
+{ config, pkgs, ... }:
+
+{
+  options = {
+    lib = pkgs.lib.mkOption {
+      default = {};
+
+      type = pkgs.lib.types.attrsOf pkgs.lib.types.attrs;
+
+      description = ''
+        This option allows modules to define helper functions, constants, etc.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
new file mode 100644
index 00000000000..02b1ed7b63d
--- /dev/null
+++ b/nixos/modules/misc/locate.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  locatedb = "/var/cache/locatedb";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.locate = {
+
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          If enabled, NixOS will periodically update the database of
+          files used by the <command>locate</command> command.
+        '';
+      };
+
+      period = mkOption {
+        default = "15 02 * * *";
+        description = ''
+          This option defines (in the format used by cron) when the
+          locate database is updated.
+          The default is to update at 02:15 (at night) every day.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = {
+
+    systemd.services.update-locatedb =
+      { description = "Update Locate Database";
+        path  = [ pkgs.su ];
+        script =
+          ''
+            mkdir -m 0755 -p $(dirname ${locatedb})
+            exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /media /run'
+          '';
+        serviceConfig.Nice = 19;
+        serviceConfig.IOSchedulingClass = "idle";
+      };
+
+    services.cron.systemCronJobs = optional config.services.locate.enable
+      "${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service";
+
+  };
+
+}
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
new file mode 100644
index 00000000000..0df0e57c98e
--- /dev/null
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -0,0 +1,87 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  isConfig = x:
+    builtins.isAttrs x || builtins.isFunction x;
+
+  optCall = f: x:
+    if builtins.isFunction f
+    then f x
+    else f;
+
+  mergeConfig = lhs_: rhs_:
+    let
+      lhs = optCall lhs_ { inherit pkgs; };
+      rhs = optCall rhs_ { inherit pkgs; };
+    in
+    lhs // rhs //
+    optionalAttrs (lhs ? packageOverrides) {
+      packageOverrides = pkgs:
+        optCall lhs.packageOverrides pkgs //
+        optCall (attrByPath ["packageOverrides"] ({}) rhs) pkgs;
+    };
+
+  configType = mkOptionType {
+    name = "nixpkgs config";
+    check = traceValIfNot isConfig;
+    merge = fold mergeConfig {};
+  };
+
+in
+
+{
+  options = {
+
+    nixpkgs.config = mkOption {
+      default = {};
+      example = literalExample
+        ''
+          { firefox.enableGeckoMediaPlayer = true;
+            packageOverrides = pkgs: {
+              firefox60Pkgs = pkgs.firefox60Pkgs.override {
+                enableOfficialBranding = true;
+              };
+            };
+          }
+        '';
+      type = configType;
+      description = ''
+        The configuration of the Nix Packages collection.  (For
+        details, see the Nixpkgs documentation.)  It allows you to set
+        package configuration options, and to override packages
+        globally through the <varname>packageOverrides</varname>
+        option.  The latter is a function that takes as an argument
+        the <emphasis>original</emphasis> Nixpkgs, and must evaluate
+        to a set of new or overridden packages.
+      '';
+    };
+
+    nixpkgs.system = mkOption {
+      default = pkgs.stdenv.system;
+      description = ''
+        Specifies the Nix platform type for which NixOS should be built.
+        If unset, it defaults to the platform type of your host system
+        (<literal>${builtins.currentSystem}</literal>).
+        Specifying this option is useful when doing distributed
+        multi-platform deployment, or when building virtual machines.
+      '';
+    };
+
+  };
+
+  config = {
+
+    # FIXME
+    nixpkgs.config.packageOverrides = pkgs: {
+      #udev = pkgs.systemd;
+      slim = pkgs.slim.override (args: if args ? consolekit then { consolekit = null; } else { });
+      lvm2 = pkgs.lvm2.override { udev = pkgs.systemd; };
+      upower = pkgs.upower.override { useSystemd = true; };
+      polkit = pkgs.polkit.override { useSystemd = true; };
+      consolekit = null;
+    };
+
+  };
+}
diff --git a/nixos/modules/misc/passthru.nix b/nixos/modules/misc/passthru.nix
new file mode 100644
index 00000000000..f68adc5e843
--- /dev/null
+++ b/nixos/modules/misc/passthru.nix
@@ -0,0 +1,15 @@
+# This module allows you to export something from configuration
+# Use case: export kernel source expression for ease of configuring
+
+{ config, pkgs, ... }:
+
+{
+  options = {
+    passthru = pkgs.lib.mkOption {
+      description = ''
+        This attribute set will be exported as a system attribute.
+        You can put whatever you want here.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
new file mode 100644
index 00000000000..20a03b44a2a
--- /dev/null
+++ b/nixos/modules/misc/version.nix
@@ -0,0 +1,55 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    system.nixosVersion = mkOption {
+      type = types.uniq types.string;
+      description = "NixOS version.";
+    };
+
+    system.nixosVersionSuffix = mkOption {
+      type = types.uniq types.string;
+      description = "NixOS version suffix.";
+    };
+
+    system.nixosCodeName = mkOption {
+      type = types.uniq types.string;
+      description = "NixOS release code name.";
+    };
+
+  };
+
+  config = {
+
+    system.nixosVersion =
+      mkDefault (builtins.readFile ../../.version + config.system.nixosVersionSuffix);
+
+    system.nixosVersionSuffix =
+      mkDefault (if builtins.pathExists ../../.version-suffix then builtins.readFile ../../.version-suffix else "pre-git");
+
+    # Note: code names must only increase in alphabetical order.
+    system.nixosCodeName = "Aardvark";
+
+    # Generate /etc/os-release.  See
+    # http://0pointer.de/public/systemd-man/os-release.html for the
+    # format.
+    environment.etc = singleton
+      { source = pkgs.writeText "os-release"
+          ''
+            NAME=NixOS
+            ID=nixos
+            VERSION="${config.system.nixosVersion} (${config.system.nixosCodeName})"
+            VERSION_ID="${config.system.nixosVersion}"
+            PRETTY_NAME="NixOS ${config.system.nixosVersion} (${config.system.nixosCodeName})"
+            HOME_URL="http://nixos.org/"
+          '';
+        target = "os-release";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
new file mode 100644
index 00000000000..c186968f059
--- /dev/null
+++ b/nixos/modules/module-list.nix
@@ -0,0 +1,278 @@
+[
+  ./config/fonts/corefonts.nix
+  ./config/fonts/fontconfig.nix
+  ./config/fonts/fontdir.nix
+  ./config/fonts/fonts.nix
+  ./config/fonts/ghostscript.nix
+  ./config/gnu.nix
+  ./config/i18n.nix
+  ./config/krb5.nix
+  ./config/ldap.nix
+  ./config/networking.nix
+  ./config/no-x-libs.nix
+  ./config/nsswitch.nix
+  ./config/power-management.nix
+  ./config/pulseaudio.nix
+  ./config/shells-environment.nix
+  ./config/swap.nix
+  ./config/sysctl.nix
+  ./config/system-path.nix
+  ./config/timezone.nix
+  ./config/unix-odbc-drivers.nix
+  ./config/users-groups.nix
+  ./hardware/all-firmware.nix
+  ./hardware/cpu/intel-microcode.nix
+  ./hardware/cpu/amd-microcode.nix
+  ./hardware/network/b43.nix
+  ./hardware/network/intel-2100bg.nix
+  ./hardware/network/intel-2200bg.nix
+  ./hardware/network/intel-3945abg.nix
+  ./hardware/network/ralink.nix
+  ./hardware/network/rtl8192c.nix
+  ./hardware/pcmcia.nix
+  ./installer/tools/nixos-checkout.nix
+  ./installer/tools/tools.nix
+  ./misc/assertions.nix
+  ./misc/check-config.nix
+  ./misc/crashdump.nix
+  ./misc/ids.nix
+  ./misc/lib.nix
+  ./misc/locate.nix
+  ./misc/nixpkgs.nix
+  ./misc/passthru.nix
+  ./misc/version.nix
+  ./programs/atop.nix
+  ./programs/bash/bash.nix
+  ./programs/bash/command-not-found.nix
+  ./programs/blcr.nix
+  ./programs/environment.nix
+  ./programs/info.nix
+  ./programs/shadow.nix
+  ./programs/shell.nix
+  ./programs/ssh.nix
+  ./programs/ssmtp.nix
+  ./programs/venus.nix
+  ./programs/wvdial.nix
+  ./programs/zsh/zsh.nix
+  ./rename.nix
+  ./security/apparmor.nix
+  ./security/apparmor-suid.nix
+  ./security/ca.nix
+  ./security/pam.nix
+  ./security/pam_usb.nix
+  ./security/polkit.nix
+  ./security/rngd.nix
+  ./security/rtkit.nix
+  ./security/setuid-wrappers.nix
+  ./security/sudo.nix
+  ./services/amqp/activemq/default.nix
+  ./services/amqp/rabbitmq.nix
+  ./services/audio/alsa.nix
+  ./services/audio/fuppes.nix
+  ./services/audio/mpd.nix
+  ./services/backup/almir.nix
+  ./services/backup/bacula.nix
+  ./services/backup/mysql-backup.nix
+  ./services/backup/postgresql-backup.nix
+  ./services/backup/sitecopy-backup.nix
+  ./services/backup/rsnapshot.nix
+  ./services/databases/4store-endpoint.nix
+  ./services/databases/4store.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/games/ghost-one.nix
+  ./services/hardware/acpid.nix
+  ./services/hardware/bluetooth.nix
+  ./services/hardware/nvidia-optimus.nix
+  ./services/hardware/pcscd.nix
+  ./services/hardware/pommed.nix
+  ./services/hardware/sane.nix
+  ./services/hardware/udev.nix
+  ./services/hardware/udisks.nix
+  ./services/hardware/udisks2.nix
+  ./services/hardware/upower.nix
+  ./services/hardware/thinkfan.nix
+  ./services/logging/klogd.nix
+  ./services/logging/logcheck.nix
+  ./services/logging/logrotate.nix
+  ./services/logging/logstash.nix
+  ./services/logging/syslogd.nix
+  ./services/logging/rsyslogd.nix
+  ./services/mail/dovecot.nix
+  ./services/mail/freepops.nix
+  ./services/mail/mail.nix
+  ./services/mail/opensmtpd.nix
+  ./services/mail/postfix.nix
+  ./services/mail/spamassassin.nix
+  ./services/misc/autofs.nix
+  ./services/misc/cgminer.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/nixos-manual.nix
+  ./services/misc/rogue.nix
+  ./services/misc/svnserve.nix
+  ./services/misc/synergy.nix
+  ./services/monitoring/apcupsd.nix
+  ./services/monitoring/dd-agent.nix
+  ./services/monitoring/graphite.nix
+  ./services/monitoring/monit.nix
+  ./services/monitoring/nagios/default.nix
+  ./services/monitoring/smartd.nix
+  ./services/monitoring/statsd.nix
+  ./services/monitoring/systemhealth.nix
+  ./services/monitoring/ups.nix
+  ./services/monitoring/uptime.nix
+  ./services/monitoring/zabbix-agent.nix
+  ./services/monitoring/zabbix-server.nix
+  ./services/network-filesystems/drbd.nix
+  ./services/network-filesystems/nfsd.nix
+  ./services/network-filesystems/openafs-client/default.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/cntlm.nix
+  ./services/networking/chrony.nix
+  ./services/networking/ddclient.nix
+  #./services/networking/dhclient.nix
+  ./services/networking/dhcpcd.nix
+  ./services/networking/dhcpd.nix
+  ./services/networking/dnsmasq.nix
+  ./services/networking/ejabberd.nix
+  ./services/networking/firewall.nix
+  ./services/networking/tcpcrypt.nix
+  ./services/networking/flashpolicyd.nix
+  ./services/networking/freenet.nix
+  ./services/networking/git-daemon.nix
+  ./services/networking/gnunet.nix
+  ./services/networking/gogoclient.nix
+  ./services/networking/gvpe.nix
+  ./services/networking/hostapd.nix
+  ./services/networking/ifplugd.nix
+  ./services/networking/iodined.nix
+  ./services/networking/ircd-hybrid/default.nix
+  ./services/networking/minidlna.nix
+  ./services/networking/nat.nix
+  ./services/networking/networkmanager.nix
+  ./services/networking/ntpd.nix
+  ./services/networking/oidentd.nix
+  ./services/networking/openfire.nix
+  ./services/networking/openvpn.nix
+  ./services/networking/prayer.nix
+  ./services/networking/privoxy.nix
+  ./services/networking/quassel.nix
+  ./services/networking/radvd.nix
+  ./services/networking/rdnssd.nix
+  ./services/networking/rpcbind.nix
+  ./services/networking/sabnzbd.nix
+  ./services/networking/supybot.nix
+  ./services/networking/ssh/lshd.nix
+  ./services/networking/ssh/sshd.nix
+  ./services/networking/tftpd.nix
+  ./services/networking/unbound.nix
+  ./services/networking/vsftpd.nix
+  ./services/networking/wakeonlan.nix
+  ./services/networking/websockify.nix
+  ./services/networking/wicd.nix
+  ./services/networking/wpa_supplicant.nix
+  ./services/networking/xinetd.nix
+  ./services/printing/cupsd.nix
+  ./services/scheduling/atd.nix
+  ./services/scheduling/cron.nix
+  ./services/scheduling/fcron.nix
+  ./services/search/elasticsearch.nix
+  ./services/security/clamav.nix
+  ./services/security/fprot.nix
+  ./services/security/frandom.nix
+  ./services/security/tor.nix
+  ./services/security/torify.nix
+  ./services/security/torsocks.nix
+  ./services/system/dbus.nix
+  ./services/system/kerberos.nix
+  ./services/system/nscd.nix
+  ./services/system/uptimed.nix
+  ./services/torrent/deluge.nix
+  ./services/torrent/transmission.nix
+  ./services/ttys/gpm.nix
+  ./services/ttys/agetty.nix
+  ./services/web-servers/apache-httpd/default.nix
+  ./services/web-servers/jboss/default.nix
+  ./services/web-servers/lighttpd/default.nix
+  ./services/web-servers/lighttpd/cgit.nix
+  ./services/web-servers/lighttpd/gitweb.nix
+  ./services/web-servers/nginx/default.nix
+  ./services/web-servers/tomcat.nix
+  ./services/web-servers/varnish/default.nix
+  ./services/web-servers/zope2.nix
+  ./services/x11/desktop-managers/default.nix
+  ./services/x11/display-managers/auto.nix
+  ./services/x11/display-managers/default.nix
+  ./services/x11/display-managers/kdm.nix
+  ./services/x11/display-managers/slim.nix
+  ./services/x11/display-managers/lightdm.nix
+  ./services/x11/hardware/multitouch.nix
+  ./services/x11/hardware/synaptics.nix
+  ./services/x11/hardware/wacom.nix
+  ./services/x11/window-managers/awesome.nix
+  #./services/x11/window-managers/compiz.nix
+  ./services/x11/window-managers/default.nix
+  ./services/x11/window-managers/icewm.nix
+  ./services/x11/window-managers/metacity.nix
+  ./services/x11/window-managers/none.nix
+  ./services/x11/window-managers/twm.nix
+  ./services/x11/window-managers/wmii.nix
+  ./services/x11/window-managers/xmonad.nix
+  ./services/x11/xfs.nix
+  ./services/x11/xserver.nix
+  ./system/activation/activation-script.nix
+  ./system/activation/top-level.nix
+  ./system/boot/kernel.nix
+  ./system/boot/kexec.nix
+  ./system/boot/loader/efi-boot-stub/efi-boot-stub.nix
+  ./system/boot/loader/efi.nix
+  ./system/boot/loader/generations-dir/generations-dir.nix
+  ./system/boot/loader/gummiboot/gummiboot.nix
+  ./system/boot/loader/raspberrypi/raspberrypi.nix
+  ./system/boot/loader/grub/grub.nix
+  ./system/boot/loader/grub/memtest.nix
+  ./system/boot/loader/init-script/init-script.nix
+  ./system/boot/luksroot.nix
+  ./system/boot/modprobe.nix
+  ./system/boot/shutdown.nix
+  ./system/boot/stage-1.nix
+  ./system/boot/stage-2.nix
+  ./system/boot/systemd.nix
+  ./system/etc/etc.nix
+  ./system/upstart/upstart.nix
+  ./tasks/cpu-freq.nix
+  ./tasks/filesystems.nix
+  ./tasks/filesystems/btrfs.nix
+  ./tasks/filesystems/ext.nix
+  ./tasks/filesystems/nfs.nix
+  ./tasks/filesystems/reiserfs.nix
+  ./tasks/filesystems/unionfs-fuse.nix
+  ./tasks/filesystems/vfat.nix
+  ./tasks/filesystems/xfs.nix
+  ./tasks/filesystems/zfs.nix
+  ./tasks/kbd.nix
+  ./tasks/lvm.nix
+  ./tasks/network-interfaces.nix
+  ./tasks/scsi-link-power-management.nix
+  ./tasks/swraid.nix
+  ./virtualisation/libvirtd.nix
+  ./virtualisation/nova.nix
+  ./virtualisation/virtualbox-guest.nix
+  ./virtualisation/xen-dom0.nix
+]
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
new file mode 100644
index 00000000000..511c118e2bf
--- /dev/null
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -0,0 +1,55 @@
+# This module enables all hardware supported by NixOS: i.e., all
+# firmware is included, and all devices from which one may boot are
+# enabled in the initrd.  Its primary use is in the NixOS installation
+# CDs.
+
+{ config, pkgs, ... }:
+
+{
+
+  # The initrd has to contain any module that might be necessary for
+  # mounting the CD/DVD.
+  boot.initrd.availableKernelModules =
+    [ # SATA/PATA support.
+      "ahci"
+
+      "ata_piix"
+
+      "sata_inic162x" "sata_nv" "sata_promise" "sata_qstor"
+      "sata_sil" "sata_sil24" "sata_sis" "sata_svw" "sata_sx4"
+      "sata_uli" "sata_via" "sata_vsc"
+
+      "pata_ali" "pata_amd" "pata_artop" "pata_atiixp"
+      "pata_cs5520" "pata_cs5530" "pata_cs5535" "pata_efar"
+      "pata_hpt366" "pata_hpt37x" "pata_hpt3x2n" "pata_hpt3x3"
+      "pata_it8213" "pata_it821x" "pata_jmicron" "pata_marvell"
+      "pata_mpiix" "pata_netcell" "pata_ns87410" "pata_oldpiix"
+      "pata_pcmcia" "pata_pdc2027x" "pata_qdi" "pata_rz1000"
+      "pata_sc1200" "pata_serverworks" "pata_sil680" "pata_sis"
+      "pata_sl82c105" "pata_triflex" "pata_via"
+      "pata_winbond"
+
+      # SCSI support (incomplete).
+      "3w-9xxx" "3w-xxxx" "aic79xx" "aic7xxx" "arcmsr"
+
+      # USB support, especially for booting from USB CD-ROM
+      # drives.
+      "usb_storage"
+
+      # Firewire support.  Not tested.
+      "ohci1394" "sbp2"
+
+      # Virtio (QEMU, KVM etc.) support.
+      "virtio_net" "virtio_pci" "virtio_blk" "virtio_balloon" "virtio_console"
+
+      # Keyboards
+      "hid_apple"
+    ];
+
+  # Include lots of firmware.
+  hardware.enableAllFirmware = true;
+
+  imports =
+    [ ../hardware/network/zydas-zd1211.nix ];
+
+}
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
new file mode 100644
index 00000000000..20b808c29e0
--- /dev/null
+++ b/nixos/modules/profiles/base.nix
@@ -0,0 +1,55 @@
+# This module defines the software packages included in the "minimal"
+# installation CD.  It might be useful elsewhere.
+
+{ config, pkgs, ... }:
+
+{
+  # Include some utilities that are useful for installing or repairing
+  # the system.
+  environment.systemPackages = [
+    pkgs.subversion # for nixos-checkout
+    pkgs.w3m # needed for the manual anyway
+    pkgs.testdisk # useful for repairing boot problems
+    pkgs.mssys # for writing Microsoft boot sectors / MBRs
+    pkgs.parted
+    pkgs.gptfdisk
+    pkgs.ddrescue
+    pkgs.ccrypt
+    pkgs.cryptsetup # needed for dm-crypt volumes
+
+    # Some networking tools.
+    pkgs.fuse
+    pkgs.sshfsFuse
+    pkgs.socat
+    pkgs.screen
+
+    # Hardware-related tools.
+    pkgs.sdparm
+    pkgs.hdparm
+    pkgs.dmraid
+    pkgs.smartmontools # for diagnosing hard disks
+
+    # Tools to create / manipulate filesystems.
+    pkgs.ntfsprogs # for resizing NTFS partitions
+    pkgs.dosfstools
+    pkgs.xfsprogs
+    pkgs.jfsutils
+    #pkgs.jfsrec # disabled because of Boost dependency
+
+    # Some compression/archiver tools.
+    pkgs.unrar
+    pkgs.unzip
+    pkgs.zip
+    pkgs.dar # disk archiver
+    pkgs.cabextract
+
+    # Some editors.
+    pkgs.vim
+    pkgs.bvi # binary editor
+    pkgs.joe
+  ];
+
+  # Include support for various filesystems.
+  boot.supportedFilesystems = [ "btrfs" "reiserfs" "vfat" ];
+
+}
diff --git a/nixos/modules/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix
new file mode 100644
index 00000000000..d7190020e7e
--- /dev/null
+++ b/nixos/modules/profiles/clone-config.nix
@@ -0,0 +1,100 @@
+{ config, pkgs, modules, ... }:
+
+with pkgs.lib;
+
+let
+
+  # Location of the repository on the harddrive
+  nixosPath = toString ../..;
+
+  # Check if the path is from the NixOS repository
+  isNixOSFile = path:
+    let s = toString path; in
+      removePrefix nixosPath s != s;
+
+  # Copy modules given as extra configuration files.  Unfortunately, we
+  # cannot serialized attribute set given in the list of modules (that's why
+  # you should use files).
+  moduleFiles =
+    filter isPath modules;
+
+  # Partition module files because between NixOS and non-NixOS files.  NixOS
+  # files may change if the repository is updated.
+  partitionedModuleFiles =
+    let p = partition isNixOSFile moduleFiles; in
+    { nixos = p.right; others = p.wrong; };
+
+  # Path transformed to be valid on the installation device.  Thus the
+  # device configuration could be rebuild.
+  relocatedModuleFiles =
+    let
+      relocateNixOS = path:
+        "<nixos" + removePrefix nixosPath (toString path) + ">";
+      relocateOthers = null;
+    in
+      { nixos = map relocateNixOS partitionedModuleFiles.nixos;
+        others = []; # TODO: copy the modules to the install-device repository.
+      };
+
+  # A dummy /etc/nixos/configuration.nix in the booted CD that
+  # rebuilds the CD's configuration (and allows the configuration to
+  # be modified, of course, providing a true live CD).  Problem is
+  # that we don't really know how the CD was built - the Nix
+  # expression language doesn't allow us to query the expression being
+  # evaluated.  So we'll just hope for the best.
+  configClone = pkgs.writeText "configuration.nix"
+    ''
+      { config, pkgs, ... }:
+
+      {
+        imports = [ ${toString config.installer.cloneConfigIncludes} ];
+      }
+    '';
+
+in
+
+{
+
+  options = {
+
+    installer.cloneConfig = mkOption {
+      default = true;
+      description = ''
+        Try to clone the installation-device configuration by re-using it's
+        profile from the list of imported modules.
+      '';
+    };
+
+    installer.cloneConfigIncludes = mkOption {
+      default = [];
+      example = [ "./nixos/modules/hardware/network/rt73.nix" ];
+      description = ''
+        List of modules used to re-build this installation device profile.
+      '';
+    };
+
+  };
+
+  config = {
+
+    installer.cloneConfigIncludes =
+      relocatedModuleFiles.nixos ++ relocatedModuleFiles.others;
+
+    boot.postBootCommands =
+      ''
+        # Provide a mount point for nixos-install.
+        mkdir -p /mnt
+
+        ${optionalString config.installer.cloneConfig ''
+          # Provide a configuration for the CD/DVD itself, to allow users
+          # to run nixos-rebuild to change the configuration of the
+          # running system on the CD/DVD.
+          if ! [ -e /etc/nixos/configuration.nix ]; then
+            cp ${configClone} /etc/nixos/configuration.nix
+          fi
+       ''}
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
new file mode 100644
index 00000000000..396dcf6c5d3
--- /dev/null
+++ b/nixos/modules/profiles/demo.nix
@@ -0,0 +1,16 @@
+{ config, pkgs, ... }:
+
+{
+  imports = [ ./graphical.nix ];
+
+  users.extraUsers.demo =
+    { description = "Demo user account";
+      group = "users";
+      extraGroups = [ "wheel" ];
+      home = "/home/demo";
+      createHome = true;
+      useDefaultShell = true;
+      password = "demo";
+      isSystemUser = false;
+    };
+}
diff --git a/nixos/modules/profiles/graphical.nix b/nixos/modules/profiles/graphical.nix
new file mode 100644
index 00000000000..75ac5e41f83
--- /dev/null
+++ b/nixos/modules/profiles/graphical.nix
@@ -0,0 +1,14 @@
+# This module defines a NixOS configuration that contains X11 and
+# KDE 4.  It's used by the graphical installation CD.
+
+{ config, pkgs, ... }:
+
+{
+  services.xserver = {
+    enable = true;
+    displayManager.kdm.enable = true;
+    desktopManager.kde4.enable = true;
+  };
+
+  environment.systemPackages = [ pkgs.glxinfo ];
+}
diff --git a/nixos/modules/profiles/headless.nix b/nixos/modules/profiles/headless.nix
new file mode 100644
index 00000000000..541c46ca50c
--- /dev/null
+++ b/nixos/modules/profiles/headless.nix
@@ -0,0 +1,21 @@
+# Common configuration for headless machines (e.g., Amazon EC2
+# instances).
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  sound.enable = false;
+  boot.vesa = false;
+
+  # Don't start a tty on the serial consoles.
+  systemd.services."serial-getty@ttyS0".enable = false;
+  systemd.services."serial-getty@hvc0".enable = false;
+
+  # Since we can't manually respond to a panic, just reboot.
+  boot.kernelParams = [ "panic=1" "boot.panic_on_fail" ];
+
+  # Don't allow emergency mode, because we don't have a console.
+  systemd.enableEmergencyMode = false;
+}
diff --git a/nixos/modules/profiles/installation-device.nix b/nixos/modules/profiles/installation-device.nix
new file mode 100644
index 00000000000..3b058c6e971
--- /dev/null
+++ b/nixos/modules/profiles/installation-device.nix
@@ -0,0 +1,56 @@
+# Provide a basic configuration for installation devices like CDs.
+{ config, pkgs, modules, ... }:
+
+with pkgs.lib;
+
+{
+  imports =
+    [ # Enable devices which are usually scanned, because we don't know the
+      # target system.
+      ../installer/scan/detected.nix
+      ../installer/scan/not-detected.nix
+
+      # Allow "nixos-rebuild" to work properly by providing
+      # /etc/nixos/configuration.nix.
+      ./clone-config.nix
+    ];
+
+  config = {
+
+    # Show the manual.
+    services.nixosManual.showManual = true;
+
+    # Let the user play Rogue on TTY 8 during the installation.
+    services.rogue.enable = true;
+
+    # Disable some other stuff we don't need.
+    security.sudo.enable = false;
+
+    # Include only the en_US locale.  This saves 75 MiB or so compared to
+    # the full glibcLocales package.
+    i18n.supportedLocales = ["en_US.UTF-8/UTF-8" "en_US/ISO-8859-1"];
+
+    # Some more help text.
+    services.mingetty.helpLine =
+      ''
+
+        Log in as "root" with an empty password.  ${
+          optionalString config.services.xserver.enable
+            "Type `start display-manager' to\nstart the graphical user interface."}
+      '';
+
+    # Allow sshd to be started manually through "start sshd".
+    services.openssh.enable = true;
+    systemd.services.sshd.wantedBy = mkOverride 50 [];
+
+    # Enable wpa_supplicant, but don't start it by default.
+    networking.wireless.enable = true;
+    jobs.wpa_supplicant.startOn = pkgs.lib.mkOverride 50 "";
+
+    # Tell the Nix evaluator to garbage collect more aggressively.
+    # This is desirable in memory-constrained environments that don't
+    # (yet) have swap set up.
+    environment.variables.GC_INITIAL_HEAP_SIZE = "100000";
+
+  };
+}
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
new file mode 100644
index 00000000000..821b9f93465
--- /dev/null
+++ b/nixos/modules/profiles/minimal.nix
@@ -0,0 +1,11 @@
+# This module defines a small NixOS configuration.  It does not
+# contain any graphical stuff.
+
+{ config, pkgs, ... }:
+
+{
+  # Don't include X libraries.
+  programs.ssh.setXAuthLocation = false;
+  fonts.enableFontConfig = false;
+  fonts.enableCoreFonts = false;
+}
diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix
new file mode 100644
index 00000000000..c8e6fd4aa76
--- /dev/null
+++ b/nixos/modules/profiles/qemu-guest.nix
@@ -0,0 +1,9 @@
+# Common configuration for virtual machines running under QEMU (using
+# virtio).
+
+{ config, pkgs, ... }:
+
+{
+  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "9p" "9pnet_virtio" ];
+  boot.kernelModules = [ "virtio_balloon" "virtio_console" ];
+}
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
new file mode 100644
index 00000000000..7fdaab9d67d
--- /dev/null
+++ b/nixos/modules/programs/atop.nix
@@ -0,0 +1,36 @@
+# Global configuration for atop.
+
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let cfg = config.programs.atop;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.atop = {
+
+      settings = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          flags = "a1f";
+          interval = 5;
+        };
+        description = ''
+          Parameters to be written to <filename>/etc/atoprc</filename>
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf (cfg.settings != {}) {
+    environment.etc."atoprc".text =
+      concatStrings (mapAttrsToList (n: v: "${n} ${toString v}\n") cfg.settings);
+  };
+}
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
new file mode 100644
index 00000000000..8cfe3f990ad
--- /dev/null
+++ b/nixos/modules/programs/bash/bash.nix
@@ -0,0 +1,217 @@
+# This module defines global configuration for the Bash shell, in
+# particular /etc/bashrc and /etc/profile.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.bash;
+
+  bashCompletion = optionalString cfg.enableCompletion ''
+    # Check whether we're running a version of Bash that has support for
+    # programmable completion. If we do, enable all modules installed in
+    # the system (and user profile).
+    if shopt -q progcomp &>/dev/null; then
+      . "${pkgs.bashCompletion}/etc/profile.d/bash_completion.sh"
+      nullglobStatus=$(shopt -p nullglob)
+      shopt -s nullglob
+      for p in $NIX_PROFILES; do
+        for m in "$p/etc/bash_completion.d/"* "$p/share/bash-completion/completions/"*; do
+          . $m
+        done
+      done
+      eval "$nullglobStatus"
+      unset nullglobStatus p m
+    fi
+  '';
+
+  bashAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k}='${v}'") cfg.shellAliases
+  );
+
+in
+
+{
+  options = {
+
+    programs.bash = {
+
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whenever to configure Bash as an interactive shell.
+          Note that this tries to make Bash the default
+          <option>users.defaultUserShell</option>,
+          which in turn means that you might need to explicitly
+          set this variable if you have another shell configured
+          with NixOS.
+        '';
+        type = types.bool;
+      };
+
+      shellAliases = mkOption {
+        default = config.environment.shellAliases // { which = "type -P"; };
+        description = ''
+          Set of aliases for bash shell. See <option>environment.shellAliases</option>
+          for an option format description.
+        '';
+        type = types.attrs; # types.attrsOf types.stringOrPath;
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during login bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = ''
+          # Provide a nice prompt.
+          PROMPT_COLOR="1;31m"
+          let $UID && PROMPT_COLOR="1;32m"
+          PS1="\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] "
+          if test "$TERM" = "xterm"; then
+            PS1="\[\033]2;\h:\u:\w\007\]$PS1"
+          fi
+        '';
+        description = ''
+          Shell script code used to initialise the bash prompt.
+        '';
+        type = types.lines;
+      };
+
+      enableCompletion = mkOption {
+        default = false;
+        description = ''
+          Enable Bash completion for all interactive bash shells.
+        '';
+        type = types.bool;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.bash = {
+
+      shellInit = ''
+        . ${config.system.build.setEnvironment}
+
+        ${cfge.shellInit}
+      '';
+
+      loginShellInit = cfge.loginShellInit;
+
+      interactiveShellInit = ''
+        ${cfge.interactiveShellInit}
+
+        # Check the window size after every command.
+        shopt -s checkwinsize
+
+        # Disable hashing (i.e. caching) of command lookups.
+        set +h
+
+        ${cfg.promptInit}
+        ${bashCompletion}
+        ${bashAliases}
+      '';
+
+    };
+
+    environment.etc."profile".text =
+      ''
+        # /etc/profile: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for login shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_PROFILE_SOURCED" ]; then return; fi
+        __ETC_PROFILE_SOURCED=1
+
+        # Prevent this file from being sourced by interactive non-login child shells.
+        export __ETC_PROFILE_DONE=1
+
+        ${cfg.shellInit}
+        ${cfg.loginShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/profile.local; then
+          . /etc/profile.local
+        fi
+
+        if [ -n "''${BASH_VERSION:-}" ]; then
+          . /etc/bashrc
+        fi
+      '';
+
+    environment.etc."bashrc".text =
+      ''
+        # /etc/bashrc: DO NOT EDIT -- this file has been generated automatically.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_BASHRC_SOURCED" -o -n "$NOSYSBASHRC" ]; then return; fi
+        __ETC_BASHRC_SOURCED=1
+
+        # If the profile was not loaded in a parent process, source
+        # it.  But otherwise don't do it because we don't want to
+        # clobber overridden values of $PATH, etc.
+        if [ -z "$__ETC_PROFILE_DONE" ]; then
+            . /etc/profile
+        fi
+
+        # We are not always an interactive shell.
+        if [ -n "$PS1" ]; then
+          ${cfg.interactiveShellInit}
+        fi
+
+        # Read system-wide modifications.
+        if test -f /etc/bashrc.local; then
+          . /etc/bashrc.local
+        fi
+      '';
+
+    # Configuration for readline in bash.
+    environment.etc."inputrc".source = ./inputrc;
+
+    users.defaultUserShell = mkDefault "/run/current-system/sw/bin/bash";
+
+    environment.pathsToLink = optionals cfg.enableCompletion [
+      "/etc/bash_completion.d"
+      "/share/bash-completion"
+    ];
+
+    environment.shells =
+      [ "/run/current-system/sw/bin/bash"
+        "/var/run/current-system/sw/bin/bash"
+        "/run/current-system/sw/bin/sh"
+        "/var/run/current-system/sw/bin/sh"
+        "${pkgs.bashInteractive}/bin/bash"
+        "${pkgs.bashInteractive}/bin/sh"
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/bash/command-not-found.nix b/nixos/modules/programs/bash/command-not-found.nix
new file mode 100644
index 00000000000..502320446a3
--- /dev/null
+++ b/nixos/modules/programs/bash/command-not-found.nix
@@ -0,0 +1,51 @@
+# This module provides suggestions of packages to install if the user
+# tries to run a missing command in Bash.  This is implemented using a
+# SQLite database that maps program names to Nix package names (e.g.,
+# "pdflatex" is mapped to "tetex").
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  commandNotFound = pkgs.substituteAll {
+    name = "command-not-found";
+    dir = "bin";
+    src = ./command-not-found.pl;
+    isExecutable = true;
+    inherit (pkgs) perl;
+    perlFlags = concatStrings (map (path: "-I ${path}/lib/perl5/site_perl ")
+      [ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite ]);
+  };
+
+in
+
+{
+
+  programs.bash.interactiveShellInit =
+    ''
+      # This function is called whenever a command is not found.
+      command_not_found_handle() {
+        local p=/run/current-system/sw/bin/command-not-found
+        if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then
+          # Run the helper program.
+          $p "$1"
+          # Retry the command if we just installed it.
+          if [ $? = 126 ]; then
+            "$@"
+          else
+            return 127
+          fi
+        else
+          echo "$1: command not found" >&2
+          return 127
+        fi
+      }
+    '';
+
+  environment.systemPackages = [ commandNotFound ];
+
+  # TODO: tab completion for uninstalled commands! :-)
+
+}
diff --git a/nixos/modules/programs/bash/command-not-found.pl b/nixos/modules/programs/bash/command-not-found.pl
new file mode 100644
index 00000000000..916649059d3
--- /dev/null
+++ b/nixos/modules/programs/bash/command-not-found.pl
@@ -0,0 +1,48 @@
+#! @perl@/bin/perl -w @perlFlags@
+
+use strict;
+use DBI;
+use DBD::SQLite;
+use Config;
+
+my $program = $ARGV[0];
+
+my $dbPath = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite";
+
+my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
+    or die "cannot open database `$dbPath'";
+$dbh->{RaiseError} = 0;
+$dbh->{PrintError} = 0;
+
+my $system = $ENV{"NIX_SYSTEM"} // $Config{myarchname};
+
+my $res = $dbh->selectall_arrayref(
+    "select package from Programs where system = ? and name = ?",
+    { Slice => {} }, $system, $program);
+
+if (!defined $res || scalar @$res == 0) {
+    print STDERR "$program: command not found\n";
+} elsif (scalar @$res == 1) {
+    my $package = @$res[0]->{package};
+    if ($ENV{"NIX_AUTO_INSTALL"} // "") {
+        print STDERR <<EOF;
+The program ‘$program’ is currently not installed. It is provided by
+the package ‘$package’, which I will now install for you.
+EOF
+        ;
+        exit 126 if system("nix-env", "-i", $package) == 0;
+    } else {
+        print STDERR <<EOF;
+The program ‘$program’ is currently not installed. You can install it by typing:
+  nix-env -i $package
+EOF
+    }
+} else {
+    print STDERR <<EOF;
+The program ‘$program’ is currently not installed. It is provided by
+several packages. You can install it by typing one of the following:
+EOF
+    print STDERR "  nix-env -i $_->{package}\n" foreach @$res;
+}
+
+exit 127;
diff --git a/nixos/modules/programs/bash/inputrc b/nixos/modules/programs/bash/inputrc
new file mode 100644
index 00000000000..e4eabc052c5
--- /dev/null
+++ b/nixos/modules/programs/bash/inputrc
@@ -0,0 +1,36 @@
+# inputrc borrowed from CentOS (RHEL).
+
+set bell-style none
+
+set meta-flag on
+set input-meta on
+set convert-meta off
+set output-meta on
+
+#set mark-symlinked-directories on
+
+$if mode=emacs
+
+# for linux console and RH/Debian xterm
+"\e[1~": beginning-of-line
+"\e[4~": end-of-line
+"\e[5~": beginning-of-history
+"\e[6~": end-of-history
+"\e[3~": delete-char
+"\e[2~": quoted-insert
+"\e[5C": forward-word
+"\e[5D": backward-word
+"\e[1;5C": forward-word
+"\e[1;5D": backward-word
+
+# for rxvt
+"\e[8~": end-of-line
+
+# for non RH/Debian xterm, can't hurt for RH/DEbian xterm
+"\eOH": beginning-of-line
+"\eOF": end-of-line
+
+# for freebsd console
+"\e[H": beginning-of-line
+"\e[F": end-of-line
+$endif
diff --git a/nixos/modules/programs/blcr.nix b/nixos/modules/programs/blcr.nix
new file mode 100644
index 00000000000..e1e31b4a56a
--- /dev/null
+++ b/nixos/modules/programs/blcr.nix
@@ -0,0 +1,27 @@
+{ config, pkgs, ... }:
+
+let
+  inherit (pkgs.lib) mkOption mkIf;
+  cfg = config.environment.blcr;
+  blcrPkg = config.boot.kernelPackages.blcr;
+in
+
+{
+  ###### interface
+
+  options = {
+    environment.blcr.enable = mkOption {
+      default = false;
+      description =
+        "Whether to enable support for the BLCR checkpointing tool.";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "blcr" "blcr_imports" ];
+    boot.extraModulePackages = [ blcrPkg ];
+    environment.systemPackages = [ blcrPkg ];
+  };
+}
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
new file mode 100644
index 00000000000..f42df351422
--- /dev/null
+++ b/nixos/modules/programs/environment.nix
@@ -0,0 +1,79 @@
+# This module defines a standard configuration for NixOS global environment.
+
+# Most of the stuff here should probably be moved elsewhere sometime.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  config = {
+
+    environment.variables =
+      { LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+        LOCATE_PATH = "/var/cache/locatedb";
+        NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
+        NIX_PATH =
+          [ "/nix/var/nix/profiles/per-user/root/channels/nixos"
+            "nixpkgs=/etc/nixos/nixpkgs"
+            "nixos=/etc/nixos/nixos"
+            "nixos-config=/etc/nixos/configuration.nix"
+            "services=/etc/nixos/services"
+          ];
+        PAGER = "less -R";
+        EDITOR = "nano";
+      };
+
+    environment.profiles =
+      [ "$HOME/.nix-profile"
+        "/nix/var/nix/profiles/default"
+        "/run/current-system/sw"
+      ];
+
+    # !!! fix environment.profileVariables definition and then move
+    # most of these elsewhere
+    environment.profileVariables = (i:
+      { PATH = [ "${i}/bin" "${i}/sbin" "${i}/lib/kde4/libexec" ];
+        MANPATH = [ "${i}/man" "${i}/share/man" ];
+        INFOPATH = [ "${i}/info" "${i}/share/info" ];
+        PKG_CONFIG_PATH = [ "${i}/lib/pkgconfig" ];
+        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" ];
+        KDEDIRS = [ "${i}" ];
+        STRIGI_PLUGIN_PATH = [ "${i}/lib/strigi/" ];
+        QT_PLUGIN_PATH = [ "${i}/lib/qt4/plugins" "${i}/lib/kde4/plugins" ];
+        QTWEBKIT_PLUGIN_PATH = [ "${i}/lib/mozilla/plugins/" ];
+        GTK_PATH = [ "${i}/lib/gtk-2.0" ];
+        XDG_CONFIG_DIRS = [ "${i}/etc/xdg" ];
+        XDG_DATA_DIRS = [ "${i}/share" ];
+        MOZ_PLUGIN_PATH = [ "${i}/lib/mozilla/plugins" ];
+      });
+
+    environment.extraInit =
+      ''
+         # reset TERM with new TERMINFO available (if any)
+         export TERM=$TERM
+
+         unset ASPELL_CONF
+         for i in ${concatStringsSep " " (reverseList cfg.profiles)} ; do
+           if [ -d "$i/lib/aspell" ]; then
+             export ASPELL_CONF="dict-dir $i/lib/aspell"
+           fi
+         done
+
+         export NIX_USER_PROFILE_DIR="/nix/var/nix/profiles/per-user/$USER"
+         export NIX_PROFILES="${concatStringsSep " " (reverseList cfg.profiles)}"
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/info.nix b/nixos/modules/programs/info.nix
new file mode 100644
index 00000000000..30c25cf3420
--- /dev/null
+++ b/nixos/modules/programs/info.nix
@@ -0,0 +1,36 @@
+{config, pkgs, ...}:
+
+let
+
+  # Quick hack to make the `info' command work properly.  `info' needs
+  # a "dir" file containing all the installed Info files, which we
+  # don't have (it would be impure to have a package installation
+  # update some global "dir" file).  So this wrapper script around
+  # "info" builds a temporary "dir" file on the fly.  This is a bit
+  # slow (on a cold cache) but not unacceptably so.
+  infoWrapper = pkgs.writeScriptBin "info"
+    ''
+      #! ${pkgs.stdenv.shell}
+
+      dir=$(mktemp --tmpdir -d "info.dir.XXXXXX")
+
+      if test -z "$dir"; then exit 1; fi
+
+      trap 'rm -rf "$dir"' EXIT
+
+      shopt -s nullglob
+
+      for i in $(IFS=:; echo $INFOPATH); do
+          for j in $i/*.info; do
+              ${pkgs.texinfo}/bin/install-info --quiet $j $dir/dir
+          done
+      done
+
+      INFOPATH=$dir:$INFOPATH ${pkgs.texinfo}/bin/info "$@"
+    ''; # */
+
+in
+
+{
+  environment.systemPackages = [ infoWrapper pkgs.texinfo ];
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
new file mode 100644
index 00000000000..695c0b6620f
--- /dev/null
+++ b/nixos/modules/programs/shadow.nix
@@ -0,0 +1,103 @@
+# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  loginDefs =
+    ''
+      DEFAULT_HOME yes
+
+      SYS_UID_MIN  100
+      SYS_UID_MAX  499
+      UID_MIN      1000
+      UID_MAX      29999
+
+      SYS_GID_MIN  100
+      SYS_GID_MAX  499
+      GID_MIN      1000
+      GID_MAX      29999
+
+      TTYGROUP     tty
+      TTYPERM      0620
+
+      # Ensure privacy for newly created home directories.
+      UMASK        077
+
+      # Uncomment this to allow non-root users to change their account
+      #information.  This should be made configurable.
+      #CHFN_RESTRICT frwh
+
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.defaultUserShell = pkgs.lib.mkOption {
+      description = ''
+        This option defines the default shell assigned to user
+        accounts.  This must not be a store path, since the path is
+        used outside the store (in particular in /etc/passwd).
+        Rather, it should be the path of a symlink that points to the
+        actual shell in the Nix store.
+      '';
+      type = types.uniq types.path;
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ pkgs.shadow ];
+
+    environment.etc =
+      [ { # /etc/login.defs: global configuration for pwdutils.  You
+          # cannot login without it!
+          source = pkgs.writeText "login.defs" loginDefs;
+          target = "login.defs";
+        }
+
+        { # /etc/default/useradd: configuration for useradd.
+          source = pkgs.writeText "useradd"
+            ''
+              GROUP=100
+              HOME=/home
+              SHELL=${config.users.defaultUserShell}
+            '';
+          target = "default/useradd";
+        }
+      ];
+
+    security.pam.services =
+      [ { name = "chsh"; rootOK = true; }
+        { name = "chfn"; rootOK = true; }
+        { name = "su"; rootOK = true; forwardXAuth = true; }
+        { name = "passwd"; }
+        # Note: useradd, groupadd etc. aren't setuid root, so it
+        # doesn't really matter what the PAM config says as long as it
+        # lets root in.
+        { name = "useradd"; rootOK = true; }
+        { name = "usermod"; rootOK = true; }
+        { name = "userdel"; rootOK = true; }
+        { name = "groupadd"; rootOK = true; }
+        { name = "groupmod"; rootOK = true; }
+        { name = "groupmems"; rootOK = true; }
+        { name = "groupdel"; rootOK = true; }
+        { name = "login"; startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; }
+      ];
+
+    security.setuidPrograms = [ "passwd" "chfn" "su" "newgrp" ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/shell.nix b/nixos/modules/programs/shell.nix
new file mode 100644
index 00000000000..679c4979dfa
--- /dev/null
+++ b/nixos/modules/programs/shell.nix
@@ -0,0 +1,64 @@
+# This module defines a standard configuration for NixOS shells.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  config = {
+
+    environment.shellAliases =
+      { ls = "ls --color=tty";
+        ll = "ls -l";
+        l  = "ls -alh";
+      };
+
+    environment.shellInit =
+      ''
+        # Set up the per-user profile.
+        mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
+        if test "$(stat --printf '%u' $NIX_USER_PROFILE_DIR)" != "$(id -u)"; then
+            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
+
+        # Subscribe the root user to the NixOS channel by default.
+        if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
+            echo "http://nixos.org/channels/nixos-unstable 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
+
+        # 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
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
new file mode 100644
index 00000000000..5ec32376b60
--- /dev/null
+++ b/nixos/modules/programs/ssh.nix
@@ -0,0 +1,68 @@
+# Global configuration for the SSH client.
+
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let cfg  = config.programs.ssh;
+    cfgd = config.services.openssh;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.ssh = {
+
+      forwardX11 = mkOption {
+        default = false;
+        description = ''
+          Whether to request X11 forwarding on outgoing connections by default.
+          This is useful for running graphical programs on the remote machine and have them display to your local X11 server.
+          Historically, this value has depended on the value used by the local sshd daemon, but there really isn't a relation between the two.
+          Note: there are some security risks to forwarding an X11 connection.
+          NixOS's X server is built with the SECURITY extension, which prevents some obvious attacks.
+          To enable or disable forwarding on a per-connection basis, see the -X and -x options to ssh.
+          The -Y option to ssh enables trusted forwarding, which bypasses the SECURITY extension.
+        '';
+      };
+
+      setXAuthLocation = mkOption {
+        default = true;
+        description = ''
+          Whether to set the path to xauth for X11-forwarded connections.
+          Pulls in X11 dependency.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration text appended to <filename>ssh_config</filename>.
+          See the ssh_config(5) man page for help.
+        '';
+      };
+    };
+  };
+
+  assertions = [{ assertion = if cfg.forwardX11 then cfg.setXAuthLocation else true;
+                  message = "cannot enable X11 forwarding without setting xauth location";}];
+
+  config = {
+    environment.etc =
+      [ { # SSH configuration.  Slight duplication of the sshd_config
+          # generation in the sshd service.
+          source = pkgs.writeText "ssh_config" ''
+            AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
+            ${optionalString cfg.setXAuthLocation ''
+              XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
+            ''}
+            ForwardX11 ${if cfg.forwardX11 then "yes" else "no"}
+            ${cfg.extraConfig}
+          '';
+          target = "ssh/ssh_config";
+        }
+      ];
+  };
+}
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
new file mode 100644
index 00000000000..904989d57a0
--- /dev/null
+++ b/nixos/modules/programs/ssmtp.nix
@@ -0,0 +1,111 @@
+# Configuration for `ssmtp', a trivial mail transfer agent that can
+# replace sendmail/postfix on simple systems.  It delivers email
+# directly to an SMTP server defined in its configuration file, wihout
+# queueing mail locally.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking.defaultMailServer;
+
+in
+
+{
+
+  options = {
+
+    networking.defaultMailServer = {
+
+      directDelivery = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Use the trivial Mail Transfer Agent (MTA)
+          <command>ssmtp</command> package to allow programs to send
+          e-mail.  If you don't want to run a “real” MTA like
+          <command>sendmail</command> or <command>postfix</command> on
+          your machine, set this option to <literal>true</literal>, and
+          set the option
+          <option>networking.defaultMailServer.hostName</option> to the
+          host name of your preferred mail server.
+        '';
+      };
+
+      hostName = mkOption {
+        example = "mail.example.org";
+        description = ''
+          The host name of the default mail server to use to deliver
+          e-mail.
+        '';
+      };
+
+      domain = mkOption {
+        default = "";
+        example = "example.org";
+        description = ''
+          The domain from which mail will appear to be sent.
+        '';
+      };
+
+      useTLS = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Whether TLS should be used to connect to the default mail
+          server.
+        '';
+      };
+
+      useSTARTTLS = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Whether the STARTTLS should be used to connect to the default
+          mail server.  (This is needed for TLS-capable mail servers
+          running on the default SMTP port 25.)
+        '';
+      };
+
+      authUser = mkOption {
+        default = "";
+        example = "foo@example.org";
+        description = ''
+          Username used for SMTP auth. Leave blank to disable.
+        '';
+      };
+
+      authPass = mkOption {
+        default = "";
+        example = "correctHorseBatteryStaple";
+        description = ''
+          Password used for SMTP auth. (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.directDelivery {
+
+    environment.etc."ssmtp/ssmtp.conf".text =
+      ''
+        MailHub=${cfg.hostName}
+        FromLineOverride=YES
+        ${if cfg.domain != "" then "rewriteDomain=${cfg.domain}" else ""}
+        UseTLS=${if cfg.useTLS then "YES" else "NO"}
+        UseSTARTTLS=${if cfg.useSTARTTLS then "YES" else "NO"}
+        #Debug=YES
+        ${if cfg.authUser != "" then "AuthUser=${cfg.authUser}" else ""}
+        ${if cfg.authPass != "" then "AuthPass=${cfg.authPass}" else ""}
+      '';
+
+    environment.systemPackages = [pkgs.ssmtp];
+
+  };
+
+}
diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix
new file mode 100644
index 00000000000..2b3bfbc6c18
--- /dev/null
+++ b/nixos/modules/programs/venus.nix
@@ -0,0 +1,174 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+let
+  cfg = config.services.venus;
+
+  configFile = pkgs.writeText "venus.ini"
+    ''
+      [Planet]
+      name = ${cfg.name}
+      link = ${cfg.link}
+      owner_name = ${cfg.ownerName}
+      owner_email = ${cfg.ownerEmail}
+      output_theme = ${cfg.cacheDirectory}/theme
+      output_dir = ${cfg.outputDirectory}
+      cache_directory = ${cfg.cacheDirectory}
+      items_per_page = ${toString cfg.itemsPerPage}
+      ${(concatStringsSep "\n\n"
+            (map ({ name, feedUrl, homepageUrl }:
+            ''
+              [${feedUrl}]
+              name = ${name}
+              link = ${homepageUrl}
+            '') cfg.feeds))}
+    '';
+
+in
+{
+
+  options = {
+    services.venus = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Planet Venus is an awesome ‘river of news’ feed reader. It downloads
+          news feeds published by web sites and aggregates their content
+          together into a single combined feed, latest news first.
+        '';
+      };
+
+      dates = mkOption {
+        default = "*:0,15,30,45";
+        type = types.string;
+        description = ''
+          Specification (in the format described by
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          which the Venus will collect feeds.
+        '';
+      };
+
+      user = mkOption {
+        default = "root";
+        type = types.string;
+        description = ''
+          User for running venus script.
+        '';
+      };
+
+      group = mkOption {
+        default = "root";
+        type = types.string;
+        description = ''
+          Group for running venus script.
+        '';
+      };
+
+      name = mkOption {
+        default = "NixOS Planet";
+        type = types.string;
+        description = ''
+          Your planet's name.
+        '';
+      };
+
+      link = mkOption {
+        default = "http://planet.nixos.org";
+        type = types.string;
+        description = ''
+          Link to the main page.
+        '';
+      };
+
+      ownerName = mkOption {
+        default = "Rok Garbas";
+        type = types.string;
+        description = ''
+          Your name.
+        '';
+      };
+
+      ownerEmail = mkOption {
+        default = "some@example.com";
+        type = types.string;
+        description = ''
+          Your e-mail address.
+        '';
+      };
+
+      outputTheme = mkOption {
+        default = "${pkgs.venus}/themes/classic_fancy";
+        type = types.path;
+        description = ''
+          Directory containing a config.ini file which is merged with this one.
+          This is typically used to specify templating and bill of material
+          information.
+        '';
+      };
+
+      outputDirectory = mkOption {
+        type = types.path;
+        description = ''
+          Directory to place output files.
+        '';
+      };
+
+      cacheDirectory = mkOption {
+        default = "/var/cache/venus";
+        type = types.path;
+        description = ''
+            Where cached feeds are stored.
+        '';
+      };
+
+      itemsPerPage = mkOption {
+        default = 15;
+        type = types.int;
+        description = ''
+          How many items to put on each page.
+        '';
+      };
+
+      feeds = mkOption {
+        default = [];
+        example = [
+          {
+            name = "Rok Garbas";
+            feedUrl= "http://url/to/rss/feed.xml";
+            homepageUrl = "http://garbas.si";
+          }
+        ];
+        description = ''
+          List of feeds.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    system.activationScripts.venus =
+      ''
+        mkdir -p ${cfg.outputDirectory}
+        chown ${cfg.user}:${cfg.group} ${cfg.outputDirectory} -R
+        rm -rf ${cfg.cacheDirectory}/theme
+        mkdir -p ${cfg.cacheDirectory}/theme
+        cp -R ${cfg.outputTheme}/* ${cfg.cacheDirectory}/theme
+        chown ${cfg.user}:${cfg.group} ${cfg.cacheDirectory} -R
+      '';
+
+    systemd.services.venus =
+      { description = "Planet Venus, an awesome ‘river of news’ feed reader";
+        path  = [ pkgs.venus ];
+        script = "exec venus-planet ${configFile}";
+        serviceConfig.User = "${cfg.user}";
+        serviceConfig.Group = "${cfg.group}";
+        environment.OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt";
+        startOn = cfg.dates;
+      };
+
+  };
+}
diff --git a/nixos/modules/programs/virtualbox.nix b/nixos/modules/programs/virtualbox.nix
new file mode 100644
index 00000000000..340fec0496a
--- /dev/null
+++ b/nixos/modules/programs/virtualbox.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let virtualbox = config.boot.kernelPackages.virtualbox; in
+
+{
+  boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ];
+  boot.extraModulePackages = [ virtualbox ];
+  environment.systemPackages = [ virtualbox ];
+
+  users.extraGroups.vboxusers.gid = config.ids.gids.vboxusers;
+
+  services.udev.extraRules =
+    ''
+      KERNEL=="vboxdrv",    OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd"
+      KERNEL=="vboxnetctl", OWNER="root", GROUP="root",      MODE="0600", TAG+="systemd"
+      SUBSYSTEM=="usb_device", ACTION=="add", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}"
+      SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}"
+      SUBSYSTEM=="usb_device", ACTION=="remove", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor"
+      SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor"
+    '';
+
+  # Since we lack the right setuid binaries, set up a host-only network by default.
+
+  systemd.services."vboxnet0" =
+    { description = "VirtualBox vboxnet0 Interface";
+      requires = [ "dev-vboxnetctl.device" ];
+      after = [ "dev-vboxnetctl.device" ];
+      wantedBy = [ "network.target" "sys-subsystem-net-devices-vboxnet0.device" ];
+      path = [ virtualbox ];
+      serviceConfig.RemainAfterExit = true;
+      serviceConfig.Type = "oneshot";
+      script =
+        ''
+          if ! [ -e /sys/class/net/vboxnet0 ]; then
+            VBoxManage hostonlyif create
+          fi
+        '';
+      postStop =
+        ''
+          VBoxManage hostonlyif remove vboxnet0
+        '';
+    };
+
+  networking.interfaces.vboxnet0 = { ipAddress = "192.168.56.1"; prefixLength = 24; };
+}
diff --git a/nixos/modules/programs/wvdial.nix b/nixos/modules/programs/wvdial.nix
new file mode 100644
index 00000000000..da3f7dce98a
--- /dev/null
+++ b/nixos/modules/programs/wvdial.nix
@@ -0,0 +1,71 @@
+# Global configuration for wvdial.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  configFile = ''
+    [Dialer Defaults]
+    PPPD PATH = ${pkgs.ppp}/sbin/pppd
+    ${config.environment.wvdial.dialerDefaults}
+  '';
+
+  cfg = config.environment.wvdial;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    environment.wvdial = {
+
+      dialerDefaults = mkOption {
+        default = "";
+        type = types.string;
+        example = ''Init1 = AT+CGDCONT=1,"IP","internet.t-mobile"'';
+        description = ''
+          Contents of the "Dialer Defaults" section of
+          <filename>/etc/wvdial.conf</filename>.
+        '';
+      };
+
+      pppDefaults = mkOption {
+        default = ''
+          noipdefault
+          usepeerdns
+          defaultroute
+          persist
+          noauth
+        '';
+        type = types.string;
+        description = "Default ppp settings for wvdial.";
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (cfg.dialerDefaults != "") {
+
+    environment = {
+
+      etc =
+      [
+        { source = pkgs.writeText "wvdial.conf" configFile;
+          target = "wvdial.conf";
+        }
+        { source = pkgs.writeText "wvdial" cfg.pppDefaults;
+          target = "ppp/peers/wvdial";
+        }
+      ];
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/programs/zsh/zinputrc b/nixos/modules/programs/zsh/zinputrc
new file mode 100644
index 00000000000..6121f3e21f1
--- /dev/null
+++ b/nixos/modules/programs/zsh/zinputrc
@@ -0,0 +1,42 @@
+# Stolen from ArchWiki
+
+# create a zkbd compatible hash;
+# to add other keys to this hash, see: man 5 terminfo
+typeset -A key
+
+key[Home]=${terminfo[khome]}
+
+key[End]=${terminfo[kend]}
+key[Insert]=${terminfo[kich1]}
+key[Delete]=${terminfo[kdch1]}
+key[Up]=${terminfo[kcuu1]}
+key[Down]=${terminfo[kcud1]}
+key[Left]=${terminfo[kcub1]}
+key[Right]=${terminfo[kcuf1]}
+key[PageUp]=${terminfo[kpp]}
+key[PageDown]=${terminfo[knp]}
+
+# setup key accordingly
+[[ -n "${key[Home]}"     ]]  && bindkey  "${key[Home]}"     beginning-of-line
+[[ -n "${key[End]}"      ]]  && bindkey  "${key[End]}"      end-of-line
+[[ -n "${key[Insert]}"   ]]  && bindkey  "${key[Insert]}"   overwrite-mode
+[[ -n "${key[Delete]}"   ]]  && bindkey  "${key[Delete]}"   delete-char
+[[ -n "${key[Up]}"       ]]  && bindkey  "${key[Up]}"       up-line-or-history
+[[ -n "${key[Down]}"     ]]  && bindkey  "${key[Down]}"     down-line-or-history
+[[ -n "${key[Left]}"     ]]  && bindkey  "${key[Left]}"     backward-char
+[[ -n "${key[Right]}"    ]]  && bindkey  "${key[Right]}"    forward-char
+[[ -n "${key[PageUp]}"   ]]  && bindkey  "${key[PageUp]}"   beginning-of-buffer-or-history
+[[ -n "${key[PageDown]}" ]]  && bindkey  "${key[PageDown]}" end-of-buffer-or-history
+
+# Finally, make sure the terminal is in application mode, when zle is
+# active. Only then are the values from $terminfo valid.
+if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
+    function zle-line-init () {
+        printf '%s' "${terminfo[smkx]}"
+    }
+    function zle-line-finish () {
+        printf '%s' "${terminfo[rmkx]}"
+    }
+    zle -N zle-line-init
+    zle -N zle-line-finish
+fi
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
new file mode 100644
index 00000000000..cff751934d7
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -0,0 +1,180 @@
+# This module defines global configuration for the zshell.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.zsh;
+
+  zshAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k}='${v}'") cfg.shellAliases
+  );
+
+in
+
+{
+
+  options = {
+
+    programs.zsh = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whenever to configure Zsh as an interactive shell.
+          Note that this tries to make Zsh the default
+          <option>users.defaultUserShell</option>,
+          which in turn means that you might need to explicitly
+          set this variable if you have another shell configured
+          with NixOS.
+        '';
+        type = types.bool;
+      };
+
+      shellAliases = mkOption {
+        default = config.environment.shellAliases;
+        description = ''
+          Set of aliases for zsh shell. See <option>environment.shellAliases</option>
+          for an option format description.
+        '';
+        type = types.attrs; # types.attrsOf types.stringOrPath;
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh login shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = ''
+          autoload -U promptinit && promptinit && prompt walters
+        '';
+        description = ''
+          Shell script code used to initialise the zsh prompt.
+        '';
+        type = types.lines;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.zsh = {
+
+      shellInit = ''
+        . ${config.system.build.setEnvironment}
+
+        ${cfge.shellInit}
+      '';
+
+      loginShellInit = cfge.loginShellInit;
+
+      interactiveShellInit = ''
+        ${cfge.interactiveShellInit}
+
+        ${cfg.promptInit}
+        ${zshAliases}
+
+        # Some sane history defaults
+        export SAVEHIST=2000
+        export HISTSIZE=2000
+        export HISTFILE=$HOME/.zsh_history
+
+        setopt HIST_IGNORE_DUPS SHARE_HISTORY
+      '';
+
+    };
+
+    environment.etc."zshenv".text =
+      ''
+        # /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for all shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
+        __ETC_ZSHENV_SOURCED=1
+
+        ${cfg.shellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zshenv.local; then
+          . /etc/zshenv.local
+        fi
+      '';
+
+    environment.etc."zprofile".text =
+      ''
+        # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for login shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi
+        __ETC_ZPROFILE_SOURCED=1
+
+        ${cfg.loginShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zprofile.local; then
+          . /etc/zprofile.local
+        fi
+      '';
+
+    environment.etc."zshrc".text =
+      ''
+        # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for interactive shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
+        __ETC_ZSHRC_SOURCED=1
+
+        . /etc/zinputrc
+
+        ${cfg.interactiveShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zshrc.local; then
+          . /etc/zshrc.local
+        fi
+      '';
+
+    environment.etc."zinputrc".source = ./zinputrc;
+
+    environment.systemPackages = [ pkgs.zsh ];
+
+    users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
+
+    environment.shells =
+      [ "/run/current-system/sw/bin/zsh"
+        "/var/run/current-system/sw/bin/zsh"
+        "${pkgs.zsh}/bin/zsh"
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
new file mode 100644
index 00000000000..631283ce219
--- /dev/null
+++ b/nixos/modules/rename.nix
@@ -0,0 +1,115 @@
+{pkgs, options, config, ...}:
+
+let
+
+  alias = from: to: {
+    name = "Alias";
+    msg.use = x: x;
+    msg.define = x: x;
+  };
+
+  obsolete = from: to: {
+    name = "Obsolete name";
+    msg.use = x:
+      builtins.trace "Obsolete option `${from}' is used instead of `${to}'." x;
+    msg.define = x:
+      builtins.trace "Obsolete option `${from}' is defined instead of `${to}'." x;
+  };
+
+  deprecated = from: to: {
+    name = "Deprecated name";
+    msg.use = x:
+      abort "Deprecated option `${from}' is used instead of `${to}'.";
+    msg.define = x:
+      abort "Deprecated option `${from}' is defined instead of `${to}'.";
+  };
+
+
+  zipModules = list: with pkgs.lib;
+    zipAttrsWith (n: v:
+      if tail v != [] then
+        if n == "_type" then (head v)
+        else if n == "extraConfigs" then (concatLists v)
+        else if n == "description" || n == "apply" then
+          abort "Cannot rename an option to multiple options."
+        else zipModules v
+      else head v
+    ) list;
+
+  rename = statusTemplate: from: to: with pkgs.lib;
+    let
+      status = statusTemplate from to;
+      setTo = setAttrByPath (splitString "." to);
+      setFrom = setAttrByPath (splitString "." from);
+      toOf = attrByPath (splitString "." to)
+        (abort "Renaming error: option `${to}' does not exists.");
+      fromOf = attrByPath (splitString "." from)
+        (abort "Internal error: option `${from}' should be declared.");
+    in
+      [{
+        options = setFrom (mkOption {
+          description = "${status.name} of <option>${to}</option>.";
+          apply = x: status.msg.use (toOf config);
+        });
+      }] ++
+      [{
+        options = setTo (mkOption {
+          extraConfigs =
+            let externalDefs = (fromOf options).definitions; in
+            if externalDefs == [] then []
+            else map (def: def.value) (status.msg.define externalDefs);
+        });
+      }];
+
+in zipModules ([]
+
+# usage example:
+# ++ rename alias "services.xserver.slim.theme" "services.xserver.displayManager.slim.theme"
+++ rename obsolete "environment.extraPackages" "environment.systemPackages"
+++ rename obsolete "environment.enableBashCompletion" "programs.bash.enableCompletion"
+
+++ rename obsolete "security.extraSetuidPrograms" "security.setuidPrograms"
+++ rename obsolete "networking.enableWLAN" "networking.wireless.enable"
+++ rename obsolete "networking.enableRT73Firmware" "networking.enableRalinkFirmware"
+
+# FIXME: Remove these eventually.
+++ rename obsolete "boot.systemd.sockets" "systemd.sockets"
+++ rename obsolete "boot.systemd.targets" "systemd.targets"
+++ rename obsolete "boot.systemd.services" "systemd.services"
+
+# Old Grub-related options.
+++ rename obsolete "boot.copyKernels" "boot.loader.grub.copyKernels"
+++ rename obsolete "boot.extraGrubEntries" "boot.loader.grub.extraEntries"
+++ rename obsolete "boot.extraGrubEntriesBeforeNixos" "boot.loader.grub.extraEntriesBeforeNixOS"
+++ rename obsolete "boot.grubDevice" "boot.loader.grub.device"
+++ rename obsolete "boot.bootMount" "boot.loader.grub.bootDevice"
+++ rename obsolete "boot.grubSplashImage" "boot.loader.grub.splashImage"
+
+++ rename obsolete "boot.initrd.extraKernelModules" "boot.initrd.kernelModules"
+
+# OpenSSH
+++ rename obsolete "services.sshd.ports" "services.openssh.ports"
+++ rename alias "services.sshd.enable" "services.openssh.enable"
+++ rename obsolete "services.sshd.allowSFTP" "services.openssh.allowSFTP"
+++ rename obsolete "services.sshd.forwardX11" "services.openssh.forwardX11"
+++ rename obsolete "services.sshd.gatewayPorts" "services.openssh.gatewayPorts"
+++ rename obsolete "services.sshd.permitRootLogin" "services.openssh.permitRootLogin"
+++ rename obsolete "services.xserver.startSSHAgent" "services.xserver.startOpenSSHAgent"
+
+# KDE
+++ rename deprecated "kde.extraPackages" "environment.kdePackages"
+# ++ rename obsolete "environment.kdePackages" "environment.systemPackages" # !!! doesn't work!
+
+# Multiple efi bootloaders now
+++ rename obsolete "boot.loader.efiBootStub.efiSysMountPoint" "boot.loader.efi.efiSysMountPoint"
+++ rename obsolete "boot.loader.efiBootStub.efiDisk" "boot.loader.efi.efibootmgr.efiDisk"
+++ rename obsolete "boot.loader.efiBootStub.efiPartition" "boot.loader.efi.efibootmgr.efiPartition"
+++ rename obsolete "boot.loader.efiBootStub.postEfiBootMgrCommands" "boot.loader.efi.efibootmgr.postEfiBootMgrCommands"
+++ rename obsolete "boot.loader.efiBootStub.runEfibootmgr" "boot.loader.efi.canTouchEfiVariables"
+++ rename obsolete "boot.loader.efi.efibootmgr.enable" "boot.loader.efi.canTouchEfiVariables"
+
+# NixOS environment changes
+# !!! this hardcodes bash, could we detect from config which shell is actually used?
+++ rename obsolete "environment.promptInit" "programs.bash.promptInit"
+
+) # do not add renaming after this.
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
new file mode 100644
index 00000000000..bc661164fdc
--- /dev/null
+++ b/nixos/modules/security/apparmor-suid.nix
@@ -0,0 +1,46 @@
+{pkgs, config, ...}:
+let
+  cfg = config.security.apparmor;
+in
+with pkgs.lib;
+{
+
+  options.security.apparmor.confineSUIDApplications = mkOption {
+    default = true;
+    description = ''
+      Install AppArmor profiles for commonly-used SUID application
+      to mitigate potential privilege escalation attacks due to bugs
+      in such applications.
+
+      Currently available profiles: ping
+    '';
+  };
+
+  config = mkIf (cfg.confineSUIDApplications) {
+    security.apparmor.profiles = [ (pkgs.writeText "ping" ''
+      #include <tunables/global>
+      /var/setuid-wrappers/ping {
+        #include <abstractions/base>
+        #include <abstractions/consoles>
+        #include <abstractions/nameservice>
+
+        capability net_raw,
+        capability setuid,
+        network inet raw,
+
+        ${pkgs.glibc}/lib/*.so mr,
+        ${pkgs.libcap}/lib/libcap.so* mr,
+        ${pkgs.attr}/lib/libattr.so* mr,
+
+        ${pkgs.iputils}/bin/ping mixr,
+        /var/setuid-wrappers/ping.real r,
+
+        #/etc/modules.conf r,
+
+        ## Site-specific additions and overrides. See local/README for details.
+        ##include <local/bin.ping>
+      }
+    '') ];
+  };
+
+}
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
new file mode 100644
index 00000000000..d4aa0598dd3
--- /dev/null
+++ b/nixos/modules/security/apparmor.nix
@@ -0,0 +1,68 @@
+{pkgs, config, ...}:
+
+let
+  cfg = config.security.apparmor;
+in
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.apparmor = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable AppArmor application security system. Enable only if
+          you want to further improve AppArmor.
+        '';
+      };
+
+      profiles = mkOption {
+        default = [];
+        merge = mergeListOption;
+        description = ''
+          List of file names of 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"; }
+                 ];
+
+    environment.systemPackages = [ pkgs.apparmor ];
+
+    systemd.services.apparmor = {
+      #wantedBy = [ "basic.target" ];
+      wantedBy = [ "local-fs.target" ];
+      path = [ pkgs.apparmor ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = "yes";
+        ExecStart = concatMapStrings (profile:
+          ''${pkgs.apparmor}/sbin/apparmor_parser -rKv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; ''
+        ) cfg.profiles;
+        ExecStop = concatMapStrings (profile:
+          ''${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
new file mode 100644
index 00000000000..2e93fb36b45
--- /dev/null
+++ b/nixos/modules/security/ca.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  config = {
+
+    environment.etc =
+      [ { 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";
+    environment.variables.CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt";
+    environment.variables.GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt";
+
+  };
+
+}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
new file mode 100644
index 00000000000..3ef01ea2c17
--- /dev/null
+++ b/nixos/modules/security/pam.nix
@@ -0,0 +1,286 @@
+# This module provides configuration for the PAM (Pluggable
+# Authentication Modules) system.
+
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) pam_krb5 pam_ccreds;
+
+  pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
+
+  otherService = pkgs.writeText "other.pam"
+    ''
+      auth     required pam_warn.so
+      auth     required pam_deny.so
+      account  required pam_warn.so
+      account  required pam_deny.so
+      password required pam_warn.so
+      password required pam_deny.so
+      session  required pam_warn.so
+      session  required pam_deny.so
+    '';
+
+  # Create a limits.conf(5) file.
+  makeLimitsConf = limits:
+    pkgs.writeText "limits.conf"
+      (concatStringsSep "\n"
+           (map ({ domain, type, item, value }:
+                 concatStringsSep " " [ domain type item value ])
+                limits));
+
+  motd = pkgs.writeText "motd" config.users.motd;
+
+  makePAMService =
+    { name
+    , # If set, root doesn't need to authenticate (e.g. for the "chsh"
+      # service).
+      rootOK ? false
+    , # If set, user listed in /etc/pamusb.conf are able to log in with
+      # the associated usb key.
+      usbAuth ? config.security.pam.usb.enable
+    , # If set, OTPW system will be used (if ~/.otpw exists)
+      otpwAuth ? config.security.pam.enableOTPW
+    , # If set, the calling user's SSH agent is used to authenticate
+      # against the keys in the calling user's ~/.ssh/authorized_keys.
+      # This is useful for "sudo" on password-less remote systems.
+      sshAgentAuth ? false
+    , # If set, the service will register a new session with systemd's
+      # login manager.  If the service is running locally, this will
+      # give the user ownership of audio devices etc.
+      startSession ? false
+    , # Set the login uid of the process (/proc/self/loginuid) for
+      # auditing purposes.  The login uid is only set by "entry
+      # points" like login and sshd, not by commands like sudo.
+      setLoginUid ? startSession
+    , # Whether to forward XAuth keys between users.  Mostly useful
+      # for "su".
+      forwardXAuth ? false
+    , # Whether to allow logging into accounts that have no password
+      # set (i.e., have an empty password field in /etc/passwd or
+      # /etc/group).  This does not enable logging into disabled
+      # accounts (i.e., that have the password field set to `!').
+      # Note that regardless of what the pam_unix documentation says,
+      # accounts with hashed empty passwords are always allowed to log
+      # in.
+      allowNullPassword ? false
+    , # The limits, as per limits.conf(5).
+      limits ? config.security.pam.loginLimits
+    , # Whether to show the message of the day.
+      showMotd ? false
+    , # Whether to update /var/log/wtmp.
+      updateWtmp ? false
+    }:
+
+    { source = pkgs.writeText "${name}.pam"
+        # !!! TODO: move the LDAP stuff to the LDAP module, and the
+        # Samba stuff to the Samba module.  This requires that the PAM
+        # module provides the right hooks.
+        ''
+          # Account management.
+          account sufficient pam_unix.so
+          ${optionalString config.users.ldap.enable
+              "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
+
+          # Authentication management.
+          ${optionalString rootOK
+              "auth sufficient pam_rootok.so"}
+          ${optionalString (config.security.pam.enableSSHAgentAuth && sshAgentAuth)
+              "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"}
+          ${optionalString usbAuth
+              "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
+          auth sufficient pam_unix.so ${optionalString allowNullPassword "nullok"} likeauth
+          ${optionalString otpwAuth
+              "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
+          ${optionalString config.users.ldap.enable
+              "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
+          ${optionalString config.krb5.enable ''
+            auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
+            auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
+            auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
+          ''}
+          auth required   pam_deny.so
+
+          # Password management.
+          password requisite pam_unix.so nullok sha512
+          ${optionalString config.users.ldap.enable
+              "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
+          ${optionalString config.services.samba.syncPasswordsByPam
+              "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"}
+
+          # Session management.
+          session required pam_unix.so
+          ${optionalString updateWtmp
+              "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
+          ${optionalString config.users.ldap.enable
+              "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
+          ${optionalString otpwAuth
+              "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
+          ${optionalString startSession
+              "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
+          ${optionalString setLoginUid
+              "session required pam_loginuid.so"}
+          ${optionalString forwardXAuth
+              "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
+          ${optionalString (limits != [])
+              "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf limits}"}
+          ${optionalString (showMotd && config.users.motd != null)
+              "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
+        '';
+      target = "pam.d/${name}";
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.pam.loginLimits = mkOption {
+      default = [];
+      example =
+        [ { domain = "ftp";
+            type   = "hard";
+            item   = "nproc";
+            value  = "0";
+          }
+          { domain = "@student";
+            type   = "-";
+            item   = "maxlogins";
+            value  = "4";
+          }
+       ];
+
+     description =
+       '' Define resource limits that should apply to users or groups.
+          Each item in the list should be an attribute set with a
+          <varname>domain</varname>, <varname>type</varname>,
+          <varname>item</varname>, and <varname>value</varname>
+          attribute.  The syntax and semantics of these attributes
+          must be that described in the limits.conf(5) man page.
+       '';
+    };
+
+    security.pam.services = mkOption {
+      default = [];
+      example = [
+        { name = "chsh"; rootOK = true; }
+        { name = "login"; startSession = true; allowNullPassword = true;
+          limits = [
+            { domain = "ftp";
+              type   = "hard";
+              item   = "nproc";
+              value  = "0";
+            }
+          ];
+        }
+      ];
+
+      description =
+        ''
+          This option defines the PAM services.  A service typically
+          corresponds to a program that uses PAM,
+          e.g. <command>login</command> or <command>passwd</command>.
+          Each element of this list is an attribute set describing a
+          service.  The attribute <varname>name</varname> specifies
+          the name of the service.  The attribute
+          <varname>rootOK</varname> specifies whether the root user is
+          allowed to use this service without authentication.  The
+          attribute <varname>startSession</varname> specifies whether
+          systemd's PAM connector module should be used to start a new
+          session; for local sessions, this will give the user
+          ownership of devices such as audio and CD-ROM drives.  The
+          attribute <varname>forwardXAuth</varname> specifies whether
+          X authentication keys should be passed from the calling user
+          to the target user (e.g. for <command>su</command>).
+
+          The attribute <varname>limits</varname> defines resource limits
+          that should apply to users or groups for the service.  Each item in
+          the list should be an attribute set with a
+          <varname>domain</varname>, <varname>type</varname>,
+          <varname>item</varname>, and <varname>value</varname> attribute.
+          The syntax and semantics of these attributes must be that described
+          in the limits.conf(5) man page.
+        '';
+    };
+
+    security.pam.enableSSHAgentAuth = mkOption {
+      default = false;
+      description =
+        ''
+          Enable sudo logins if the user's SSH agent provides a key
+          present in <filename>~/.ssh/authorized_keys</filename>.
+          This allows machines to exclusively use SSH keys instead of
+          passwords.
+        '';
+    };
+
+    security.pam.enableOTPW = mkOption {
+      default = false;
+      description = ''
+        Enable the OTPW (one-time password) PAM module
+      '';
+    };
+
+    users.motd = mkOption {
+      default = null;
+      example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
+      type = types.nullOr types.string;
+      description = "Message of the day shown to users when they log in.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages =
+      # Include the PAM modules in the system path mostly for the manpages.
+      [ pkgs.pam ]
+      ++ optional config.users.ldap.enable pam_ldap
+      ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
+      ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ];
+
+    environment.etc =
+      map makePAMService config.security.pam.services
+      ++ singleton
+        { source = otherService;
+          target = "pam.d/other";
+        };
+
+    security.setuidOwners = [ {
+      program = "unix_chkpwd";
+      source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
+      owner = "root";
+      setuid = true;
+    } ];
+
+    security.pam.services =
+      # Most of these should be moved to specific modules.
+      [ { name = "cups"; }
+        { name = "ejabberd"; }
+        { name = "ftp"; }
+        { name = "i3lock"; }
+        { name = "lshd"; }
+        { name = "samba"; }
+        { name = "screen"; }
+        { name = "vlock"; }
+        { name = "xlock"; }
+        { name = "xscreensaver"; }
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/security/pam_usb.nix b/nixos/modules/security/pam_usb.nix
new file mode 100644
index 00000000000..1c2a6a05f26
--- /dev/null
+++ b/nixos/modules/security/pam_usb.nix
@@ -0,0 +1,41 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) pam_usb;
+
+  cfg = config.security.pam.usb;
+
+  anyUsbAuth = any (attrByPath ["usbAuth"] false) config.security.pam.services;
+
+in
+
+{
+  options = {
+
+    security.pam.usb = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable USB login for all login system unless the service disabled
+          it.  For more information, visit <link
+          xlink:href="http://pamusb.org/doc/quickstart#setting_up" />.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf (cfg.enable || anyUsbAuth) {
+
+    # pmount need to have a set-uid bit to make pam_usb works in user
+    # environment. (like su, sudo)
+
+    security.setuidPrograms = [ "pmount" "pumount" ];
+    environment.systemPackages = [ pkgs.pmount ];
+
+  };
+}
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
new file mode 100644
index 00000000000..b9b32496a36
--- /dev/null
+++ b/nixos/modules/security/polkit.nix
@@ -0,0 +1,121 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.security.polkit;
+
+in
+
+{
+
+  options = {
+
+    security.polkit.enable = mkOption {
+      default = true;
+      description = "Whether to enable PolKit.";
+    };
+
+    security.polkit.permissions = mkOption {
+      default = "";
+      example =
+        ''
+          [Disallow Users To Suspend]
+          Identity=unix-group:users
+          Action=org.freedesktop.upower.*
+          ResultAny=no
+          ResultInactive=no
+          ResultActive=no
+
+          [Allow Anybody To Eject Disks]
+          Identity=unix-user:*
+          Action=org.freedesktop.udisks.drive-eject
+          ResultAny=yes
+          ResultInactive=yes
+          ResultActive=yes
+
+          [Allow Alice To Mount Filesystems After Admin Authentication]
+          Identity=unix-user:alice
+          Action=org.freedesktop.udisks.filesystem-mount
+          ResultAny=auth_admin
+          ResultInactive=auth_admin
+          ResultActive=auth_admin
+        '';
+      description =
+        ''
+          Allows the default permissions of privileged actions to be overridden.
+        '';
+    };
+
+    security.polkit.adminIdentities = mkOption {
+      default = "unix-user:0;unix-group:wheel";
+      example = "";
+      description =
+        ''
+          Specifies which users are considered “administrators”, for those
+          actions that require the user to authenticate as an
+          administrator (i.e. have an <literal>auth_admin</literal>
+          value).  By default, this is the <literal>root</literal>
+          user and all users in the <literal>wheel</literal> group.
+        '';
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.polkit ];
+
+    # The polkit daemon reads action files
+    environment.pathsToLink = [ "/share/polkit-1/actions" ];
+
+    environment.etc =
+      [ # No idea what the "null backend" is, but it seems to need this.
+        { source = "${pkgs.polkit}/etc/polkit-1/nullbackend.conf.d";
+          target = "polkit-1/nullbackend.conf.d";
+        }
+
+        # This file determines what users are considered
+        # "administrators".
+        { source = pkgs.writeText "10-nixos.conf"
+            ''
+              [Configuration]
+              AdminIdentities=${cfg.adminIdentities}
+            '';
+          target = "polkit-1/localauthority.conf.d/10-nixos.conf";
+        }
+
+        { source = pkgs.writeText "org.nixos.pkla" cfg.permissions;
+          target = "polkit-1/localauthority/10-vendor.d/org.nixos.pkla";
+        }
+      ];
+
+    services.dbus.packages = [ pkgs.polkit ];
+
+    security.pam.services = [ { name = "polkit-1"; } ];
+
+    security.setuidPrograms = [ "pkexec" ];
+
+    security.setuidOwners = singleton
+      { program = "polkit-agent-helper-1";
+        owner = "root";
+        group = "root";
+        setuid = true;
+        source = "${pkgs.polkit}/libexec/polkit-1/polkit-agent-helper-1";
+      };
+
+    system.activationScripts.polkit =
+      ''
+        mkdir -p /var/lib/polkit-1/localauthority
+        chmod 700 /var/lib/polkit-1{/localauthority,}
+
+        # Force polkitd to be restarted so that it reloads its
+        # configuration.
+        ${pkgs.procps}/bin/pkill -INT -u root -x polkitd
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
new file mode 100644
index 00000000000..dd251fe69d3
--- /dev/null
+++ b/nixos/modules/security/rngd.nix
@@ -0,0 +1,37 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+    security.rngd.enable = mkOption {
+      default = true;
+      description = ''
+        Whether to enable the rng daemon, which adds entropy from
+        hardware sources of randomness to the kernel entropy pool when
+        available.
+      '';
+    };
+  };
+
+  config = mkIf config.security.rngd.enable {
+    services.udev.extraRules = ''
+      KERNEL=="random", TAG+="systemd"
+      SUBSYSTEM=="cpu", ENV{MODALIAS}=="x86cpu:*feature:*009E*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+      KERNEL=="hw_random", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+      KERNEL=="tmp0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+    '';
+
+    systemd.services.rngd = {
+      bindsTo = [ "dev-random.device" ];
+
+      after = [ "dev-random.device" ];
+
+      description = "Hardware RNG Entropy Gatherer Daemon";
+
+      serviceConfig.ExecStart = "${pkgs.rng_tools}/sbin/rngd -f";
+
+      restartTriggers = [ pkgs.rng_tools ];
+    };
+  };
+}
diff --git a/nixos/modules/security/rtkit.nix b/nixos/modules/security/rtkit.nix
new file mode 100644
index 00000000000..e47e7baa2b8
--- /dev/null
+++ b/nixos/modules/security/rtkit.nix
@@ -0,0 +1,39 @@
+# A module for ‘rtkit’, a DBus system service that hands out realtime
+# scheduling priority to processes that ask for it.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    security.rtkit.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the RealtimeKit system service, which hands
+        out realtime scheduling priority to user processes on
+        demand. For example, the PulseAudio server uses this to
+        acquire realtime priority.
+      '';
+    };
+
+  };
+
+
+  config = mkIf config.security.rtkit.enable {
+
+    environment.systemPackages = [ pkgs.rtkit ];
+
+    services.dbus.packages = [ pkgs.rtkit ];
+
+    users.extraUsers = singleton
+      { name = "rtkit";
+        uid = config.ids.uids.rtkit;
+        description = "RealtimeKit daemon";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/security/setuid-wrapper.c b/nixos/modules/security/setuid-wrapper.c
new file mode 100644
index 00000000000..007ffbc34fe
--- /dev/null
+++ b/nixos/modules/security/setuid-wrapper.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+/* Make sure assertions are not compiled out.  */
+#undef NDEBUG
+
+extern char **environ;
+
+static char * wrapperDir = WRAPPER_DIR;
+
+int main(int argc, char * * argv)
+{
+    char self[PATH_MAX];
+
+    int len = readlink("/proc/self/exe", self, sizeof(self) - 1);
+    assert (len > 0);
+    self[len] = 0;
+
+    /* Make sure that we are being executed from the right location,
+       i.e., `wrapperDir'.  This is to prevent someone from
+       creating hard link `X' from some other location, along with a
+       false `X.real' file, to allow arbitrary programs from being
+       executed setuid.  */
+    assert ((strncmp(self, wrapperDir, sizeof(wrapperDir)) == 0) &&
+	    (self[strlen(wrapperDir)] == '/'));
+
+    /* Make *really* *really* sure that we were executed as `self',
+       and not, say, as some other setuid program.  That is, our
+       effective uid/gid should match the uid/gid of `self'. */
+    //printf("%d %d\n", geteuid(), getegid());
+
+    struct stat st;
+    assert (lstat(self, &st) != -1);
+
+    //printf("%d %d\n", st.st_uid, st.st_gid);
+    
+    assert ((st.st_mode & S_ISUID) == 0 ||
+	    (st.st_uid == geteuid()));
+
+    assert ((st.st_mode & S_ISGID) == 0 ||
+	    st.st_gid == getegid());
+
+    /* And, of course, we shouldn't be writable. */
+    assert (!(st.st_mode & (S_IWGRP | S_IWOTH)));
+
+
+    /* Read the path of the real (wrapped) program from <self>.real. */
+    char realFN[PATH_MAX + 10];
+    int realFNSize = snprintf (realFN, sizeof(realFN), "%s.real", self);
+    assert (realFNSize < sizeof(realFN));
+
+    int fdSelf = open(realFN, O_RDONLY);
+    assert (fdSelf != -1);
+
+    char real[PATH_MAX];
+    len = read(fdSelf, real, PATH_MAX);
+    assert (len != -1);
+    assert (len < sizeof (real));
+    assert (len > 0);
+    real[len] = 0;
+
+    close(fdSelf);
+    
+    //printf("real = %s, len = %d\n", real, len);
+
+    execve(real, argv, environ);
+
+    fprintf(stderr, "%s: cannot run `%s': %s\n",
+        argv[0], real, strerror(errno));
+    
+    exit(1);
+}
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
new file mode 100644
index 00000000000..e75679e5ff6
--- /dev/null
+++ b/nixos/modules/security/setuid-wrappers.nix
@@ -0,0 +1,121 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (config.security) wrapperDir;
+
+  setuidWrapper = pkgs.stdenv.mkDerivation {
+    name = "setuid-wrapper";
+    buildCommand = ''
+      ensureDir $out/bin
+      gcc -Wall -O2 -DWRAPPER_DIR=\"${wrapperDir}\" \
+          ${./setuid-wrapper.c} -o $out/bin/setuid-wrapper
+      strip -s $out/bin/setuid-wrapper
+    '';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.setuidPrograms = mkOption {
+      default = [];
+      description = ''
+        The Nix store cannot contain setuid/setgid programs directly.
+        For this reason, NixOS can automatically generate wrapper
+        programs that have the necessary privileges.  This option
+        lists the names of programs in the system environment for
+        which setuid root wrappers should be created.
+      '';
+    };
+
+    security.setuidOwners = mkOption {
+      default = [];
+      example =
+        [ { program = "sendmail";
+            owner = "nobody";
+            group = "postdrop";
+            setuid = false;
+            setgid = true;
+          }
+        ];
+      description = ''
+        This option allows the ownership and permissions on the setuid
+        wrappers for specific programs to be overridden from the
+        default (setuid root, but not setgid root).
+      '';
+    };
+
+    security.wrapperDir = mkOption {
+      default = "/var/setuid-wrappers";
+      description = ''
+        This option defines the path to the setuid wrappers.  It
+        should generally not be overriden. Some packages in Nixpkgs
+        expect that <option>wrapperDir</option> is
+        <filename>/var/setuid-wrappers</filename>.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    security.setuidPrograms =
+      [ "fusermount" "wodim" "cdrdao" "growisofs" ];
+
+    system.activationScripts.setuid =
+      let
+        setuidPrograms =
+          (map (x: { program = x; owner = "root"; group = "root"; setuid = true; })
+            config.security.setuidPrograms)
+          ++ config.security.setuidOwners;
+
+        makeSetuidWrapper =
+          { program
+          , source ? ""
+          , owner ? "nobody"
+          , group ? "nogroup"
+          , setuid ? false
+          , setgid ? false
+          , permissions ? "u+rx,g+x,o+x"
+          }:
+
+          ''
+            source=${if source != "" then source else "$(PATH=$SETUID_PATH type -tP ${program})"}
+            if test -z "$source"; then
+                # If we can't find the program, fall back to the
+                # system profile.
+                source=/nix/var/nix/profiles/default/bin/${program}
+            fi
+
+            cp ${setuidWrapper}/bin/setuid-wrapper ${wrapperDir}/${program}
+            echo -n "$source" > ${wrapperDir}/${program}.real
+            chmod 0000 ${wrapperDir}/${program} # to prevent races
+            chown ${owner}.${group} ${wrapperDir}/${program}
+            chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" ${wrapperDir}/${program}
+          '';
+
+      in stringAfter [ "users" ]
+        ''
+          # Look in the system path and in the default profile for
+          # programs to be wrapped.
+          SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin
+
+          if test -d ${wrapperDir}; then rm -f ${wrapperDir}/*; fi # */
+          mkdir -p ${wrapperDir}
+
+          ${concatMapStrings makeSetuidWrapper setuidPrograms}
+        '';
+
+  };
+
+}
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
new file mode 100644
index 00000000000..cd548f4a4fe
--- /dev/null
+++ b/nixos/modules/security/sudo.nix
@@ -0,0 +1,90 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.security.sudo;
+
+  inherit (pkgs) sudo;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.sudo.enable = mkOption {
+      default = true;
+      description =
+        ''
+          Whether to enable the <command>sudo</command> command, which
+          allows non-root users to execute commands as root.
+        '';
+    };
+
+    security.sudo.wheelNeedsPassword = mkOption {
+      default = true;
+      description =
+        ''
+          Whether users of the <code>wheel</code> group can execute
+          commands as super user without entering a password.
+        '';
+      };
+
+    security.sudo.configFile = mkOption {
+      # Note: if syntax errors are detected in this file, the NixOS
+      # configuration will fail to build.
+      description =
+        ''
+          This string contains the contents of the
+          <filename>sudoers</filename> file.
+        '';
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    security.sudo.configFile =
+      ''
+        # Don't edit this file. Set the NixOS option ‘security.sudo.configFile’ instead.
+
+        # Environment variables to keep for root and %wheel.
+        Defaults:root,%wheel env_keep+=LOCALE_ARCHIVE
+        Defaults:root,%wheel env_keep+=NIX_CONF_DIR
+        Defaults:root,%wheel env_keep+=NIX_PATH
+        Defaults:root,%wheel env_keep+=TERMINFO_DIRS
+
+        # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
+        Defaults env_keep+=SSH_AUTH_SOCK
+
+        # "root" is allowed to do anything.
+        root        ALL=(ALL) SETENV: ALL
+
+        # Users in the "wheel" group can do anything.
+        %wheel      ALL=(ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
+      '';
+
+    security.setuidPrograms = [ "sudo" "sudoedit" ];
+
+    environment.systemPackages = [ sudo ];
+
+    security.pam.services = [ { name = "sudo"; sshAgentAuth = true; } ];
+
+    environment.etc = singleton
+      { source = pkgs.writeText "sudoers-in" cfg.configFile;
+          # Make sure that the sudoers file is syntactically valid.
+          # (currently disabled - NIXOS-66)
+          #"${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out";
+        target = "sudoers";
+        mode = "0440";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/amqp/activemq/ActiveMQBroker.java b/nixos/modules/services/amqp/activemq/ActiveMQBroker.java
new file mode 100644
index 00000000000..c0f5d16ea11
--- /dev/null
+++ b/nixos/modules/services/amqp/activemq/ActiveMQBroker.java
@@ -0,0 +1,19 @@
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.broker.BrokerFactory;
+import java.net.URI;
+
+public class ActiveMQBroker {
+
+  public static void main(String[] args) throws Throwable {
+    URI uri = new URI((args.length > 0) ? args[0] : "xbean:activemq.xml");
+    BrokerService broker = BrokerFactory.createBroker(uri);
+    broker.start();
+    if (broker.waitUntilStarted()) {
+      broker.waitUntilStopped();
+    } else {
+      System.out.println("Failed starting broker");
+      System.exit(-1);
+    };
+  }
+
+}
diff --git a/nixos/modules/services/amqp/activemq/default.nix b/nixos/modules/services/amqp/activemq/default.nix
new file mode 100644
index 00000000000..915d179e699
--- /dev/null
+++ b/nixos/modules/services/amqp/activemq/default.nix
@@ -0,0 +1,131 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+
+  cfg = config.services.activemq;
+
+  activemqBroker = stdenv.mkDerivation {
+    name = "activemq-broker";
+    phases = [ "installPhase" ];
+    buildInputs = [ jdk ];
+    installPhase = ''
+      ensureDir $out/lib
+      source ${activemq}/lib/classpath.env
+      export CLASSPATH
+      ln -s "${./ActiveMQBroker.java}" ActiveMQBroker.java
+      javac -d $out/lib ActiveMQBroker.java
+    '';
+  };
+
+in {
+
+  options = {
+    services.activemq = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the Apache ActiveMQ message broker service.
+        '';
+      };
+      configurationDir = mkOption {
+        default = "${activemq}/conf";
+        description = ''
+          The base directory for ActiveMQ's configuration.
+          By default, this directory is searched for a file named activemq.xml,
+          which should contain the configuration for the broker service.
+        '';
+      };
+      configurationURI = mkOption {
+        type = types.string;
+        default = "xbean:activemq.xml";
+        description = ''
+          The URI that is passed along to the BrokerFactory to
+          set up the configuration of the ActiveMQ broker service.
+          You should not need to change this. For custom configuration,
+          set the <literal>configurationDir</literal> instead, and create
+          an activemq.xml configuration file in it.
+        '';
+      };
+      baseDir = mkOption {
+        type = types.string;
+        default = "/var/activemq";
+        description = ''
+          The base directory where ActiveMQ stores its persistent data and logs.
+          This will be overridden if you set "activemq.base" and "activemq.data"
+          in the <literal>javaProperties</literal> option. You can also override
+          this in activemq.xml.
+        '';
+      };
+      javaProperties = mkOption {
+        type = types.attrs;
+        default = { };
+        example = {
+          "java.net.preferIPv4Stack" = "true";
+        };
+        apply = attrs: {
+          "activemq.base" = "${cfg.baseDir}";
+          "activemq.data" = "${cfg.baseDir}/data";
+          "activemq.conf" = "${cfg.configurationDir}";
+          "activemq.home" = "${activemq}";
+        } // attrs;
+        description = ''
+          Specifies Java properties that are sent to the ActiveMQ
+          broker service with the "-D" option. You can set properties
+          here to change the behaviour and configuration of the broker.
+          All essential properties that are not set here are automatically
+          given reasonable defaults.
+        '';
+      };
+      extraJavaOptions = mkOption {
+        type = types.string;
+        default = "";
+        example = "-Xmx2G -Xms2G -XX:MaxPermSize=512M";
+        description = ''
+          Add extra options here that you want to be sent to the
+          Java runtime when the broker service is started.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.activemq = {
+      description = "ActiveMQ server user";
+      group = "activemq";
+      uid = config.ids.uids.activemq;
+    };
+
+    users.extraGroups.activemq.gid = config.ids.gids.activemq;
+
+    systemd.services.activemq_init = {
+      wantedBy = [ "activemq.service" ];
+      partOf = [ "activemq.service" ];
+      before = [ "activemq.service" ];
+      serviceConfig.Type = "oneshot";
+      script = ''
+        mkdir -p "${cfg.javaProperties."activemq.data"}"
+        chown -R activemq "${cfg.javaProperties."activemq.data"}"
+      '';
+    };
+
+    systemd.services.activemq = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = [ jre ];
+      serviceConfig.User = "activemq";
+      script = ''
+        source ${activemq}/lib/classpath.env
+        export CLASSPATH=${activemqBroker}/lib:${cfg.configurationDir}:$CLASSPATH
+        exec java \
+          ${concatStringsSep " \\\n" (mapAttrsToList (name: value: "-D${name}=${value}") cfg.javaProperties)} \
+          ${cfg.extraJavaOptions} ActiveMQBroker "${cfg.configurationURI}"
+      '';
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/amqp/rabbitmq.nix b/nixos/modules/services/amqp/rabbitmq.nix
new file mode 100644
index 00000000000..696b5ad4379
--- /dev/null
+++ b/nixos/modules/services/amqp/rabbitmq.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.rabbitmq;
+
+  run = cmd: "${pkgs.sudo}/bin/sudo -E -u rabbitmq ${cmd}";
+
+in
+
+{
+
+
+  ###### interface
+
+  options = {
+
+    services.rabbitmq = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the RabbitMQ server, an Advanced Message
+          Queuing Protocol (AMQP) broker.
+        '';
+      };
+
+      listenAddress = mkOption {
+        default = "127.0.0.1";
+        example = "";
+        description = ''
+          IP address on which RabbitMQ will listen for AMQP
+          connections.  Set to the empty string to listen on all
+          interfaces.  Note that RabbitMQ creates a user named
+          <literal>guest</literal> with password
+          <literal>guest</literal> by default, so you should delete
+          this user if you intend to allow external access.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.rabbitmq_server ];
+
+    users.extraUsers.rabbitmq = {
+      description = "RabbitMQ server user";
+      home = "/var/empty";
+      group = "rabbitmq";
+      uid = config.ids.uids.rabbitmq;
+    };
+
+    users.extraGroups.rabbitmq.gid = config.ids.gids.rabbitmq;
+
+    jobs.rabbitmq = {
+        description = "RabbitMQ server";
+
+        startOn = "started network-interfaces";
+
+        preStart =
+          ''
+            mkdir -m 0700 -p /var/lib/rabbitmq
+            chown rabbitmq /var/lib/rabbitmq
+
+            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"}
+          '';
+
+        preStop =
+          ''
+            ${run "${pkgs.rabbitmq_server}/sbin/rabbitmqctl stop"}
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix
new file mode 100644
index 00000000000..6c53ef46ab9
--- /dev/null
+++ b/nixos/modules/services/audio/alsa.nix
@@ -0,0 +1,67 @@
+# ALSA sound support.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) alsaUtils;
+
+  soundState = "/var/lib/alsa/asound.state";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    sound = {
+
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whether to enable ALSA sound.
+        '';
+        merge = mergeEnableOption;
+      };
+
+      enableOSSEmulation = mkOption {
+        default = true;
+        description = ''
+          Whether to enable ALSA OSS emulation (with certain cards sound mixing may not work!).
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.sound.enable {
+
+    environment.systemPackages = [ alsaUtils ];
+
+    # ALSA provides a udev rule for restoring volume settings.
+    services.udev.packages = [ alsaUtils ];
+
+    boot.kernelModules = optional config.sound.enableOSSEmulation "snd_pcm_oss";
+
+    systemd.services."alsa-store" =
+      { description = "Store Sound Card State";
+        wantedBy = [ "multi-user.target" ];
+        unitConfig.RequiresMountsFor = "/var/lib/alsa";
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa";
+          ExecStop = "${alsaUtils}/sbin/alsactl store --ignore";
+        };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/audio/fuppes.nix b/nixos/modules/services/audio/fuppes.nix
new file mode 100644
index 00000000000..df73e62fc94
--- /dev/null
+++ b/nixos/modules/services/audio/fuppes.nix
@@ -0,0 +1,114 @@
+{config, pkgs, ...}:
+
+let
+  cfg = config.services.fuppesd;
+in
+
+with pkgs.lib;
+
+{
+  options = {
+    services.fuppesd = {
+      enable = mkOption {
+        default = false;
+        type = with types; bool;
+        description = ''
+          Enables Fuppes (UPnP A/V Media Server).  Can be used to watch
+          photos, video and listen to music from a phone/tv connected to the
+          local network.
+        '';
+      };
+
+      name = mkOption {
+        example = "Media Center";
+        type = with types; uniq string;
+        description = ''
+          Enables Fuppes (UPnP A/V Media Server).  Can be used to watch
+          photos, video and listen to music from a phone/tv connected to the
+          local network.
+        '';
+      };
+
+      log = {
+        level = mkOption {
+          default = 0;
+          example = 3;
+          type = with types; uniq int;
+          description = ''
+            Logging level of fuppes, An integer between 0 and 3.
+          '';
+        };
+
+        file = mkOption {
+          default = "/var/log/fuppes.log";
+          type = with types; uniq string;
+          description = ''
+            File which will contains the log produced by the daemon.
+          '';
+        };
+      };
+
+      config = mkOption {
+        example = "/etc/fuppes/fuppes.cfg";
+        type = with types; uniq string;
+        description = ''
+          Mutable configuration file which can be edited with the web
+          interface.  Due to possible modification, double quote the full
+          path of the filename stored in your filesystem to avoid attempts
+          to modify the content of the nix store.
+        '';
+      };
+
+      vfolder = mkOption {
+        default = ./fuppes/vfolder.cfg;
+        example = /etc/fuppes/vfolder.cfg;
+        description = ''
+          XML file describing the layout of virtual folder visible by the
+          client.
+        '';
+      };
+
+      database = mkOption {
+        default = "/var/lib/fuppes/fuppes.db";
+        type = with types; uniq string;
+        description = ''
+          Database file which index all shared files.
+        '';
+      };
+
+      ## At the moment, no plugins are packaged.
+      /*
+      plugins = mkOption {
+        type = with types; listOf package;
+        description = ''
+          List of Fuppes plugins.
+        '';
+      };
+      */
+
+      user = mkOption {
+        default = "root"; # The default is not secure.
+        example = "fuppes";
+        type = with types; uniq string;
+        description = ''
+          Name of the user which own the configuration files and under which
+          the fuppes daemon will be executed.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    jobs.fuppesd = {
+      description = "UPnP A/V Media Server. (${cfg.name})";
+      startOn = "ip-up";
+      daemonType = "fork";
+      exec = ''/var/setuid-wrappers/sudo -u ${cfg.user} -- ${pkgs.fuppes}/bin/fuppesd --friendly-name ${cfg.name} --log-level ${toString cfg.log.level} --log-file ${cfg.log.file} --config-file ${cfg.config} --vfolder-config-file ${cfg.vfolder} --database-file ${cfg.database}'';
+    };
+
+    services.fuppesd.name = mkDefault config.networking.hostName;
+
+    security.sudo.enable = true;
+  };
+}
diff --git a/nixos/modules/services/audio/fuppes/vfolder.cfg b/nixos/modules/services/audio/fuppes/vfolder.cfg
new file mode 100644
index 00000000000..35ec3bffeb0
--- /dev/null
+++ b/nixos/modules/services/audio/fuppes/vfolder.cfg
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fuppes_vfolder_config version="0.2">
+
+ <vfolder_layout device="default" enabled="false">
+
+    <vfolder name="Genre">
+      <vfolders property="genre">
+        <items type="audioItem" />
+      </vfolders>
+    </vfolder>
+
+    <vfolder name="Genre/Artists">
+      <vfolders property="genre">
+        <vfolders property="artist">
+          <items type="audioItem" />
+        </vfolders>
+      </vfolders>
+    </vfolder>
+
+    <vfolder name="Artists/Albums">
+      <vfolders property="artist">
+        <vfolders property="album">
+          <items type="audioItem" />
+        </vfolders>
+      </vfolders>
+    </vfolder> 
+    
+    <vfolder name="ABC/Artists/Albums">
+      <vfolders split="ABC">
+        <vfolders property="artist">
+          <vfolders property="album">
+            <items type="audioItem" />
+          </vfolders>
+        </vfolders>
+      </vfolders>
+    </vfolder>
+       
+    <vfolder name="Photos">
+      <vfolder name="All">
+        <items type="imageItem" />
+      </vfolder>
+      <vfolder name="Folders">
+        <folders filter="contains(imageItem)" />
+      </vfolder>      
+    </vfolder>
+
+    <vfolder name="Videos">
+      <vfolder name="All">
+        <items type="videoItem" />
+      </vfolder>
+      <vfolder name="Folders">
+        <folders filter="contains(videoItem)" />
+      </vfolder>
+    </vfolder>
+    
+    <vfolder name="shared dirs">
+      <shared_dirs full_extend="true" />
+    </vfolder>
+    
+  </vfolder_layout>
+
+  <vfolder_layout device="Xbox 360" enabled="false">
+
+    <vfolder name="Music" id="1">
+      <vfolder name="Album" id="7">
+        <vfolders property="album">
+          <items type="audioItem" />
+        </vfolders>
+      </vfolder>
+            
+      <vfolder name="All Music" id="4">
+        <items type="audioItem" />
+      </vfolder>
+      
+      <vfolder name="Artist" id="6">
+        <vfolders property="artist">
+          <items type="audioItem" />
+        </vfolders>
+      </vfolder>
+      
+      <vfolder name="Folders" id="20">
+        <folders filter="contains(audioItem)" />
+      </vfolder>
+      
+      <vfolder name="Genre" id="5">
+        <vfolders property="genre">
+          <items type="audioItem" />
+        </vfolders>
+      </vfolder>
+      
+      <vfolder name="Playlist" id="15" />
+    </vfolder>
+   
+    <vfolder name="Pictures" id="3">
+      <vfolder name="Album" id="13" />
+      
+      <vfolder name="All Pictures" id="11">
+        <items type="imageItem" />
+      </vfolder>
+      
+      <vfolder name="Date Taken" id="12" />
+      
+      <vfolder name="Folders" id="22">
+        <folders filter="contains(imageItem)" />
+      </vfolder>
+    </vfolder>
+
+    <vfolder name="Playlists" id="18">
+      <vfolder name="All Playlists" id="19" />
+      <vfolder name="Folders" id="23" />
+    </vfolder>
+
+    <vfolder name="Video" id="2">
+      <vfolder name="Actor" id="10" />
+      <vfolder name="Album" id="14" />
+      <vfolder name="All Video" id="8">
+				<items type="videoItem" />
+			</vfolder>
+      <vfolder name="Folders" id="21">
+			   <folders filter="contains(videoItem)" />
+			</vfolder>
+      <vfolder name="Genre" id="9" />
+    </vfolder>
+
+  </vfolder_layout>
+
+  <vfolder_layout device="Yamaha" enabled="false" create_references="true" >
+
+    <vfolder name="Playlists" />
+
+    <vfolder name="Artists">
+      <vfolders property="artist">
+        <items type="audioItem" />
+      </vfolders>
+    </vfolder>
+
+    <vfolder name="Albums">
+      <vfolders property="album">
+        <items type="audioItem" />
+      </vfolders>
+    </vfolder>
+
+    <vfolder name="Songs">
+      <items type="audioItem" />
+    </vfolder>
+
+    <vfolder name="Genres">
+      <vfolders property="genre">
+        <items type="audioItem" />
+      </vfolders>
+    </vfolder>
+
+  </vfolder_layout>
+
+</fuppes_vfolder_config>
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
new file mode 100644
index 00000000000..a9880dee20c
--- /dev/null
+++ b/nixos/modules/services/audio/mpd.nix
@@ -0,0 +1,92 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  uid = config.ids.uids.mpd;
+  gid = config.ids.gids.mpd;
+  cfg = config.services.mpd;
+
+  mpdConf = pkgs.writeText "mpd.conf" ''
+    music_directory     "${cfg.musicDirectory}"
+    playlist_directory  "${cfg.dataDir}/playlists"
+    db_file             "${cfg.dataDir}/tag_cache"
+    state_file          "${cfg.dataDir}/state"
+    sticker_file        "${cfg.dataDir}/sticker.sql"
+    log_file            "syslog"
+    user                "mpd"
+    ${cfg.extraConfig}
+  ''; 
+
+in {
+
+  ###### interface
+
+  options = { 
+
+    services.mpd = { 
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable MPD, the music player daemon.
+        ''; 
+      };  
+
+      musicDirectory = mkOption {
+        default = "${cfg.dataDir}/music";
+        description = ''
+          Extra configuration added to the end of MPD's
+          configuration file, mpd.conf.
+        ''; 
+      };  
+
+      extraConfig = mkOption {
+        default = ""; 
+        description = ''
+          Extra directives added to to the end of MPD's configuration file,
+          mpd.conf. Basic configuration like file location and uid/gid
+          is added automatically to the beginning of the file.
+        ''; 
+      };  
+
+      dataDir = mkOption {
+        default = "/var/lib/mpd/";
+        description = ''
+          The directory where MPD stores its state, tag cache,
+          playlists etc.
+        ''; 
+      };  
+
+    };  
+
+  };  
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.mpd = {
+      after = [ "network.target" "sound.target" ];
+      description = "Music Player Daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.mpd ];
+      preStart = "mkdir -p ${cfg.dataDir} && chown -R mpd:mpd  ${cfg.dataDir}";
+      script = "exec mpd --no-daemon ${mpdConf}";
+    };
+
+    users.extraUsers.mpd = {
+      inherit uid;
+      group = "mpd";
+      extraGroups = [ "audio" ];
+      description = "Music Player Daemon user";
+      home = "${cfg.dataDir}";
+    };
+
+    users.extraGroups.mpd.gid = gid;
+
+  };
+
+}
diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix
new file mode 100644
index 00000000000..d5bc932c6b9
--- /dev/null
+++ b/nixos/modules/services/backup/almir.nix
@@ -0,0 +1,171 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.almir;
+
+  bconsoleconf = pkgs.writeText "bconsole.conf"
+    ''
+      Director {
+        Name = ${cfg.director_name}
+        DIRport = ${toString cfg.director_port}
+        address = ${cfg.director_address}
+        Password = "${cfg.director_password}"
+      }
+    '';
+
+  productionini = pkgs.writeText "production.ini"
+    ''
+[app:main]
+use = egg:almir
+
+pyramid.reload_templates = false
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.debug_templates = false
+pyramid.default_locale_name = en
+pyramid.includes =
+    pyramid_exclog
+exclog.extra_info = true
+
+sqlalchemy.url = ${cfg.sqlalchemy_engine_url}
+timezone = ${cfg.timezone}
+bconsole_config = ${bconsoleconf}
+
+[server:main]
+use = egg:waitress#main
+host = 127.0.0.1
+port = ${toString cfg.port}
+
+
+# Begin logging configuration
+
+[loggers]
+keys = root, almir, sqlalchemy, exc_logger
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+
+[logger_almir]
+level = WARN
+handlers =
+qualname = almir
+
+[logger_exc_logger]
+level = ERROR
+handlers =
+qualname = exc_logger
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither.  (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+    '';
+in {
+  options = {
+    services.almir = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable Almir web server. Also configures postgresql database and installs bacula.
+        '';
+      };
+
+      port = mkOption {
+        default = 35000;
+        type = types.uniq types.int;
+        description = ''
+          Port for Almir web server to listen on.
+        '';
+      };
+
+      timezone = mkOption {
+	description = ''
+         Timezone as specified in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+        '';
+        example = "Europe/Ljubljana";
+      };
+
+      sqlalchemy_engine_url = mkOption {
+        example = ''
+          postgresql://bacula:bacula@localhost:5432/bacula
+          mysql+mysqlconnector://<user>:<password>@<hostname>/<database>'
+          sqlite:////var/lib/bacula/bacula.db'
+        '';
+	description = ''
+         Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
+        '';
+      };
+
+      director_name = mkOption {
+        description = ''
+          Name of the Director to connect with bconsole.
+        '';
+      };
+
+      director_password = mkOption {
+        description = ''
+          Password for Director to connect with bconsole.
+        '';
+      };
+
+      director_port = mkOption {
+        default = 9101;
+        type = types.int;
+        description = ''
+          Port for Director to connect with bconsole.
+        '';
+      };
+
+      director_address = mkOption {
+        default = "127.0.0.1";
+        description = ''
+          IP/Hostname for Director to connect with bconsole.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.almir = {
+      after = [ "network.target" "postgresql.service" ];
+      description = "Almir web app";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.pythonPackages.almir ];
+      serviceConfig.ExecStart = "${pkgs.pythonPackages.almir}/bin/pserve ${productionini}";
+    };
+
+    environment.systemPackages = [ pkgs.pythonPackages.almir ];
+
+    users.extraUsers.almir = {
+      group = "almir";
+      uid = config.ids.uids.almir;
+      createHome = true;
+      shell = "${pkgs.bash}/bin/bash";
+    };
+
+    users.extraGroups.almir.gid = config.ids.gids.almir;
+  };
+}
diff --git a/nixos/modules/services/backup/bacula.nix b/nixos/modules/services/backup/bacula.nix
new file mode 100644
index 00000000000..272903c99e3
--- /dev/null
+++ b/nixos/modules/services/backup/bacula.nix
@@ -0,0 +1,408 @@
+{ config, pkgs, ... }:
+
+# TODO: test configuration when building nixexpr (use -t parameter)
+# TODO: support sqlite3 (it's deprecate?) and mysql
+
+with pkgs.lib;
+
+let
+  libDir = "/var/lib/bacula";
+
+  fd_cfg = config.services.bacula-fd;
+  fd_conf = pkgs.writeText "bacula-fd.conf"
+    ''
+      Client {
+        Name = "${fd_cfg.name}";
+        FDPort = ${toString fd_cfg.port};
+        WorkingDirectory = "${libDir}";
+        Pid Directory = "/var/run";
+        ${fd_cfg.extraClientConfig}
+      }
+     
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+      Director {
+        Name = "${name}";
+        Password = "${value.password}";
+        Monitor = "${value.monitor}";
+      }
+      '') fd_cfg.director)}
+     
+      Messages {
+        Name = Standard;
+        syslog = all, !skipped, !restored
+        ${fd_cfg.extraMessagesConfig}
+      }
+    '';
+
+  sd_cfg = config.services.bacula-sd;
+  sd_conf = pkgs.writeText "bacula-sd.conf" 
+    ''
+      Storage {
+        Name = "${sd_cfg.name}";
+        SDPort = ${toString sd_cfg.port};
+        WorkingDirectory = "${libDir}";
+        Pid Directory = "/var/run";
+        ${sd_cfg.extraStorageConfig}
+      }
+ 
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+      Device {
+        Name = "${name}";
+        Archive Device = "${value.archiveDevice}";
+        Media Type = "${value.mediaType}";
+        ${value.extraDeviceConfig}
+      }
+      '') sd_cfg.device)}
+
+      ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
+      Director {
+        Name = "${name}";
+        Password = "${value.password}";
+        Monitor = "${value.monitor}";
+      }
+      '') sd_cfg.director)}
+
+      Messages {
+        Name = Standard;
+        syslog = all, !skipped, !restored
+        ${sd_cfg.extraMessagesConfig}
+      }
+    '';
+
+  dir_cfg = config.services.bacula-dir;
+  dir_conf = pkgs.writeText "bacula-dir.conf" 
+    ''
+    Director {
+      Name = "${dir_cfg.name}";
+      Password = "${dir_cfg.password}";
+      DirPort = ${toString dir_cfg.port};
+      Working Directory = "${libDir}";
+      Pid Directory = "/var/run/";
+      QueryFile = "${pkgs.bacula}/etc/query.sql";
+      ${dir_cfg.extraDirectorConfig}
+    }
+
+    Catalog {
+      Name = "PostgreSQL";
+      dbname = "bacula";
+      user = "bacula";
+    }
+
+    Messages {
+      Name = Standard;
+      syslog = all, !skipped, !restored
+      ${dir_cfg.extraMessagesConfig}
+    }
+
+    ${dir_cfg.extraConfig}
+    '';
+
+  # TODO: by default use this config
+  bconsole_conf = pkgs.writeText "bconsole.conf"
+    ''
+    Director {
+      Name = ${dir_cfg.name};
+      Address = "localhost";
+      DirPort = ${toString dir_cfg.port};
+      Password = "${dir_cfg.password}";
+    }
+    '';
+
+  directorOptions = {name, config, ...}:
+  {
+    options = {
+      password = mkOption {
+        # TODO: required?
+        description = ''
+           Specifies the password that must be supplied for a Director to b
+        '';
+      };
+      
+      monitor = mkOption {
+        default = "no";
+        example = "yes";
+        description = ''
+           If Monitor is set to no (default), this director will have full 
+        '';
+      };
+    };
+  };
+
+  deviceOptions = {name, config, ...}:
+  {
+    options = {
+      archiveDevice = mkOption {
+        # TODO: required?
+        description = ''
+          The specified name-string gives the system file name of the storage device managed by this storage daemon. This will usually be the device file name of a removable storage device (tape drive), for example " /dev/nst0" or "/dev/rmt/0mbn". For a DVD-writer, it will be for example /dev/hdc. It may also be a directory name if you are archiving to disk storage.
+        '';
+      };
+
+      mediaType = mkOption {
+        # TODO: required?
+        description = ''
+          The specified name-string names the type of media supported by this device, for example, "DLT7000". Media type names are arbitrary in that you set them to anything you want, but they must be known to the volume database to keep track of which storage daemons can read which volumes. In general, each different storage type should have a unique Media Type associated with it. The same name-string must appear in the appropriate Storage resource definition in the Director's configuration file.
+        '';
+      };
+
+      extraDeviceConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Device directive.
+        '';
+        example = ''
+          LabelMedia = yes
+          Random Access = no
+          AutomaticMount = no
+          RemovableMedia = no
+          MaximumOpenWait = 60
+          AlwaysOpen = no
+        '';
+      };
+    };
+  };
+
+in {
+  options = {
+    services.bacula-fd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Bacula File Daemon.
+        '';
+      };
+ 
+      name = mkOption {
+        default = "${config.networking.hostName}-fd";
+        description = ''
+        	The client name that must be used by the Director when connecting. Generally, it is a good idea to use a name related to the machine so that error messages can be easily identified if you have multiple Clients. This directive is required.
+        '';
+      };
+ 
+      port = mkOption {
+        default = 9102;
+        type = types.uniq types.int;
+        description = ''
+        	This specifies the port number on which the Client listens for Director connections. It must agree with the FDPort specified in the Client resource of the Director's configuration file. The default is 9102.
+        '';
+      };
+ 
+      director = mkOption {
+        default = {};
+        description = ''
+          This option defines director resources in Bacula File Daemon.
+        '';
+        type = types.attrsOf types.optionSet;
+        options = [ directorOptions ];
+      };
+
+      extraClientConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Client directive.
+        '';
+        example = ''
+          Maximum Concurrent Jobs = 20;
+          Heartbeat Interval = 30;
+        '';
+      };
+
+      extraMessagesConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Messages directive.
+        '';
+        example = ''
+          console = all
+        '';
+      };
+    };
+
+    services.bacula-sd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Bacula Storage Daemon.
+        '';
+      };
+ 
+      name = mkOption {
+        default = "${config.networking.hostName}-sd";
+        description = ''
+          Specifies the Name of the Storage daemon.
+        '';
+      };
+ 
+      port = mkOption {
+        default = 9103;
+        type = types.uniq types.int;
+        description = ''
+          Specifies port number on which the Storage daemon listens for Director connections. The default is 9103.
+        '';
+      };
+
+      director = mkOption {
+        default = {};
+        description = ''
+          This option defines Director resources in Bacula Storage Daemon.
+        '';
+        type = types.attrsOf types.optionSet;
+        options = [ directorOptions ];
+      };
+
+      device = mkOption {
+        default = {};
+        description = ''
+          This option defines Device resources in Bacula Storage Daemon.
+        '';
+        type = types.attrsOf types.optionSet;
+        options = [ deviceOptions ];
+      };
+ 
+      extraStorageConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Storage directive.
+        '';
+        example = ''
+          Maximum Concurrent Jobs = 20;
+          Heartbeat Interval = 30;
+        '';
+      };
+
+      extraMessagesConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Messages directive.
+        '';
+        example = ''
+          console = all
+        '';
+      };
+ 
+    };
+
+    services.bacula-dir = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable Bacula Director Daemon.
+        '';
+      };
+
+      name = mkOption {
+        default = "${config.networking.hostName}-dir";
+        description = ''
+          The director name used by the system administrator. This directive is required.
+        '';
+      };
+ 
+      port = mkOption {
+        default = 9101;
+        type = types.uniq types.int;
+        description = ''
+          Specify the port (a positive integer) on which the Director daemon will listen for Bacula Console connections. This same port number must be specified in the Director resource of the Console configuration file. The default is 9101, so normally this directive need not be specified. This directive should not be used if you specify DirAddresses (N.B plural) directive.
+        '';
+      };
+ 
+      password = mkOption {
+        # TODO: required?
+        description = ''
+           Specifies the password that must be supplied for a Director.
+        '';
+      };
+
+      extraMessagesConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Messages directive.
+        '';
+        example = ''
+          console = all
+        '';
+      };
+
+      extraDirectorConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration to be passed in Director directive.
+        '';
+        example = ''
+          Maximum Concurrent Jobs = 20;
+          Heartbeat Interval = 30;
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration for Bacula Director Daemon.
+        '';
+        example = ''
+          TODO
+        '';
+      };
+    };
+  };
+
+  config = mkIf (fd_cfg.enable || sd_cfg.enable || dir_cfg.enable) {
+    systemd.services.bacula-fd = mkIf fd_cfg.enable {
+      after = [ "network.target" ];
+      description = "Bacula File Daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.bacula ];
+      serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-fd -f -u root -g bacula -c ${fd_conf}";
+      serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    };
+
+    systemd.services.bacula-sd = mkIf sd_cfg.enable {
+      after = [ "network.target" ];
+      description = "Bacula Storage Daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.bacula ];
+      serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-sd -f -u bacula -g bacula -c ${sd_conf}";
+      serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+    };
+
+    services.postgresql.enable = dir_cfg.enable == true;
+
+    systemd.services.bacula-dir = mkIf dir_cfg.enable {
+      after = [ "network.target" "postgresql.service" ];
+      description = "Bacula Director Daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.bacula ];
+      serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-dir -f -u bacula -g bacula -c ${dir_conf}";
+      serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      preStart = ''
+        if ! test -e "${libDir}/db-created"; then
+            ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole bacula
+            #${pkgs.postgresql}/bin/createdb --owner bacula bacula
+
+            # populate DB
+            ${pkgs.bacula}/etc/create_bacula_database postgresql
+            ${pkgs.bacula}/etc/make_bacula_tables postgresql
+            ${pkgs.bacula}/etc/grant_bacula_privileges postgresql
+            touch "${libDir}/db-created"
+        else
+            ${pkgs.bacula}/etc/update_bacula_tables postgresql || true
+        fi
+      '';
+    };
+
+    environment.systemPackages = [ pkgs.bacula ];
+
+    users.extraUsers.bacula = {
+      group = "bacula";
+      uid = config.ids.uids.bacula;
+      home = "${libDir}";
+      createHome = true;
+      description = "Bacula Daemons user";
+      shell = "${pkgs.bash}/bin/bash";
+    };
+
+    users.extraGroups.bacula.gid = config.ids.gids.bacula;
+  };
+}
diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix
new file mode 100644
index 00000000000..3ff9978fbb9
--- /dev/null
+++ b/nixos/modules/services/backup/mysql-backup.nix
@@ -0,0 +1,81 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) mysql gzip;
+
+  cfg = config.services.mysqlBackup ;
+  location = cfg.location ;
+  mysqlBackupCron = db : ''
+    ${cfg.period} ${cfg.user} ${mysql}/bin/mysqldump ${if cfg.singleTransaction then "--single-transaction" else ""} ${db} | ${gzip}/bin/gzip -c > ${location}/${db}.gz
+  '';
+
+in
+
+{
+  options = {
+
+    services.mysqlBackup = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable MySQL backups.
+        '';
+      };
+
+      period = mkOption {
+        default = "15 01 * * *";
+        description = ''
+          This option defines (in the format used by cron) when the
+          databases should be dumped.
+          The default is to update at 01:15 (at night) every day.
+        '';
+      };
+
+      user = mkOption {
+        default = "mysql";
+        description = ''
+          User to be used to perform backup.
+        '';
+      };
+
+      databases = mkOption {
+        default = [];
+        description = ''
+          List of database names to dump.
+        '';
+      };
+
+      location = mkOption {
+        default = "/var/backup/mysql";
+        description = ''
+          Location to put the gzipped MySQL database dumps.
+        '';
+      };
+
+      singleTransaction = mkOption {
+        default = false;
+        description = ''
+          Whether to create database dump in a single transaction
+        '';
+      };
+    };
+
+  };
+
+  config = mkIf config.services.mysqlBackup.enable {
+
+    services.cron.systemCronJobs = map mysqlBackupCron config.services.mysqlBackup.databases;
+
+    system.activationScripts.mysqlBackup = stringAfter [ "stdio" "users" ]
+      ''
+        mkdir -m 0700 -p ${config.services.mysqlBackup.location}
+        chown ${config.services.mysqlBackup.user} ${config.services.mysqlBackup.location}
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
new file mode 100644
index 00000000000..e68ad794a96
--- /dev/null
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -0,0 +1,66 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  inherit (pkgs) postgresql gzip;
+
+  location = config.services.postgresqlBackup.location ;
+
+  postgresqlBackupCron = db:
+    ''
+      ${config.services.postgresqlBackup.period} root ${postgresql}/bin/pg_dump ${db} | ${gzip}/bin/gzip -c > ${location}/${db}.gz
+    '';
+
+in
+
+{
+
+  options = {
+
+    services.postgresqlBackup = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable PostgreSQL dumps.
+        '';
+      };
+
+      period = mkOption {
+        default = "15 01 * * *";
+        description = ''
+          This option defines (in the format used by cron) when the
+          databases should be dumped.
+          The default is to update at 01:15 (at night) every day.
+        '';
+      };
+
+      databases = mkOption {
+        default = [];
+        description = ''
+          List of database names to dump.
+        '';
+      };
+
+      location = mkOption {
+        default = "/var/backup/postgresql";
+        description = ''
+          Location to put the gzipped PostgreSQL database dumps.
+        '';
+      };
+    };
+
+  };
+
+  config = mkIf config.services.postgresqlBackup.enable {
+    services.cron.systemCronJobs = map postgresqlBackupCron config.services.postgresqlBackup.databases;
+
+    system.activationScripts.postgresqlBackup = stringAfter [ "stdio" "users" ]
+      ''
+        mkdir -m 0700 -p ${config.services.postgresqlBackup.location}
+        chown root ${config.services.postgresqlBackup.location}
+      '';
+  };
+
+}
diff --git a/nixos/modules/services/backup/rsnapshot.nix b/nixos/modules/services/backup/rsnapshot.nix
new file mode 100644
index 00000000000..178ba3ec720
--- /dev/null
+++ b/nixos/modules/services/backup/rsnapshot.nix
@@ -0,0 +1,65 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let cfg = config.services.rsnapshot;
+in
+{
+  options = {
+    services.rsnapshot = {
+      enable = mkEnableOption "rsnapshot backups";
+
+      extraConfig = mkOption {
+        default = "";
+        example = ''
+          retains	hourly	24
+          retain	daily	365
+          backup	/home/	localhost/
+        '';
+        type = types.lines;
+        description = ''
+          rsnapshot configuration option in addition to the defaults from
+          rsnapshot and this module.
+
+          Note that tabs are required to separate option arguments, and
+          directory names require trailing slashes.
+
+          The "extra" in the option name might be a little misleading right
+          now, as it is required to get a functional configuration.
+        '';
+      };
+
+      cronIntervals = mkOption {
+        default = {};
+        example = { "hourly" = "0 * * * *"; "daily" = "50 21 * * *"; };
+        type = types.attrsOf types.string;
+        description = ''
+          Periodicity at which intervals should be run by cron.
+          Note that the intervals also have to exist in configuration
+          as retain options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable (let
+    myRsnapshot = pkgs.rsnapshot.override { configFile = rsnapshotCfg; };
+    rsnapshotCfg = with pkgs; writeText "gen-rsnapshot.conf" (''
+        config_version	1.2
+        cmd_cp	${coreutils}/bin/cp
+        cmd_rsync	${rsync}/bin/rsync
+        cmd_ssh	${openssh}/bin/ssh
+        cmd_logger	${inetutils}/bin/logger
+        cmd_du	${coreutils}/bin/du
+        lockfile	/run/rsnapshot.pid
+
+        ${cfg.extraConfig}
+      '');
+    in {
+      environment.systemPackages = [ myRsnapshot ];
+
+      services.cron.systemCronJobs =
+        mapAttrsToList (interval: time: "${time} root ${myRsnapshot}/bin/rsnapshot ${interval}") cfg.cronIntervals;
+    }
+  );
+}
diff --git a/nixos/modules/services/backup/sitecopy-backup.nix b/nixos/modules/services/backup/sitecopy-backup.nix
new file mode 100644
index 00000000000..5c7f7ffae5b
--- /dev/null
+++ b/nixos/modules/services/backup/sitecopy-backup.nix
@@ -0,0 +1,104 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  inherit (pkgs) sitecopy;
+
+  stateDir = "/var/spool/sitecopy";
+
+  sitecopyCron = backup : ''
+    ${if backup ? period then backup.period else config.services.sitecopy.period} root ${sitecopy}/bin/sitecopy --storepath=${stateDir} --rcfile=${stateDir}/${backup.name}.conf --update ${backup.name} >> /var/log/sitecopy.log 2>&1
+  '';
+in
+
+{
+
+  options = {
+
+    services.sitecopy = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable sitecopy backups of specified directories.
+        '';
+      };
+
+      period = mkOption {
+        default = "15 04 * * *";
+        description = ''
+          This option defines (in the format used by cron) when the
+          sitecopy backup are being run.
+          The default is to update at 04:15 (at night) every day.
+        '';
+      };
+
+      backups = mkOption {
+        example = [
+          { name = "test";
+            local = "/tmp/backup";
+            remote = "/staff-groups/ewi/st/strategoxt/backup/test";
+            server = "webdata.tudelft.nl";
+            protocol = "webdav";
+            https = true ;
+            symlinks = "maintain" ;
+          }
+        ];
+        default = [];
+        description = ''
+           List of attributesets describing the backups.
+
+           Username/password are extracted from <filename>${stateDir}/sitecopy.secrets</filename> at activation
+           time. The secrets file lines should have the following structure:
+           <screen>
+             server username password
+           </screen>
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf config.services.sitecopy.enable {
+    environment.systemPackages = [ sitecopy ];
+
+    services.cron.systemCronJobs = map sitecopyCron config.services.sitecopy.backups;
+
+    system.activationScripts.sitecopyBackup = stringAfter [ "stdio" "users" ]
+      ''
+        mkdir -m 0700 -p ${stateDir}
+        chown root ${stateDir}
+        touch ${stateDir}/sitecopy.secrets
+        chown root ${stateDir}/sitecopy.secrets
+
+        ${pkgs.lib.concatStrings (map ( b: ''
+            unset secrets
+            unset secret
+            secrets=`grep '^${b.server}' ${stateDir}/sitecopy.secrets | head -1`
+            secret=($secrets)
+            cat > ${stateDir}/${b.name}.conf << EOF
+              site ${b.name}
+              server ${b.server}
+              protocol ${b.protocol}
+              username ''${secret[1]}
+              password ''${secret[2]}
+              local ${b.local}
+              remote ${b.remote}
+              symlinks ${b.symlinks}
+              ${if b.https then "http secure" else ""}
+            EOF
+            chmod 0600 ${stateDir}/${b.name}.conf
+            if ! test -e ${stateDir}/${b.name} ; then
+              echo " * Initializing sitecopy '${b.name}'"
+              ${sitecopy}/bin/sitecopy --storepath=${stateDir} --rcfile=${stateDir}/${b.name}.conf --initialize ${b.name}
+            else
+              echo " * Sitecopy '${b.name}' already initialized"
+            fi
+          '' ) config.services.sitecopy.backups
+        )}
+      '';
+  };
+
+}
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
new file mode 100644
index 00000000000..7b03b4d8f1d
--- /dev/null
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -0,0 +1,72 @@
+{ config, pkgs, ... }:
+let
+  cfg = config.services.fourStoreEndpoint;
+  endpointUser = "fourstorehttp";
+  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
+in
+with pkgs.lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.fourStoreEndpoint = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable 4Store SPARQL endpoint.";
+      };
+
+      database = mkOption {
+        default = config.services.fourStore.database;
+        description = "RDF database name to expose via the endpoint. Defaults to local 4Store database name.";
+      };
+
+      listenAddress = mkOption {
+	default = null;
+        description = "IP address to listen on.";
+      };
+
+      port = mkOption {
+	default = 8080;
+        description = "port to listen on.";
+      };
+
+      options = mkOption {
+        default = "";
+        description = "Extra CLI options to pass to 4Store's 4s-httpd process.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (
+    mkAssert (cfg.enable -> cfg.database != "")
+      "Must specify database name" {
+
+    users.extraUsers = singleton
+      { name = endpointUser;
+        uid = config.ids.uids.fourStoreEndpoint;
+        description = "4Store SPARQL endpoint user";
+#        home = stateDir;
+      };
+
+    services.avahi.enable = true;
+
+    jobs.fourStoreEndpoint = {
+      name = "4store-endpoint";
+      startOn = "filesystem";
+
+      exec = ''
+	${run} '${pkgs.rdf4store}/bin/4s-httpd -D ${cfg.options} ${if cfg.listenAddress!=null then "-H ${cfg.listenAddress}" else "" } -p ${toString cfg.port} ${cfg.database}'
+      '';
+    };
+
+  });
+
+}
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
new file mode 100644
index 00000000000..14990e92ea3
--- /dev/null
+++ b/nixos/modules/services/databases/4store.nix
@@ -0,0 +1,71 @@
+{ config, 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;
+{
+
+  ###### interface
+
+  options = {
+
+    services.fourStore = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable 4Store RDF database server.";
+      };
+
+      database = mkOption {
+        default = "";
+        description = "RDF database name. If it doesn't exist, it will be created. Databases are stored in ${stateDir}.";
+      };
+
+      options = mkOption {
+        default = "";
+        description = "Extra CLI options to pass to 4Store.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (
+    mkAssert (cfg.enable -> cfg.database != "")
+      "Must specify database name" {
+
+    users.extraUsers = singleton
+      { name = fourStoreUser;
+        uid = config.ids.uids.fourStore;
+        description = "4Store database user";
+        home = stateDir;
+      };
+
+    services.avahi.enable = true;
+
+    jobs.fourStore = {
+      name = "4store";
+      startOn = "filesystem";
+
+      preStart = ''
+        mkdir -p ${stateDir}/
+        chown ${fourStoreUser} ${stateDir}
+	if ! test -e "${stateDir}/${cfg.database}"; then
+	  ${run} -c '${pkgs.rdf4store}/bin/4s-backend-setup ${cfg.database}'
+        fi
+      '';
+
+      exec = ''
+	${run} -c '${pkgs.rdf4store}/bin/4s-backend -D ${cfg.options} ${cfg.database}'
+      '';
+    };
+
+  });
+
+}
diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix
new file mode 100644
index 00000000000..aca0d58900b
--- /dev/null
+++ b/nixos/modules/services/databases/firebird.nix
@@ -0,0 +1,149 @@
+{ config, pkgs, ... }:
+
+# TODO: this file needs some additional work - at least you can connect to
+# firebird ..
+# Example how to connect:
+# isql /var/db/firebird/data/your-db.fdb -u sysdba -p <default password>
+
+# There are at least two ways to run firebird. superserver has been choosen
+# however there are no strong reasons to prefer this or the other one AFAIK
+# 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;
+
+let
+
+  cfg = config.services.firebird;
+
+  firebird = cfg.package;
+
+  pidFile = "${cfg.pidDir}/firebirdd.pid";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.firebird = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the firebird super server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.firebirdSuper;
+        /*
+          Example: <code>package = pkgs.firebirdSuper.override { icu =
+            pkgs.icu; };</code> which is not recommended for compatibility
+            reasons. See comments at the firebirdSuper derivation
+        */
+
+        description = "
+          Which firebird derivation to use.
+        ";
+      };
+
+      port = mkOption {
+        default = "3050";
+        description = "Port of Firebird.";
+      };
+
+      user = mkOption {
+        default = "firebird";
+        description = "User account under which firebird runs.";
+      };
+
+      dataDir = mkOption {
+        default = "/var/db/firebird/data"; # ubuntu is using /var/lib/firebird/2.1/data/.. ?
+        description = "Location where firebird databases are stored.";
+      };
+
+      pidDir = mkOption {
+        default = "/run/firebird";
+        description = "Location of the file which stores the PID of the firebird server.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.firebird.enable {
+
+    users.extraUsers.firebird.description =  "Firebird server user";
+
+    environment.systemPackages = [firebird];
+
+    systemd.services.firebird =
+      { description = "firebird super server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        # TODO: moving security2.fdb into the data directory works, maybe there
+        # is a better way
+        preStart =
+          ''
+            secureDir="${cfg.dataDir}/../system"
+
+            mkdir -m 0700 -p \
+              "${cfg.dataDir}" \
+              "${cfg.pidDir}" \
+              /var/log/firebird \
+              "$secureDir"
+
+            if ! test -e "$secureDir/security2.fdb"; then
+                cp ${firebird}/security2.fdb "$secureDir"
+            fi
+
+            chown -R ${cfg.user} "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird
+            chmod -R 700 "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird
+          '';
+
+        serviceConfig.PermissionsStartOnly = true; # preStart must be run as root
+        serviceConfig.User = cfg.user;
+        serviceConfig.ExecStart = ''${firebird}/bin/fbserver -d'';
+
+        # TODO think about shutdown
+      };
+
+    environment.etc."firebird/firebird.msg".source = "${firebird}/firebird.msg";
+
+    # think about this again - and eventually make it an option
+    environment.etc."firebird/firebird.conf".text = ''
+      # RootDirectory = Restrict ${cfg.dataDir}
+      DatabaseAccess = Restrict ${cfg.dataDir}
+      ExternalFileAccess = Restrict ${cfg.dataDir}
+      # what is this? is None allowed?
+      UdfAccess = None
+      # "Native" =  traditional interbase/firebird, "mixed" is windows only
+      Authentication = Native
+
+      # defaults to -1 on non Win32
+      #MaxUnflushedWrites = 100
+      #MaxUnflushedWriteTime = 100
+
+      # show trace if trouble occurs (does this require debug build?)
+      # BugcheckAbort = 0
+      # ConnectionTimeout = 180
+
+      #RemoteServiceName = gds_db
+      RemoteServicePort = ${cfg.port}
+
+      # randomly choose port for server Event Notification
+      #RemoteAuxPort = 0
+      # rsetrict connections to a network card:
+      #RemoteBindAddress =
+      # there are some more settings ..
+    '';
+    };
+
+}
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
new file mode 100644
index 00000000000..a0e264f2299
--- /dev/null
+++ b/nixos/modules/services/databases/memcached.nix
@@ -0,0 +1,97 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.memcached;
+
+  memcached = pkgs.memcached;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.memcached = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable Memcached.
+        ";
+      };
+
+      user = mkOption {
+        default = "memcached";
+        description = "The user to run Memcached as";
+      };
+
+      listen = mkOption {
+        default = "127.0.0.1";
+        description = "The IP address to bind to";
+      };
+
+      port = mkOption {
+        default = 11211;
+        description = "The port to bind to";
+      };
+
+      socket = mkOption {
+        default = "";
+        description = "Unix socket path to listen on. Setting this will disable network support";
+        example = "/var/run/memcached";
+      };
+
+      maxMemory = mkOption {
+        default = 64;
+        description = "The maximum amount of memory to use for storage, in megabytes.";
+      };
+
+      maxConnections = mkOption {
+        default = 1024;
+        description = "The maximum number of simultaneous connections";
+      };
+
+      extraOptions = mkOption {
+        default = [];
+        description = "A list of extra options that will be added as a suffix when running memcached";
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.memcached.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "Memcached server user";
+      };
+
+    environment.systemPackages = [ memcached ];
+
+    systemd.services.memcached =
+      { description = "Memcached server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart =
+            let
+              networking = if cfg.socket != ""
+                then "-s ${cfg.socket}"
+                else "-l ${cfg.listen} -p ${toString cfg.port}";
+            in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
+
+          User = cfg.user;
+        };
+      };
+  };
+
+}
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
new file mode 100644
index 00000000000..d6299415893
--- /dev/null
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -0,0 +1,130 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  b2s = x: if x then "true" else "false";
+
+  cfg = config.services.mongodb;
+
+  mongodb = cfg.package;
+
+  mongoCnf = pkgs.writeText "mongodb.conf"
+  ''
+    bind_ip = ${cfg.bind_ip}
+    ${optionalString cfg.quiet "quiet = true"}
+    dbpath = ${cfg.dbpath}
+    logpath = ${cfg.logpath}
+    logappend = ${b2s cfg.logappend}
+    ${optionalString (cfg.replSetName != "") "replSet = ${cfg.replSetName}"}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mongodb = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the MongoDB server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.mongodb;
+        description = "
+          Which MongoDB derivation to use.
+        ";
+      };
+
+      user = mkOption {
+        default = "mongodb";
+        description = "User account under which MongoDB runs";
+      };
+
+      bind_ip = mkOption {
+        default = "127.0.0.1";
+        description = "IP to bind to";
+      };
+
+      quiet = mkOption {
+        default = false;
+        description = "quieter output";
+      };
+
+      dbpath = mkOption {
+        default = "/var/db/mongodb";
+        description = "Location where MongoDB stores its files";
+      };
+
+      logpath = mkOption {
+        default = "/var/log/mongodb/mongod.log";
+        description = "Location where MongoDB stores its logfile";
+      };
+
+      logappend = mkOption {
+        default = true;
+        description = "Append logfile instead over overwriting";
+      };
+
+      replSetName = mkOption {
+        default = "";
+        description = ''
+          If this instance is part of a replica set, set its name here.
+          Otherwise, leave empty to run as single node.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.mongodb.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "MongoDB server user";
+      };
+
+    environment.systemPackages = [ mongodb ];
+
+    systemd.services.mongodb_init =
+      { description = "MongoDB server initialisation";
+
+        wantedBy = [ "mongodb.service" ];
+        before = [ "mongodb.service" ];
+
+        serviceConfig.Type = "oneshot";
+
+        script = ''
+          if ! test -e ${cfg.dbpath}; then
+              install -d -m0700 -o ${cfg.user} ${cfg.dbpath}
+              install -d -m0755 -o ${cfg.user} `dirname ${cfg.logpath}`
+          fi
+        '';
+      };
+
+    systemd.services.mongodb =
+      { description = "MongoDB server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf}";
+          User = cfg.user;
+        };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
new file mode 100644
index 00000000000..663c2cc4505
--- /dev/null
+++ b/nixos/modules/services/databases/mysql.nix
@@ -0,0 +1,245 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.mysql;
+
+  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}"}
+    ${optionalString (cfg.replication.role == "slave")
+    ''
+      master-host = ${cfg.replication.masterHost}
+      master-user = ${cfg.replication.masterUser}
+      master-password = ${cfg.replication.masterPassword}
+      master-port = ${toString cfg.replication.masterPort}
+    ''}
+    ${cfg.extraOptions}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mysql = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the MySQL server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.mysql;
+        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 = ./foodatabase.sql; }
+          { name = "bardatabase"; schema = ./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.mysql.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}/libexec/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.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/mysql55.nix b/nixos/modules/services/databases/mysql55.nix
new file mode 100644
index 00000000000..46148d68f4c
--- /dev/null
+++ b/nixos/modules/services/databases/mysql55.nix
@@ -0,0 +1,248 @@
+{ 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 = ./foodatabase.sql; }
+          { name = "bardatabase"; schema = ./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
new file mode 100644
index 00000000000..a4dd30be1fb
--- /dev/null
+++ b/nixos/modules/services/databases/openldap.nix
@@ -0,0 +1,58 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.openldap;
+  openldap = pkgs.openldap;
+
+  configFile = pkgs.writeText "slapd.conf" cfg.extraConfig;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.openldap = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the ldap server.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          sldapd.conf configuration
+        ";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.openldap.enable {
+
+    environment.systemPackages = [ openldap ];
+
+    systemd.services.openldap = {
+      description = "LDAP server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      preStart = ''
+        mkdir -p /var/run/slapd
+      '';
+      serviceConfig.ExecStart = "${openldap}/libexec/slapd -d 0 -f ${configFile}";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
new file mode 100644
index 00000000000..fc6b5b167b8
--- /dev/null
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -0,0 +1,232 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.postgresql;
+
+  # see description of extraPlugins
+  postgresqlAndPlugins = pg:
+    if cfg.extraPlugins == [] then pg
+    else pkgs.buildEnv {
+      name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
+      paths = [ pg ] ++ cfg.extraPlugins;
+      postBuild =
+        ''
+          mkdir -p $out/bin
+          rm $out/bin/{pg_config,postgres,pg_ctl}
+          cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
+        '';
+    };
+
+  postgresql = postgresqlAndPlugins cfg.package;
+
+  flags = optional cfg.enableTCPIP "-i";
+
+  # The main PostgreSQL configuration file.
+  configFile = pkgs.writeText "postgresql.conf"
+    ''
+      hba_file = '${pkgs.writeText "pg_hba.conf" cfg.authentication}'
+      ident_file = '${pkgs.writeText "pg_ident.conf" cfg.identMap}'
+      log_destination = 'stderr'
+      ${cfg.extraConfig}
+    '';
+
+  pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.postgresql = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run PostgreSQL.
+        '';
+      };
+
+      package = mkOption {
+        example = literalExample "pkgs.postgresql92";
+        description = ''
+          PostgreSQL package to use.
+        '';
+      };
+
+      port = mkOption {
+        default = "5432";
+        description = ''
+          Port for PostgreSQL.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/db/postgresql";
+        description = ''
+          Data directory for PostgreSQL.
+        '';
+      };
+
+      authentication = mkOption {
+        default = "";
+        description = ''
+          Defines how users authenticate themselves to the server.
+        '';
+      };
+
+      identMap = mkOption {
+        default = "";
+        description = ''
+          Defines the mapping from system users to database users.
+        '';
+      };
+
+      initialScript = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          A file containing SQL statements to execute on first startup.
+        '';
+      };
+
+      authMethod = mkOption {
+        default = " ident sameuser ";
+        description = ''
+          How to authorize users.
+          Note: ident needs absolute trust to all allowed client hosts.
+        '';
+      };
+
+      enableTCPIP = mkOption {
+        default = false;
+        description = ''
+          Whether to run PostgreSQL with -i flag to enable TCP/IP connections.
+        '';
+      };
+
+      extraPlugins = mkOption {
+        default = [];
+        example = "pkgs.postgis"; # of course don't use a string here!
+        description = ''
+          When this list contains elements a new store path is created.
+          PostgreSQL and the elments are symlinked into it. Then pg_config,
+          postgres and pc_ctl are copied to make them use the new
+          $out/lib directory as pkglibdir. This makes it possible to use postgis
+          without patching the .sql files which reference $libdir/postgis-1.5.
+        '';
+        # Note: the duplication of executables is about 4MB size.
+        # So a nicer solution was patching postgresql to allow setting the
+        # libdir explicitely.
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "Additional text to be appended to <filename>postgresql.conf</filename>.";
+      };
+
+      recoveryConfig = mkOption {
+        default = null;
+        type = types.nullOr types.string;
+        description = ''
+          Values to put into recovery.conf file.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.postgresql.enable {
+
+    services.postgresql.authentication =
+      ''
+        # Generated file; do not edit!
+        local all all              ident ${optionalString pre84 "sameuser"}
+        host  all all 127.0.0.1/32 md5
+        host  all all ::1/128      md5
+      '';
+
+    users.extraUsers.postgres =
+      { name = "postgres";
+        uid = config.ids.uids.postgres;
+        group = "postgres";
+        description = "PostgreSQL server user";
+      };
+
+    users.extraGroups.postgres.gid = config.ids.gids.postgres;
+
+    environment.systemPackages = [postgresql];
+
+    systemd.services.postgresql =
+      { description = "PostgreSQL Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        environment.PGDATA = cfg.dataDir;
+
+        path = [ pkgs.su postgresql ];
+
+        preStart =
+          ''
+            # Initialise the database.
+            if ! test -e ${cfg.dataDir}; then
+                mkdir -m 0700 -p ${cfg.dataDir}
+                chown -R postgres ${cfg.dataDir}
+                su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root'
+                rm -f ${cfg.dataDir}/*.conf
+                touch "${cfg.dataDir}/.first_startup"
+            fi
+
+            ln -sfn "${configFile}" "${cfg.dataDir}/postgresql.conf"
+            ${optionalString (cfg.recoveryConfig != null) ''
+              ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
+                "${cfg.dataDir}/recovery.conf"
+            ''}
+          ''; # */
+
+        serviceConfig =
+          { ExecStart = "@${postgresql}/bin/postgres postgres ${toString flags}";
+            User = "postgres";
+            Group = "postgres";
+            PermissionsStartOnly = true;
+
+            # Shut down Postgres using SIGINT ("Fast Shutdown mode").  See
+            # http://www.postgresql.org/docs/current/static/server-shutdown.html
+            KillSignal = "SIGINT";
+
+            # Give Postgres a decent amount of time to clean up after
+            # receiving systemd's SIGINT.
+            TimeoutSec = 120;
+          };
+
+        # Wait for PostgreSQL to be ready to accept connections.
+        postStart =
+          ''
+            while ! psql postgres -c "" 2> /dev/null; do
+                if ! kill -0 "$MAINPID"; then exit 1; fi
+                sleep 0.1
+            done
+
+            if test -e "${cfg.dataDir}/.first_startup"; then
+              ${optionalString (cfg.initialScript != null) ''
+                cat "${cfg.initialScript}" | psql postgres
+              ''}
+              rm -f "${cfg.dataDir}/.first_startup"
+            fi
+          '';
+
+        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
new file mode 100644
index 00000000000..5bc58c73bd6
--- /dev/null
+++ b/nixos/modules/services/databases/redis.nix
@@ -0,0 +1,216 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.redis;
+  redisBool = b: if b then "yes" else "no";
+  condOption = name: value: if value != null then "${name} ${toString value}" else "";
+
+  redisConfig = pkgs.writeText "redis.conf" ''
+    pidfile ${cfg.pidFile}
+    port ${toString cfg.port}
+    ${condOption "bind" cfg.bind}
+    ${condOption "unixsocket" cfg.unixSocket}
+    loglevel ${cfg.logLevel}
+    logfile ${cfg.logfile}
+    databases ${toString cfg.databases}
+    ${concatMapStrings (d: "save ${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}\n") cfg.save}
+    dbfilename ${cfg.dbFilename}
+    dir ${toString cfg.dbpath}
+    ${if cfg.slaveOf != null then "slaveof ${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}" else ""}
+    ${condOption "masterauth" cfg.masterAuth}
+    ${condOption "requirepass" cfg.requirePass}
+    appendOnly ${redisBool cfg.appendOnly}
+    appendfsync ${cfg.appendFsync}
+    slowlog-log-slower-than ${toString cfg.slowLogLogSlowerThan}
+    slowlog-max-len ${toString cfg.slowLogMaxLen}
+    ${cfg.extraConfig}
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.redis = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the Redis server.";
+      };
+
+      package = mkOption {
+        default = pkgs.redis;
+        description = "Which Redis derivation to use.";
+      };
+
+      user = mkOption {
+        default = "redis";
+        description = "User account under which Redis runs";
+      };
+
+      pidFile = mkOption {
+        default = "/var/lib/redis/redis.pid";
+        description = "";
+      };
+
+      port = mkOption {
+        default = 6379;
+        description = "The port for Redis to listen to";
+        type = with types; int;
+      };
+
+      bind = mkOption {
+        default = null; # All interfaces
+        description = "The IP interface to bind to";
+        example = "127.0.0.1";
+      };
+
+      unixSocket = mkOption {
+        default = null;
+        description = "The path to the socket to bind to";
+        example = "/var/run/redis.sock";
+      };
+
+      logLevel = mkOption {
+        default = "notice"; # debug, verbose, notice, warning
+        example = "debug";
+        description = "Specify the server verbosity level, options: debug, verbose, notice, warning";
+        type = with types; string;
+      };
+
+      logfile = mkOption {
+        default = "stdout";
+        description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
+        example = "/var/log/redis.log";
+        type = with types; string;
+      };
+
+      databases = mkOption {
+        default = 16;
+        description = "Set the number of databases.";
+        type = with types; int;
+      };
+
+      save = mkOption {
+        default = [ [900 1] [300 10] [60 10000] ];
+        description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
+        example = [ [900 1] [300 10] [60 10000] ];
+      };
+
+      dbFilename = mkOption {
+        default = "dump.rdb";
+        description = "The filename where to dump the DB";
+        type = with types; string;
+      };
+
+      dbpath = mkOption {
+        default = "/var/lib/redis";
+        description = "The DB will be written inside this directory, with the filename specified using the 'dbFilename' configuration";
+        type = with types; string;
+      };
+
+      slaveOf = mkOption {
+        default = null; # { ip, port }
+        description = "An attribute set with two attributes: ip and port to which this redis instance acts as a slave";
+        example = { ip = "192.168.1.100"; port = 6379; };
+      };
+
+      masterAuth = mkOption {
+        default = null;
+        description = ''If the master is password protected (using the requirePass configuration)
+        it is possible to tell the slave to authenticate before starting the replication synchronization
+        process, otherwise the master will refuse the slave request.
+        (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
+      };
+
+      requirePass = mkOption {
+        default = null;
+        description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)";
+        example = "letmein!";
+      };
+
+      appendOnly = mkOption {
+        default = false;
+        description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
+        type = with types; bool;
+      };
+
+      appendOnlyFilename = mkOption {
+        default = "appendonly.aof";
+        description = "Filename for the append-only file (stored inside of dbpath)";
+        type = with types; string;
+      };
+
+      appendFsync = mkOption {
+        default = "everysec"; # no, always, everysec
+        description = "How often to fsync the append-only log, options: no, always, everysec";
+        type = with types; string;
+      };
+
+      slowLogLogSlowerThan = mkOption {
+        default = 10000;
+        description = "Log queries whose execution take longer than X in milliseconds";
+        example = 1000;
+        type = with types; int;
+      };
+
+      slowLogMaxLen = mkOption {
+        default = 128;
+        description = "Maximum number of items to keep in slow log";
+        type = with types; int;
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "Extra configuration options for redis.conf";
+        type = with types; string;
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.redis.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "Redis database user";
+      };
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.redis_init =
+      { description = "Redis server initialisation";
+
+        wantedBy = [ "redis.service" ];
+        before = [ "redis.service" ];
+
+        serviceConfig.Type = "oneshot";
+
+        script = ''
+          if ! test -e ${cfg.dbpath}; then
+              install -d -m0700 -o ${cfg.user} ${cfg.dbpath}
+          fi
+        '';
+      };
+
+    systemd.services.redis =
+      { description = "Redis server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}";
+          User = cfg.user;
+        };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix
new file mode 100644
index 00000000000..6a29fc13211
--- /dev/null
+++ b/nixos/modules/services/databases/virtuoso.nix
@@ -0,0 +1,98 @@
+{ config, pkgs, ... }:
+let
+  cfg = config.services.virtuoso;
+  virtuosoUser = "virtuoso";
+  stateDir = "/var/lib/virtuoso";
+in
+with pkgs.lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.virtuoso = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Virtuoso Opensource database server.";
+      };
+
+      config = mkOption {
+        default = "";
+        description = "Extra options to put into Virtuoso configuration file.";
+      };
+
+      parameters = mkOption {
+        default = "";
+        description = "Extra options to put into [Parameters] section of Virtuoso configuration file.";
+      };
+
+      listenAddress = mkOption {
+	default = "1111";
+	example = "myserver:1323";
+        description = "ip:port or port to listen on.";
+      };
+
+      httpListenAddress = mkOption {
+	default = null;
+	example = "myserver:8080";
+        description = "ip:port or port for Virtuoso HTTP server to listen on.";
+      };
+
+      dirsAllowed = mkOption {
+	default = null;
+	example = "/www, /home/";
+        description = "A list of directories Virtuoso is allowed to access";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = virtuosoUser;
+        uid = config.ids.uids.virtuoso;
+        description = "virtuoso user";
+        home = stateDir;
+      };
+
+    jobs.virtuoso = {
+      name = "virtuoso";
+      startOn = "filesystem";
+
+      preStart = ''
+	mkdir -p ${stateDir}
+	chown ${virtuosoUser} ${stateDir}
+      '';
+
+      script = ''
+	cd ${stateDir}
+	${pkgs.virtuoso}/bin/virtuoso-t +foreground +configfile ${pkgs.writeText "virtuoso.ini" cfg.config}
+      '';
+    };
+
+    services.virtuoso.config = ''
+      [Database]
+      DatabaseFile=${stateDir}/x-virtuoso.db
+      TransactionFile=${stateDir}/x-virtuoso.trx
+      ErrorLogFile=${stateDir}/x-virtuoso.log
+      xa_persistent_file=${stateDir}/x-virtuoso.pxa
+
+      [Parameters]
+      ServerPort=${cfg.listenAddress}
+      RunAs=${virtuosoUser}
+      ${optionalString (cfg.dirsAllowed != null) "DirsAllowed=${cfg.dirsAllowed}"}
+      ${cfg.parameters}
+
+      [HTTPServer]
+      ${optionalString (cfg.httpListenAddress != null) "ServerPort=${cfg.httpListenAddress}"}
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/services/games/ghost-one.nix b/nixos/modules/services/games/ghost-one.nix
new file mode 100644
index 00000000000..815118be1c6
--- /dev/null
+++ b/nixos/modules/services/games/ghost-one.nix
@@ -0,0 +1,105 @@
+{pkgs, config, ...}:
+with pkgs.lib;
+let
+
+  cfg = config.services.ghostOne;
+  ghostUser = "ghostone";
+  stateDir = "/var/lib/ghost-one";
+
+in
+{
+
+  ###### interface
+
+  options = {
+    services.ghostOne = {
+
+      enable = mkOption {
+        default = false;
+        description = "Enable Ghost-One Warcraft3 game hosting server.";
+      };
+
+      language = mkOption {
+        default = "English";
+        check = lang: elem lang [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
+        description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
+      };
+
+      war3path = mkOption {
+        default = "";
+        description = ''
+          The path to your local Warcraft III directory, which must contain war3.exe, storm.dll, and game.dll.
+        '';
+      };
+
+      mappath = mkOption {
+        default = "";
+        description = ''
+          The path to the directory where you keep your map files. GHost One doesn't require
+          map files but if it has access to them it can send them to players and automatically
+          calculate most map config values. GHost One will search [bot_mappath + map_localpath]
+          for the map file (map_localpath is set in each map's config file).
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = "Extra configuration options.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = ghostUser;
+        uid = config.ids.uids.ghostOne;
+        description = "Ghost One game server user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = ghostUser;
+        gid = config.ids.gids.ghostOne;
+      };
+
+    services.ghostOne.config = ''
+#      bot_log = /dev/stderr
+      bot_language = ${pkgs.ghostOne}/share/ghost-one/languages/${cfg.language}.cfg
+      bot_war3path = ${cfg.war3path}
+
+      bot_mapcfgpath = mapcfgs
+      bot_savegamepath = savegames
+      bot_mappath = ${cfg.mappath}
+      bot_replaypath = replays
+    '';
+
+    jobs.ghostOne = {
+      name = "ghost-one";
+      script = ''
+        mkdir -p ${stateDir}
+        cd ${stateDir}
+        chown ${ghostUser}:${ghostUser} .
+
+        mkdir -p mapcfgs
+        chown ${ghostUser}:${ghostUser} mapcfgs
+
+        mkdir -p replays
+        chown ${ghostUser}:${ghostUser} replays
+
+        mkdir -p savegames
+        chown ${ghostUser}:${ghostUser} savegames
+
+        ln -sf ${pkgs.writeText "ghost.cfg" cfg.config} ghost.cfg
+        ln -sf ${pkgs.ghostOne}/share/ghost-one/ip-to-country.csv
+        ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${ghostUser} \
+          -c "LANG=C ${pkgs.ghostOne}/bin/ghost++"
+      '';
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix
new file mode 100644
index 00000000000..6a595f8306b
--- /dev/null
+++ b/nixos/modules/services/hardware/acpid.nix
@@ -0,0 +1,114 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  acpiConfDir = pkgs.runCommand "acpi-events" {}
+    ''
+      ensureDir $out
+      ${
+        # Generate a configuration file for each event. (You can't have
+        # multiple events in one config file...)
+        let f = event:
+          ''
+            fn=$out/${event.name}
+            echo "event=${event.event}" > $fn
+            echo "action=${pkgs.writeScript "${event.name}.sh" event.action}" >> $fn
+          '';
+        in pkgs.lib.concatMapStrings f events
+      }
+    '';
+
+  events = [powerEvent lidEvent acEvent];
+
+  # Called when the power button is pressed.
+  powerEvent =
+    { name = "power-button";
+      event = "button/power.*";
+      action =
+        ''
+          #! ${pkgs.bash}/bin/sh
+          ${config.services.acpid.powerEventCommands}
+        '';
+    };
+
+  # Called when the laptop lid is opened/closed.
+  lidEvent =
+    { name = "lid";
+      event = "button/lid.*";
+      action =
+        ''
+          #! ${pkgs.bash}/bin/sh
+          ${config.services.acpid.lidEventCommands}
+        '';
+    };
+
+  # Called when the AC power is connected or disconnected.
+  acEvent =
+    { name = "ac-power";
+      event = "ac_adapter.*";
+      action =
+        ''
+          #! ${pkgs.bash}/bin/sh
+          ${config.services.acpid.acEventCommands}
+        '';
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.acpid = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the ACPI daemon.";
+      };
+
+      powerEventCommands = mkOption {
+        default = "";
+        description = "Shell commands to execute on a button/power.* event.";
+      };
+
+      lidEventCommands = mkOption {
+        default = "";
+        description = "Shell commands to execute on a button/lid.* event.";
+      };
+
+      acEventCommands = mkOption {
+        default = "";
+        description = "Shell commands to execute on an ac_adapter.* event.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.acpid.enable {
+
+    jobs.acpid =
+      { description = "ACPI Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "systemd-udev-settle.service" ];
+
+        path = [ pkgs.acpid ];
+
+        daemonType = "fork";
+
+        exec = "acpid --confdir ${acpiConfDir}";
+
+        unitConfig.ConditionPathExists = [ "/proc/acpi" ];
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
new file mode 100644
index 00000000000..6bc0ad0bf77
--- /dev/null
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -0,0 +1,41 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.bluetooth.enable = mkOption {
+      default = false;
+      description = "Whether to enable support for Bluetooth.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.hardware.bluetooth.enable {
+
+    environment.systemPackages = [ pkgs.bluez pkgs.openobex pkgs.obexftp ];
+
+    services.udev.packages = [ pkgs.bluez ];
+
+    services.dbus.packages = [ pkgs.bluez ];
+
+    systemd.services."dbus-org.bluez" = {
+      description = "Bluetooth service";
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "org.bluez";
+        ExecStart = "${pkgs.bluez}/sbin/bluetoothd -n";
+      };
+      wantedBy = [ "bluetooth.target" ];
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/nvidia-optimus.nix b/nixos/modules/services/hardware/nvidia-optimus.nix
new file mode 100644
index 00000000000..4c0ce794d4f
--- /dev/null
+++ b/nixos/modules/services/hardware/nvidia-optimus.nix
@@ -0,0 +1,43 @@
+{ config, pkgs, ... }:
+
+let kernel = config.boot.kernelPackages; in
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.nvidiaOptimus.disable = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        Completely disable the NVIDIA graphics card and use the
+        integrated graphics processor instead.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.hardware.nvidiaOptimus.disable {
+    boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb"];
+    boot.kernelModules = [ "bbswitch" ];
+    boot.extraModulePackages = [ kernel.bbswitch ];
+
+    systemd.services.bbswitch = {
+      description = "Disable NVIDIA Card";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${kernel.bbswitch}/bin/discrete_vga_poweroff";
+        ExecStop = "${kernel.bbswitch}/bin/discrete_vga_poweron";
+      };
+      path = [ kernel.bbswitch ];
+    };
+  };
+
+}
diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix
new file mode 100644
index 00000000000..9f389efc06d
--- /dev/null
+++ b/nixos/modules/services/hardware/pcscd.nix
@@ -0,0 +1,46 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.pcscd = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the PCSC-Lite daemon.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  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/
+        '';
+
+        exec = "${pkgs.pcsclite}/sbin/pcscd";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/pommed.nix b/nixos/modules/services/hardware/pommed.nix
new file mode 100644
index 00000000000..32599554fc1
--- /dev/null
+++ b/nixos/modules/services/hardware/pommed.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options.services.hardware.pommed = {
+    enable = mkOption {
+      default = false;
+       description = ''
+        Whether to use the pommed tool to handle Apple laptop keyboard hotkeys.
+      '';
+    };
+
+    configFile = mkOption {
+      default = "${pkgs.pommed}/etc/pommed.conf";
+      description = ''
+        The contents of the pommed.conf file.
+      '';
+    };
+  };
+
+  config = mkIf config.services.hardware.pommed.enable {
+    environment.systemPackages = [ pkgs.polkit ];
+
+    environment.etc = [
+      { source = config.services.hardware.pommed.configFile;
+        target = "pommed.conf";
+      }
+    ];
+
+    services.dbus.packages = [ pkgs.pommed ];
+
+    jobs.pommed = { name = "pommed";
+
+      description = "Pommed hotkey management";
+
+      startOn = "started dbus";
+
+      postStop = "rm -f /var/run/pommed.pid";
+
+      exec = "${pkgs.pommed}/bin/pommed";
+
+      daemonType = "fork";
+
+      path = [ pkgs.eject ];
+    };
+  };
+}
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
new file mode 100644
index 00000000000..905445f22c1
--- /dev/null
+++ b/nixos/modules/services/hardware/sane.nix
@@ -0,0 +1,40 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.sane.enable = mkOption {
+      default = false;
+      description = "Enable support for SANE scanners.";
+    };
+
+    hardware.sane.snapshot = mkOption {
+      default = false;
+      description = "Use a development snapshot of SANE scanner drivers.";
+    };
+
+  };
+
+
+  ###### implementation
+
+    config = let pkg = if config.hardware.sane.snapshot
+                          then pkgs.saneBackendsGit
+                          else pkgs.saneBackends;
+      in mkIf config.hardware.sane.enable {
+           environment.systemPackages = [ pkg ];
+           services.udev.packages = [ pkg ];
+           
+           users.extraGroups = singleton {
+             name = "scanner";
+             gid = config.ids.gids.scanner;
+           };
+
+      };
+
+}
diff --git a/nixos/modules/services/hardware/thinkfan.nix b/nixos/modules/services/hardware/thinkfan.nix
new file mode 100644
index 00000000000..b39c9cb1d9b
--- /dev/null
+++ b/nixos/modules/services/hardware/thinkfan.nix
@@ -0,0 +1,95 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.thinkfan;
+  configFile = pkgs.writeText "thinkfan.conf" ''
+    # ATTENTION: There is only very basic sanity checking on the configuration.
+    # That means you can set your temperature limits as insane as you like. You
+    # can do anything stupid, e.g. turn off your fan when your CPU reaches 70°C.
+    #
+    # That's why this program is called THINKfan: You gotta think for yourself.
+    #
+    ######################################################################
+    #
+    # IBM/Lenovo Thinkpads (thinkpad_acpi, /proc/acpi/ibm)
+    # ====================================================
+    #
+    # IMPORTANT:
+    #
+    # To keep your HD from overheating, you have to specify a correction value for
+    # the sensor that has the HD's temperature. You need to do this because
+    # thinkfan uses only the highest temperature it can find in the system, and
+    # that'll most likely never be your HD, as most HDs are already out of spec
+    # when they reach 55 °C.
+    # Correction values are applied from left to right in the same order as the
+    # temperatures are read from the file.
+    #
+    # For example:
+    # sensor /proc/acpi/ibm/thermal (0, 0, 10)
+    # will add a fixed value of 10 °C the 3rd value read from that file. Check out
+    # http://www.thinkwiki.org/wiki/Thermal_Sensors to find out how much you may
+    # want to add to certain temperatures.
+    
+    #  Syntax:
+    #  (LEVEL, LOW, HIGH)
+    #  LEVEL is the fan level to use (0-7 with thinkpad_acpi)
+    #  LOW is the temperature at which to step down to the previous level
+    #  HIGH is the temperature at which to step up to the next level
+    #  All numbers are integers.
+    #
+
+    sensor ${cfg.sensor} (0, 10, 15, 2, 10, 5, 0, 3, 0, 3)
+    
+    (0,     0,      55)
+    (1,     48,     60)
+    (2,     50,     61)
+    (3,     52,     63)
+    (6,     56,     65)
+    (7,     60,     85)
+    (127,   80,     32767)
+  '';
+
+in {
+
+  options = {
+
+    services.thinkfan = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable thinkfan, fan controller for ibm/lenovo thinkpads.
+        '';
+      };
+
+      sensor = mkOption {
+        default = "/proc/acpi/ibm/thermal";
+        description =''
+          Sensor used by thinkfan
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.thinkfan ];
+
+    systemd.services.thinkfan = {
+      description = "Thinkfan";
+      after = [ "basic.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.thinkfan ];
+      serviceConfig.ExecStart = "${pkgs.thinkfan}/bin/thinkfan -n -c ${configFile}";
+    };
+
+    boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1";
+
+  };
+
+}
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
new file mode 100644
index 00000000000..37dba8ce71d
--- /dev/null
+++ b/nixos/modules/services/hardware/udev.nix
@@ -0,0 +1,239 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) stdenv writeText procps;
+
+  udev = config.systemd.package;
+
+  cfg = config.services.udev;
+
+  extraUdevRules = pkgs.writeTextFile {
+    name = "extra-udev-rules";
+    text = cfg.extraRules;
+    destination = "/etc/udev/rules.d/10-local.rules";
+  };
+
+  nixosRules = ''
+    # Miscellaneous devices.
+    KERNEL=="kvm",                  MODE="0666"
+    KERNEL=="kqemu",                MODE="0666"
+  '';
+
+  # Perform substitutions in all udev rules files.
+  udevRules = stdenv.mkDerivation {
+    name = "udev-rules";
+    buildCommand = ''
+      mkdir -p $out
+      shopt -s nullglob
+
+      # Set a reasonable $PATH for programs called by udev rules.
+      echo 'ENV{PATH}="${udevPath}/bin:${udevPath}/sbin"' > $out/00-path.rules
+
+      # Add the udev rules from other packages.
+      for i in ${toString cfg.packages}; do
+        echo "Adding rules for package $i"
+        for j in $i/{etc,lib}/udev/rules.d/*; do
+          echo "Copying $j to $out/$(basename $j)"
+          cat $j > $out/$(basename $j)
+        done
+      done
+
+      # Fix some paths in the standard udev rules.  Hacky.
+      for i in $out/*.rules; do
+        substituteInPlace $i \
+          --replace \"/sbin/modprobe \"${config.system.sbin.modprobe}/sbin/modprobe \
+          --replace \"/sbin/mdadm \"${pkgs.mdadm}/sbin/mdadm \
+          --replace \"/sbin/blkid \"${pkgs.utillinux}/sbin/blkid \
+          --replace \"/bin/mount \"${pkgs.utillinux}/bin/mount
+      done
+
+      echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev... "
+      import_progs=$(grep 'IMPORT{program}="[^/$]' $out/* |
+        sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq)
+      run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="[^/$]' |
+        sed -e 's/.*RUN+="\([^ "]*\)[ "].*/\1/' | uniq)
+      for i in $import_progs $run_progs; do
+        if [[ ! -x ${pkgs.udev}/lib/udev/$i && ! $i =~ socket:.* ]]; then
+          echo "FAIL"
+          echo "$i is called in udev rules but not installed by udev"
+          exit 1
+        fi
+      done
+      echo "OK"
+
+      echo -n "Checking that all programs called by absolute paths in udev rules exist... "
+      import_progs=$(grep 'IMPORT{program}="\/' $out/* |
+        sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq)
+      run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="/' |
+        sed -e 's/.*RUN+="\([^ "]*\)[ "].*/\1/' | uniq)
+      for i in $import_progs $run_progs; do
+        if [[ ! -x $i ]]; then
+          echo "FAIL"
+          echo "$i is called in udev rules but not installed by udev"
+          exit 1
+        fi
+      done
+      echo "OK"
+
+      echo "Consider fixing the following udev rules:"
+      for i in ${toString cfg.packages}; do
+        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
+      ''}
+
+      # If auto-configuration is disabled, then remove
+      # udev's 80-drivers.rules file, which contains rules for
+      # automatically calling modprobe.
+      ${optionalString (!config.boot.hardwareScan) ''
+        ln -s /dev/null $out/80-drivers.rules
+      ''}
+    ''; # */
+  };
+
+  # Udev has a 512-character limit for ENV{PATH}, so create a symlink
+  # tree to work around this.
+  udevPath = pkgs.buildEnv {
+    name = "udev-path";
+    paths = cfg.path;
+    pathsToLink = [ "/bin" "/sbin" ];
+    ignoreCollisions = true;
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.hardwareScan = mkOption {
+      default = true;
+      description = ''
+        Whether to try to load kernel modules for all detected hardware.
+        Usually this does a good job of providing you with the modules
+        you need, but sometimes it can crash the system or cause other
+        nasty effects.
+      '';
+    };
+
+    services.udev = {
+
+      packages = mkOption {
+        default = [];
+        merge = mergeListOption;
+        description = ''
+          List of packages containing <command>udev</command> rules.
+          All files found in
+          <filename><replaceable>pkg</replaceable>/etc/udev/rules.d</filename> and
+          <filename><replaceable>pkg</replaceable>/lib/udev/rules.d</filename>
+          will be included.
+        '';
+      };
+
+      path = mkOption {
+        default = [];
+        merge = mergeListOption;
+        description = ''
+          Packages added to the <envar>PATH</envar> environment variable when
+          executing programs from Udev rules.
+        '';
+      };
+
+      extraRules = mkOption {
+        default = "";
+        example = ''
+          KERNEL=="eth*", ATTR{address}=="00:1D:60:B9:6D:4F", NAME="my_fast_network_card"
+        '';
+        type = types.lines;
+        description = ''
+          Additional <command>udev</command> rules. They'll be written
+          into file <filename>10-local.rules</filename>. Thus they are
+          read before all other rules.
+        '';
+      };
+
+    };
+
+    hardware.firmware = mkOption {
+      default = [];
+      example = [ "/root/my-firmware" ];
+      merge = mergeListOption;
+      description = ''
+        List of directories containing firmware files.  Such files
+        will be loaded automatically if the kernel asks for them
+        (i.e., when it has detected specific hardware that requires
+        firmware to function).  If more than one path contains a
+        firmware file with the same name, the first path in the list
+        takes precedence.  Note that you must rebuild your system if
+        you add files to any of these directories.  For quick testing,
+        put firmware files in /root/test-firmware and add that
+        directory to the list.
+        Note that you can also add firmware packages to this
+        list as these are directories in the nix store.
+      '';
+      apply = list: pkgs.buildEnv {
+        name = "firmware";
+        paths = list;
+        pathsToLink = [ "/" ];
+        ignoreCollisions = true;
+      };
+    };
+
+    networking.usePredictableInterfaceNames = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to assign <link
+        xlink:href='http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames'>predictable
+        names to network interfaces</link>.  If enabled, interfaces
+        are assigned names that contain topology information
+        (e.g. <literal>wlp3s0</literal>) and thus should be stable
+        across reboots.  If disabled, names depend on the order in
+        which interfaces are discovered by the kernel, which may
+        change randomly across reboots; for instance, you may find
+        <literal>eth0</literal> and <literal>eth1</literal> flipping
+        unpredictably.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    services.udev.extraRules = nixosRules;
+
+    services.udev.packages = [ extraUdevRules ];
+
+    services.udev.path = [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.utillinux udev ];
+
+    environment.etc =
+      [ { source = udevRules;
+          target = "udev/rules.d";
+        }
+      ];
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isEnabled "UNIX")
+      (isYes "INOTIFY_USER")
+      (isYes "NET")
+    ];
+
+    boot.extraModprobeConfig = "options firmware_class path=${config.hardware.firmware}";
+
+    system.activationScripts.clearHotplug =
+      ''
+        echo "" > /proc/sys/kernel/hotplug
+      '';
+
+  };
+}
diff --git a/nixos/modules/services/hardware/udisks.nix b/nixos/modules/services/hardware/udisks.nix
new file mode 100644
index 00000000000..1ba17c589d2
--- /dev/null
+++ b/nixos/modules/services/hardware/udisks.nix
@@ -0,0 +1,44 @@
+# Udisks daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.udisks = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable Udisks, a DBus service that allows
+          applications to query and manipulate storage devices.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.udisks.enable {
+
+    environment.systemPackages = [ pkgs.udisks ];
+
+    services.dbus.packages = [ pkgs.udisks ];
+
+    system.activationScripts.udisks =
+      ''
+        mkdir -m 0755 -p /var/lib/udisks
+      '';
+
+    services.udev.packages = [ pkgs.udisks ];
+  };
+
+}
diff --git a/nixos/modules/services/hardware/udisks2.nix b/nixos/modules/services/hardware/udisks2.nix
new file mode 100644
index 00000000000..eae4172ccb3
--- /dev/null
+++ b/nixos/modules/services/hardware/udisks2.nix
@@ -0,0 +1,53 @@
+# Udisks daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.udisks2 = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable Udisks, a DBus service that allows
+          applications to query and manipulate storage devices.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.udisks2.enable {
+
+    environment.systemPackages = [ pkgs.udisks2 ];
+
+    services.dbus.packages = [ pkgs.udisks2 ];
+
+    system.activationScripts.udisks2 =
+      ''
+        mkdir -m 0755 -p /var/lib/udisks2
+      '';
+
+    #services.udev.packages = [ pkgs.udisks2 ];
+    
+    systemd.services.udisks2 = {
+      description = "Udisks2 service";
+      serviceConfig = {
+        Type = "dbus";
+        BusName = "org.freedesktop.UDisks2";
+        ExecStart = "${pkgs.udisks2}/lib/udisks2/udisksd --no-debug";
+      };
+    };
+  };
+
+}
diff --git a/nixos/modules/services/hardware/upower.nix b/nixos/modules/services/hardware/upower.nix
new file mode 100644
index 00000000000..5d1658adb75
--- /dev/null
+++ b/nixos/modules/services/hardware/upower.nix
@@ -0,0 +1,64 @@
+# Upower daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.upower = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable Upower, a DBus service that provides power
+          management support to applications.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.upower.enable {
+
+    environment.systemPackages = [ pkgs.upower ];
+
+    services.dbus.packages = [ pkgs.upower ];
+
+    services.udev.packages = [ pkgs.upower ];
+
+    systemd.services.upower =
+      { description = "Power Management Daemon";
+        path = [ pkgs.glib ]; # needed for gdbus
+        serviceConfig =
+          { Type = "dbus";
+            BusName = "org.freedesktop.UPower";
+            ExecStart = "@${pkgs.upower}/libexec/upowerd upowerd";
+          };
+      };
+
+    system.activationScripts.upower =
+      ''
+        mkdir -m 0755 -p /var/lib/upower
+      '';
+
+    # The upower daemon seems to get stuck after doing a suspend
+    # (i.e. subsequent suspend requests will say "Sleep has already
+    # been requested and is pending").  So as a workaround, restart
+    # the daemon.
+    powerManagement.resumeCommands =
+      ''
+        ${config.systemd.package}/bin/systemctl try-restart upower
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/logging/klogd.nix b/nixos/modules/services/logging/klogd.nix
new file mode 100644
index 00000000000..d7d0bbf89a5
--- /dev/null
+++ b/nixos/modules/services/logging/klogd.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  ###### interface
+
+  options = {
+
+    services.klogd.enable = mkOption {
+      type = types.bool;
+      default = versionOlder (getVersion config.boot.kernelPackages.kernel) "3.5";
+      description = ''
+        Whether to enable klogd, the kernel log message processing
+        daemon.  Since systemd handles logging of kernel messages on
+        Linux 3.5 and later, this is only useful if you're running an
+        older kernel.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.klogd.enable {
+
+    jobs.klogd =
+      { description = "Kernel Log Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+
+        path = [ pkgs.sysklogd ];
+
+        exec =
+          "klogd -c 1 -2 -n " +
+          "-k $(dirname $(readlink -f /run/booted-system/kernel))/System.map";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
new file mode 100644
index 00000000000..23f21b6a754
--- /dev/null
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -0,0 +1,231 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.logcheck;
+
+  defaultRules = pkgs.runCommand "logcheck-default-rules" {} ''
+                   cp -prd ${pkgs.logcheck}/etc/logcheck $out
+                   chmod u+w $out
+                   rm $out/logcheck.*
+                 '';
+
+  rulesDir = pkgs.symlinkJoin "logcheck-rules-dir" ([ defaultRules ] ++ cfg.extraRulesDirs);
+
+  configFile = pkgs.writeText "logcheck.conf" cfg.config;
+
+  logFiles = pkgs.writeText "logcheck.logfiles" cfg.files;
+
+  flags = "-r ${rulesDir} -c ${configFile} -L ${logFiles} -${levelFlag} -m ${cfg.mailTo}";
+
+  levelFlag = getAttrFromPath [cfg.level]
+    { "paranoid"    = "p";
+      "server"      = "s";
+      "workstation" = "w";
+    };
+
+  cronJob = ''
+    @reboot   logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags}
+    2 ${cfg.timeOfDay} * * * logcheck env PATH=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck ${flags}
+  '';
+
+  writeIgnoreRule = name: {level, regex, ...}:
+    pkgs.writeTextFile
+      { inherit name;
+        destination = "/ignore.d.${level}/${name}";
+        text = ''
+          ^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ${regex}
+        '';
+      };
+
+  writeIgnoreCronRule = name: {level, user, regex, cmdline, ...}:
+    let escapeRegex = escape (stringToCharacters "\\[]{}()^$?*+|.");
+        cmdline_ = builtins.unsafeDiscardStringContext cmdline;
+        re = if regex != "" then regex else if cmdline_ == "" then ".*" else escapeRegex cmdline_;
+    in writeIgnoreRule "cron-${name}" {
+      inherit level;
+      regex = ''
+        (/usr/bin/)?cron\[[0-9]+\]: \(${user}\) CMD \(${re}\)$
+      '';
+    };
+
+  levelOption = mkOption {
+    default = "server";
+    type = types.uniq types.string;
+    description = ''
+      Set the logcheck level. Either "workstation", "server", or "paranoid".
+    '';
+  };
+
+  ignoreOptions = {
+    level = levelOption;
+
+    regex = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      description = ''
+        Regex specifying which log lines to ignore.
+      '';
+    };
+  };
+
+  ignoreCronOptions = {
+    user = mkOption {
+      default = "root";
+      type = types.uniq types.string;
+      description = ''
+        User that runs the cronjob.
+      '';
+    };
+
+    cmdline = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      description = ''
+        Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
+      '';
+    };
+
+    timeArgs = mkOption {
+      default = null;
+      type = types.nullOr (types.uniq types.string);
+      example = "02 06 * * *";
+      description = ''
+        "min hr dom mon dow" crontab time args, to auto-create a cronjob too.
+        Leave at null to not do this and just add a logcheck ignore rule.
+      '';
+    };
+  };
+
+in
+{
+  options = {
+    services.logcheck = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable the logcheck cron job.
+        '';
+      };
+
+      user = mkOption {
+        default = "logcheck";
+        type = types.uniq types.string;
+        description = ''
+          Username for the logcheck user.
+        '';
+      };
+
+      timeOfDay = mkOption {
+        default = "*";
+        example = "6";
+        type = types.uniq types.string;
+        description = ''
+          Time of day to run logcheck. A logcheck will be scheduled at xx:02 each day.
+          Leave default (*) to run every hour. Of course when nothing special was logged,
+          logcheck will be silent.
+        '';
+      };
+
+      mailTo = mkOption {
+        default = "root";
+        example = "you@domain.com";
+        type = types.uniq types.string;
+        description = ''
+          Email address to send reports to.
+        '';
+      };
+
+      level = mkOption {
+        default = "server";
+        type = types.uniq types.string;
+        description = ''
+          Set the logcheck level. Either "workstation", "server", or "paranoid".
+        '';
+      };
+
+      config = mkOption {
+        default = "FQDN=1";
+        type = types.string;
+        description = ''
+          Config options that you would like in logcheck.conf.
+        '';
+      };
+
+      files = mkOption {
+        default = [ "/var/log/messages" ];
+        type = types.listOf types.path;
+        example = [ "/var/log/messages" "/var/log/mail" ];
+        description = ''
+          Which log files to check.
+        '';
+      };
+
+      extraRulesDirs = mkOption {
+        default = [];
+        example = "/etc/logcheck";
+        type = types.listOf types.path;
+        description = ''
+          Directories with extra rules.
+        '';
+      };
+
+      ignore = mkOption {
+        default = {};
+        description = ''
+          This option defines extra ignore rules.
+        '';
+        type = types.loaOf types.optionSet;
+        options = [ ignoreOptions ];
+      };
+
+      ignoreCron = mkOption {
+        default = {};
+        description = ''
+          This option defines extra ignore rules for cronjobs.
+        '';
+        type = types.loaOf types.optionSet;
+        options = [ ignoreOptions ignoreCronOptions ];
+      };
+
+      extraGroups = mkOption {
+        default = [];
+        type = types.listOf types.string;
+        example = [ "postdrop" "mongodb" ];
+        description = ''
+          Extra groups for the logcheck user, for example to be able to use sendmail,
+          or to access certain log files.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.logcheck.extraRulesDirs =
+        mapAttrsToList writeIgnoreRule cfg.ignore
+        ++ mapAttrsToList writeIgnoreCronRule cfg.ignoreCron;
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        shell = "/bin/sh";
+        description = "Logcheck user account";
+        extraGroups = cfg.extraGroups;
+      };
+
+    system.activationScripts.logcheck = ''
+      mkdir -m 700 -p /var/{lib,lock}/logcheck
+      chown ${cfg.user} /var/{lib,lock}/logcheck
+    '';
+
+    services.cron.systemCronJobs =
+        let withTime = name: {timeArgs, ...}: ! (builtins.isNull timeArgs);
+            mkCron = name: {user, cmdline, timeArgs, ...}: ''
+              ${timeArgs} ${user} ${cmdline}
+            '';
+        in mapAttrsToList mkCron (filterAttrs withTime cfg.ignoreCron)
+           ++ [ cronJob ];
+  };
+}
diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix
new file mode 100644
index 00000000000..c6c0d2ea238
--- /dev/null
+++ b/nixos/modules/services/logging/logrotate.nix
@@ -0,0 +1,38 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.logrotate;
+
+  configFile = pkgs.writeText "logrotate.conf"
+    cfg.config;
+
+  cronJob = ''
+    5 * * * * root ${pkgs.logrotate}/sbin/logrotate ${configFile}
+  '';
+
+in
+{
+  options = {
+    services.logrotate = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable the logrotate cron job
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          The contents of the logrotate config file
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.cron.systemCronJobs = [ cronJob ];
+  };
+}
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
new file mode 100644
index 00000000000..2f0eea50526
--- /dev/null
+++ b/nixos/modules/services/logging/logstash.nix
@@ -0,0 +1,161 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.logstash;
+
+  listToConfig = list: "[ " + (concatStringsSep ", " (map exprToConfig list)) + " ]";
+
+  hashToConfig = attrs:
+    let
+      attrNameToConfigList = name:
+        [ (exprToConfig name)  (exprToConfig (getAttr name attrs)) ];
+    in
+      "[ " +
+      (concatStringsSep ", " (map attrNameToConfigList (attrNames attrs))) +
+      " ]";
+
+  valueToConfig = nvpair: let name = nvpair.name; value = nvpair.value; in
+    if (isAttrs value) && ((!(value ? __type)) || value.__type == "repeated")
+      then ''
+        ${name} {
+          ${exprToConfig value}
+        }
+      ''
+      else "${name} => ${exprToConfig value}";
+
+  repeatedAttrsToConfig = values:
+      concatStringsSep "\n" (map valueToConfig values);
+
+  attrsToConfig = attrs:
+    let
+      attrToConfig = name: valueToConfig {
+        inherit name;
+        value = (getAttr name attrs);
+      };
+    in
+      concatStringsSep "\n" (map attrToConfig (attrNames attrs));
+
+  exprToConfig = expr:
+    let
+      isCustomType = expr: (isAttrs expr) && (expr ? __type);
+
+      isFloat = expr: (isCustomType expr) && (expr.__type == "float");
+
+      isHash = expr: (isCustomType expr) && (expr.__type == "hash");
+
+      isRepeatedAttrs = expr: (isCustomType expr) && (expr.__type == "repeated");
+    in
+      if builtins.isBool expr then (if expr then "true" else "false") else
+      if builtins.isString expr then ''"${expr}"'' else
+      if builtins.isInt expr then toString expr else
+      if isFloat expr then expr.value else
+      if isList expr then listToConfig expr else
+      if isHash expr then hashToConfig expr.value else
+      if isRepeatedAttrs expr then repeatedAttrsToConfig expr.values
+      else attrsToConfig expr;
+
+  mergeConfigs = configs:
+    let
+      op = attrs: newAttrs:
+        let
+          isRepeated = newAttrs ? __type && newAttrs.__type == "repeated";
+        in {
+            values = attrs.values ++ (if isRepeated then newAttrs.values else
+              map (name: { inherit name; value = getAttr name newAttrs; })
+              (attrNames newAttrs));
+          };
+    in (foldl op { values = []; } configs) // { __type = "repeated"; };
+
+in
+
+{
+  ###### interface
+
+  options = {
+    services.logstash = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable logstash.
+        '';
+      };
+
+      inputConfig = mkOption {
+        default = {};
+        description = ''
+          An attribute set (or an expression generated by mkNameValuePairs)
+          representing a logstash configuration's input section.
+          Logstash configs are name-value pairs, where values can be bools,
+          strings, numbers, arrays, hashes, or other name-value pairs,
+          and names are strings that can be repeated. Name-value pairs with no
+          repeats are represented by attr sets. Bools, strings, ints, and
+          arrays are mapped directly. Name-value pairs with repeats can be
+          generated by the config.lib.logstash.mkNameValuePairs function, which
+          takes a list of attrsets and combines them while preserving attribute
+          name duplicates if they occur. Similarly, there are the mkFloat and
+          mkHash functions, which take a string representation of a float and an
+          attrset, respectively.
+        '';
+        merge = mergeConfigs;
+      };
+
+      filterConfig = mkOption {
+        default = {};
+        description = ''
+          An attribute set (or an expression generated by mkNameValuePairs)
+          representing a logstash configuration's filter section.
+          See inputConfig description for details.
+        '';
+        merge = mergeConfigs;
+      };
+
+      outputConfig = mkOption {
+        default = {};
+        description = ''
+          An attribute set (or an expression generated by mkNameValuePairs)
+          representing a logstash configuration's output section.
+          See inputConfig description for details.
+        '';
+        merge = mergeConfigs;
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [ {
+    lib.logstash = {
+      mkFloat = stringRep: { __type = "float"; value = stringRep; };
+
+      mkHash = attrs: { __type = "hash"; value = attrs; };
+
+      mkNameValuePairs = mergeConfigs;
+    };
+  } ( mkIf cfg.enable {
+    systemd.services.logstash = with pkgs; {
+      description = "Logstash daemon";
+
+      wantedBy = [ "multi-user.target" ];
+
+      path = [ jre ];
+
+      script = "cd /tmp && exec java -jar ${logstash} agent -f ${writeText "logstash.conf" ''
+        input {
+          ${exprToConfig cfg.inputConfig}
+        }
+
+        filter {
+          ${exprToConfig cfg.filterConfig}
+        }
+
+        output {
+          ${exprToConfig cfg.outputConfig}
+        }
+      ''} &> /var/log/logstash.log";
+    };
+  })];
+}
diff --git a/nixos/modules/services/logging/rsyslogd.nix b/nixos/modules/services/logging/rsyslogd.nix
new file mode 100644
index 00000000000..680c7a912c1
--- /dev/null
+++ b/nixos/modules/services/logging/rsyslogd.nix
@@ -0,0 +1,105 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.rsyslogd;
+
+  syslogConf = pkgs.writeText "syslog.conf" ''
+    $ModLoad imuxsock
+    $SystemLogSocketName /run/systemd/journal/syslog
+    $WorkDirectory /var/spool/rsyslog
+
+    ${cfg.defaultConfig}
+    ${cfg.extraConfig}
+  '';
+
+  defaultConf = ''
+    # "local1" is used for dhcpd messages.
+    local1.*                     -/var/log/dhcpd
+
+    mail.*                       -/var/log/mail
+
+    *.=warning;*.=err            -/var/log/warn
+    *.crit                        /var/log/warn
+
+    *.*;mail.none;local1.none    -/var/log/messages
+  '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.rsyslogd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable syslogd.  Note that systemd also logs
+          syslog messages, so you normally don't need to run syslogd.
+        '';
+      };
+
+      defaultConfig = mkOption {
+        type = types.string;
+        default = defaultConf;
+        description = ''
+          The default <filename>syslog.conf</filename> file configures a
+          fairly standard setup of log files, which can be extended by
+          means of <varname>extraConfig</varname>.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.string;
+        default = "";
+        example = "news.* -/var/log/news";
+        description = ''
+          Additional text appended to <filename>syslog.conf</filename>,
+          i.e. the contents of <varname>defaultConfig</varname>.
+        '';
+      };
+
+      extraParams = mkOption {
+        type = types.listOf types.string;
+        default = [ ];
+        example = [ "-m 0" ];
+        description = ''
+          Additional parameters passed to <command>rsyslogd</command>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.rsyslog ];
+
+    systemd.services.syslog =
+      { description = "Syslog Daemon";
+
+        requires = [ "syslog.socket" ];
+
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig =
+          { ExecStart = "${pkgs.rsyslog}/sbin/rsyslogd ${toString cfg.extraParams} -f ${syslogConf} -n";
+            ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/spool/rsyslog";
+            # Prevent syslogd output looping back through journald.
+            StandardOutput = "null";
+          };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/logging/syslogd.nix b/nixos/modules/services/logging/syslogd.nix
new file mode 100644
index 00000000000..24cd5c1567e
--- /dev/null
+++ b/nixos/modules/services/logging/syslogd.nix
@@ -0,0 +1,124 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.syslogd;
+
+  syslogConf = pkgs.writeText "syslog.conf" ''
+    ${if (cfg.tty != "") then "kern.warning;*.err;authpriv.none /dev/${cfg.tty}" else ""}
+    ${cfg.defaultConfig}
+    ${cfg.extraConfig}
+  '';
+
+  defaultConf = ''
+    # Send emergency messages to all users.
+    *.emerg                       *
+
+    # "local1" is used for dhcpd messages.
+    local1.*                     -/var/log/dhcpd
+
+    mail.*                       -/var/log/mail
+
+    *.=warning;*.=err            -/var/log/warn
+    *.crit                        /var/log/warn
+
+    *.*;mail.none;local1.none    -/var/log/messages
+  '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.syslogd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable syslogd.  Note that systemd also logs
+          syslog messages, so you normally don't need to run syslogd.
+        '';
+      };
+
+      tty = mkOption {
+        type = types.uniq types.string;
+        default = "tty10";
+        description = ''
+          The tty device on which syslogd will print important log
+          messages. Leave this option blank to disable tty logging.
+        '';
+      };
+
+      defaultConfig = mkOption {
+        type = types.string;
+        default = defaultConf;
+        description = ''
+          The default <filename>syslog.conf</filename> file configures a
+          fairly standard setup of log files, which can be extended by
+          means of <varname>extraConfig</varname>.
+        '';
+      };
+
+      enableNetworkInput = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Accept logging through UDP. Option -r of syslogd(8).
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.string;
+        default = "";
+        example = "news.* -/var/log/news";
+        description = ''
+          Additional text appended to <filename>syslog.conf</filename>,
+          i.e. the contents of <varname>defaultConfig</varname>.
+        '';
+      };
+
+      extraParams = mkOption {
+        type = types.listOf types.string;
+        default = [ ];
+        example = [ "-m 0" ];
+        description = ''
+          Additional parameters passed to <command>syslogd</command>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.sysklogd ];
+
+    services.syslogd.extraParams = optional cfg.enableNetworkInput "-r";
+
+    # FIXME: restarting syslog seems to break journal logging.
+    systemd.services.syslog =
+      { description = "Syslog Daemon";
+
+        requires = [ "syslog.socket" ];
+
+        wantedBy = [ "multi-user.target" ];
+
+        serviceConfig =
+          { ExecStart = "${pkgs.sysklogd}/sbin/syslogd ${toString cfg.extraParams} -f ${syslogConf} -n";
+            # Prevent syslogd output looping back through journald.
+            StandardOutput = "null";
+          };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
new file mode 100644
index 00000000000..5f8e8e1ade3
--- /dev/null
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -0,0 +1,168 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.dovecot2;
+
+  dovecotConf =
+    ''
+      base_dir = /var/run/dovecot2/
+
+      protocols = ${optionalString cfg.enableImap "imap"} ${optionalString cfg.enablePop3 "pop3"}
+    ''
+    + (if cfg.sslServerCert!="" then
+    ''
+      ssl_cert = <${cfg.sslServerCert}
+      ssl_key = <${cfg.sslServerKey}
+      ssl_ca = <${cfg.sslCACert}
+      disable_plaintext_auth = yes
+    '' else ''
+      ssl = no
+      disable_plaintext_auth = no
+    '')
+
+    + ''
+      default_internal_user = ${cfg.user}
+
+      mail_location = ${cfg.mailLocation}
+
+      maildir_copy_with_hardlinks = yes
+
+      auth_mechanisms = plain login
+      service auth {
+        user = root
+      }
+      userdb {
+        driver = passwd
+      }
+      passdb {
+        driver = pam
+        args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2
+      }
+
+      pop3_uidl_format = %08Xv%08Xu
+    '' + cfg.extraConfig;
+
+  confFile = pkgs.writeText "dovecot.conf" dovecotConf;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dovecot2 = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the Dovecot 2.x POP3/IMAP server.";
+      };
+
+      enablePop3 = mkOption {
+        default = true;
+        description = "Start the POP3 listener (when Dovecot is enabled).";
+      };
+
+      enableImap = mkOption {
+        default = true;
+        description = "Start the IMAP listener (when Dovecot is enabled).";
+      };
+
+      user = mkOption {
+        default = "dovecot2";
+        description = "Dovecot user name.";
+      };
+
+      group = mkOption {
+        default = "dovecot2";
+        description = "Dovecot group name.";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        example = "mail_debug = yes";
+        description = "Additional entries to put verbatim into Dovecot's config file.";
+      };
+
+      mailLocation = mkOption {
+        default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */
+        example = "maildir:~/mail:INBOX=/var/spool/mail/%u";
+        description = ''
+          Location that dovecot will use for mail folders. Dovecot mail_location option.
+        '';
+      };
+
+      sslServerCert = mkOption {
+        default = "";
+        description = "Server certificate";
+      };
+
+      sslCACert = mkOption {
+        default = "";
+        description = "CA certificate used by the server certificate.";
+      };
+
+      sslServerKey = mkOption {
+        default = "";
+        description = "Server key.";
+      };
+
+      showPAMFailure = mkOption {
+        default = false;
+        description = "Show the PAM failure message on authentication error (useful for OTPW).";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.dovecot2.enable {
+
+    security.pam.services = [ { name = "dovecot2"; } ];
+
+    users.extraUsers = [
+      { name = cfg.user;
+        uid = config.ids.uids.dovecot2;
+        description = "Dovecot user";
+        group = cfg.group;
+      }
+      { name = "dovenull";
+        uid = config.ids.uids.dovenull2;
+        description = "Dovecot user for untrusted logins";
+        group = cfg.group;
+      }
+    ];
+
+    users.extraGroups = singleton
+      { name = cfg.group;
+        gid = config.ids.gids.dovecot2;
+      };
+
+    jobs.dovecot2 =
+      { description = "Dovecot IMAP/POP3 server";
+
+        startOn = "started networking";
+
+        preStart =
+          ''
+            ${pkgs.coreutils}/bin/mkdir -p /var/run/dovecot2 /var/run/dovecot2/login
+            ${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} /var/run/dovecot2
+          '';
+
+        exec = "${pkgs.dovecot}/sbin/dovecot -F -c ${confFile}";
+      };
+
+    environment.systemPackages = [ pkgs.dovecot ];
+
+    assertions = [{ assertion = cfg.enablePop3 || cfg.enableImap;
+                    message = "dovecot needs at least one of the IMAP or POP3 listeners enabled";}];
+
+  };
+
+}
diff --git a/nixos/modules/services/mail/freepops.nix b/nixos/modules/services/mail/freepops.nix
new file mode 100644
index 00000000000..8f6e9382607
--- /dev/null
+++ b/nixos/modules/services/mail/freepops.nix
@@ -0,0 +1,87 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.mail.freepopsd;
+in
+
+{
+  options = {
+    services.mail.freepopsd = {
+      enable = mkOption {
+        default = false;
+        type = with types; bool;
+        description = ''
+          Enables Freepops, a POP3 webmail wrapper.
+        '';
+      };
+
+      port = mkOption {
+        default = 2000;
+        type = with types; uniq int;
+        description = ''
+          Port on which the pop server will listen.
+        '';
+      };
+
+      threads = mkOption {
+        default = 5;
+        type = with types; uniq int;
+        description = ''
+          Max simultaneous connections.
+        '';
+      };
+
+      bind = mkOption {
+        default = "0.0.0.0";
+        type = with types; uniq string;
+        description = ''
+          Bind over an IPv4 address instead of any.
+        '';
+      };
+
+      logFile = mkOption {
+        default = "/var/log/freepopsd";
+        example = "syslog";
+        type = with types; uniq string;
+        description = ''
+          Filename of the log file or syslog to rely on the logging daemon.
+        '';
+      };
+
+      suid = {
+        user = mkOption {
+          default = "nobody";
+          type = with types; uniq string;
+          description = ''
+            User name under which freepopsd will be after binding the port.
+          '';
+        };
+
+        group = mkOption {
+          default = "nogroup";
+          type = with types; uniq string;
+          description = ''
+            Group under which freepopsd will be after binding the port.
+          '';
+        };
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    jobs.freepopsd = {
+      description = "Freepopsd (webmail over POP3)";
+      startOn = "ip-up";
+      exec = ''${pkgs.freepops}/bin/freepopsd \
+        -p ${toString cfg.port} \
+        -t ${toString cfg.threads} \
+        -b ${cfg.bind} \
+        -vv -l ${cfg.logFile} \
+        -s ${cfg.suid.user}.${cfg.suid.group}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/mail/mail.nix b/nixos/modules/services/mail/mail.nix
new file mode 100644
index 00000000000..bad0b22625d
--- /dev/null
+++ b/nixos/modules/services/mail/mail.nix
@@ -0,0 +1,33 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mail = {
+
+      sendmailSetuidWrapper = mkOption {
+        default = null;
+        description = ''
+          Configuration for the sendmail setuid wrwapper (like an element of
+          security.setuidOwners)";
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (config.services.mail.sendmailSetuidWrapper != null) {
+
+    security.setuidOwners = [ config.services.mail.sendmailSetuidWrapper ];
+
+  };
+
+}
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
new file mode 100644
index 00000000000..2732fd60200
--- /dev/null
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -0,0 +1,83 @@
+{ pkgs, config, ... }:
+
+with pkgs;
+with pkgs.lib;
+
+let
+
+  cfg = config.services.opensmtpd;
+  conf = writeText "smtpd.conf" cfg.serverConfiguration;
+  args = concatStringsSep " " cfg.extraServerArgs;
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.opensmtpd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the OpenSMTPD server.";
+      };
+
+      extraServerArgs = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        example = [ "-v" "-P mta" ];
+        description = ''
+          Extra command line arguments provided when the smtpd process
+          is started.
+        '';
+      };
+
+      serverConfiguration = mkOption {
+        type = types.string;
+        default = "";
+        example = ''
+          listen on lo
+          accept for any deliver to lmtp localhost:24
+        ''; 
+        description = ''
+          The contents of the smtpd.conf configuration file. See the
+          OpenSMTPD documentation for syntax information. If this option
+          is left empty, the OpenSMTPD server will not start.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.opensmtpd.enable {
+    users.extraGroups = {
+      smtpd.gid = config.ids.gids.smtpd;
+      smtpq.gid = config.ids.gids.smtpq;
+    };
+
+    users.extraUsers = {
+      smtpd = {
+        description = "OpenSMTPD process user";
+        uid = config.ids.uids.smtpd;
+        group = "smtpd";
+      };
+      smtpq = {
+        description = "OpenSMTPD queue user";
+        uid = config.ids.uids.smtpq;
+        group = "smtpq";
+      };
+    };
+
+    systemd.services.opensmtpd = {
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      after = [ "network.target" ];
+      preStart = "mkdir -p /var/spool";
+      serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
+    };
+  };
+}
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
new file mode 100644
index 00000000000..2633289b46d
--- /dev/null
+++ b/nixos/modules/services/mail/postfix.nix
@@ -0,0 +1,405 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.postfix;
+  user = cfg.user;
+  group = cfg.group;
+  setgidGroup = cfg.setgidGroup;
+
+  mainCf =
+    ''
+      queue_directory = /var/postfix/queue
+      command_directory = ${pkgs.postfix}/sbin
+      daemon_directory = ${pkgs.postfix}/libexec/postfix
+
+      mail_owner = ${user}
+      default_privs = nobody
+
+    ''
+    + optionalString config.networking.enableIPv6 ''
+      inet_protocols = all
+    ''
+    + (if cfg.networks != null then
+        ''
+          mynetworks = ${concatStringsSep ", " cfg.networks}
+        ''
+      else if cfg.networksStyle != "" then
+        ''
+          mynetworks_style = ${cfg.networksStyle}
+        ''
+      else
+        # Postfix default is subnet, but let's play safe
+        ''
+          mynetworks_style = host
+        '')
+    + optionalString (cfg.hostname != "") ''
+      myhostname = ${cfg.hostname}
+    ''
+    + optionalString (cfg.domain != "") ''
+      mydomain = ${cfg.domain}
+    ''
+    + optionalString (cfg.origin != "") ''
+      myorigin = ${cfg.origin}
+    ''
+    + optionalString (cfg.destination != null) ''
+      mydestination = ${concatStringsSep ", " cfg.destination}
+    ''
+    + optionalString (cfg.relayDomains != null) ''
+      relay_domains = ${concatStringsSep ", " cfg.relayDomains}
+    ''
+    + ''
+      local_recipient_maps =
+
+      relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
+          cfg.relayHost
+        else
+          "[" + cfg.relayHost + "]"}
+
+      alias_maps = hash:/var/postfix/conf/aliases
+
+      mail_spool_directory = /var/spool/mail/
+
+      setgid_group = ${setgidGroup}
+    ''
+    + optionalString (cfg.sslCert != "") ''
+
+      smtp_tls_CAfile = ${cfg.sslCACert}
+      smtp_tls_cert_file = ${cfg.sslCert}
+      smtp_tls_key_file = ${cfg.sslKey}
+
+      smtp_use_tls = yes
+
+      smtpd_tls_CAfile = ${cfg.sslCACert}
+      smtpd_tls_cert_file = ${cfg.sslCert}
+      smtpd_tls_key_file = ${cfg.sslKey}
+
+      smtpd_use_tls = yes
+
+      recipientDelimiter = ${cfg.recipientDelimiter}
+    ''
+    + optionalString (cfg.virtual != "") ''
+      virtual_alias_maps = hash:/etc/postfix/virtual
+    ''
+    + cfg.extraConfig;
+
+  masterCf = ''
+    # ==========================================================================
+    # service type  private unpriv  chroot  wakeup  maxproc command + args
+    #               (yes)   (yes)   (yes)   (never) (100)
+    # ==========================================================================
+    smtp      inet  n       -       n       -       -       smtpd
+    #submission inet n       -       n       -       -       smtpd
+    #  -o smtpd_tls_security_level=encrypt
+    #  -o smtpd_sasl_auth_enable=yes
+    #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+    #  -o milter_macro_daemon_name=ORIGINATING
+    pickup    fifo  n       -       n       60      1       pickup
+    cleanup   unix  n       -       n       -       0       cleanup
+    qmgr      fifo  n       -       n       300     1       qmgr
+    tlsmgr    unix  -       -       n       1000?   1       tlsmgr
+    rewrite   unix  -       -       n       -       -       trivial-rewrite
+    bounce    unix  -       -       n       -       0       bounce
+    defer     unix  -       -       n       -       0       bounce
+    trace     unix  -       -       n       -       0       bounce
+    verify    unix  -       -       n       -       1       verify
+    flush     unix  n       -       n       1000?   0       flush
+    proxymap  unix  -       -       n       -       -       proxymap
+    proxywrite unix -       -       n       -       1       proxymap
+    smtp      unix  -       -       n       -       -       smtp
+    relay     unix  -       -       n       -       -       smtp
+    	      -o smtp_fallback_relay=
+    #       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+    showq     unix  n       -       n       -       -       showq
+    error     unix  -       -       n       -       -       error
+    retry     unix  -       -       n       -       -       error
+    discard   unix  -       -       n       -       -       discard
+    local     unix  -       n       n       -       -       local
+    virtual   unix  -       n       n       -       -       virtual
+    lmtp      unix  -       -       n       -       -       lmtp
+    anvil     unix  -       -       n       -       1       anvil
+    scache    unix  -       -       n       -       1       scache
+    ${cfg.extraMasterConf}
+  '';
+
+  aliases =
+    optionalString (cfg.postmasterAlias != "") ''
+      postmaster: ${cfg.postmasterAlias}
+    ''
+    + optionalString (cfg.rootAlias != "") ''
+      root: ${cfg.rootAlias}
+    ''
+    + cfg.extraAliases
+  ;
+
+  aliasesFile = pkgs.writeText "postfix-aliases" aliases;
+  virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
+  mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
+  masterCfFile = pkgs.writeText "postfix-master.cf" masterCf;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.postfix = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to run the Postfix mail server.";
+      };
+
+      setSendmail = mkOption {
+        default = true;
+        description = "Whether to set the system sendmail to postfix's.";
+      };
+
+      user = mkOption {
+        default = "postfix";
+        description = "What to call the Postfix user (must be used only for postfix).";
+      };
+
+      group = mkOption {
+        default = "postfix";
+        description = "What to call the Postfix group (must be used only for postfix).";
+      };
+
+      setgidGroup = mkOption {
+        default = "postdrop";
+        description = "
+          How to call postfix setgid group (for postdrop). Should
+          be uniquely used group.
+        ";
+      };
+
+      networks = mkOption {
+        default = null;
+        example = ["192.168.0.1/24"];
+        description = "
+          Net masks for trusted - allowed to relay mail to third parties -
+          hosts. Leave empty to use mynetworks_style configuration or use
+          default (localhost-only).
+        ";
+      };
+
+      networksStyle = mkOption {
+        default = "";
+        description = "
+          Name of standard way of trusted network specification to use,
+          leave blank if you specify it explicitly or if you want to use
+          default (localhost-only).
+        ";
+      };
+
+      hostname = mkOption {
+        default = "";
+        description ="
+          Hostname to use. Leave blank to use just the hostname of machine.
+          It should be FQDN.
+        ";
+      };
+
+      domain = mkOption {
+        default = "";
+        description ="
+          Domain to use. Leave blank to use hostname minus first component.
+        ";
+      };
+
+      origin = mkOption {
+        default = "";
+        description ="
+          Origin to use in outgoing e-mail. Leave blank to use hostname.
+        ";
+      };
+
+      destination = mkOption {
+        default = null;
+        example = ["localhost"];
+        description = "
+          Full (!) list of domains we deliver locally. Leave blank for
+          acceptable Postfix default.
+        ";
+      };
+
+      relayDomains = mkOption {
+        default = null;
+        example = ["localdomain"];
+        description = "
+          List of domains we agree to relay to. Default is the same as
+          destination.
+        ";
+      };
+
+      relayHost = mkOption {
+        default = "";
+        description = "
+          Mail relay for outbound mail.
+        ";
+      };
+
+      lookupMX = mkOption {
+        default = false;
+        description = "
+          Whether relay specified is just domain whose MX must be used.
+        ";
+      };
+
+      postmasterAlias = mkOption {
+        default = "root";
+        description = "Who should receive postmaster e-mail.";
+      };
+
+      rootAlias = mkOption {
+        default = "";
+        description = "
+          Who should receive root e-mail. Blank for no redirection.
+        ";
+      };
+
+      extraAliases = mkOption {
+        default = "";
+        description = "
+          Additional entries to put verbatim into aliases file.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          Extra lines to be added verbatim to the main.cf configuration file.
+        ";
+      };
+
+      sslCert = mkOption {
+        default = "";
+        description = "SSL certificate to use.";
+      };
+
+      sslCACert = mkOption {
+        default = "";
+        description = "SSL certificate of CA.";
+      };
+
+      sslKey = mkOption {
+        default = "";
+        description = "SSL key to use.";
+      };
+
+      recipientDelimiter = mkOption {
+        default = "";
+        example = "+";
+        description = "
+          Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test
+        ";
+      };
+
+      virtual = mkOption {
+        default = "";
+        description = "
+          Entries for the virtual alias map.
+        ";
+      };
+
+      extraMasterConf = mkOption {
+        default = "";
+        example = "submission inet n - n - - smtpd";
+        description = "Extra lines to append to the generated master.cf file.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.postfix.enable {
+
+    environment = {
+      etc = singleton
+        { source = "/var/postfix/conf";
+          target = "postfix";
+        };
+
+      # This makes comfortable for root to run 'postqueue' for example.
+      systemPackages = [ pkgs.postfix ];
+    };
+
+    services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.postfix}/bin/sendmail";
+      owner = "nobody";
+      group = "postdrop";
+      setuid = false;
+      setgid = true;
+    };
+
+    users.extraUsers = singleton
+      { name = user;
+        description = "Postfix mail server user";
+        uid = config.ids.uids.postfix;
+        group = group;
+      };
+
+    users.extraGroups =
+      [ { name = group;
+          gid = config.ids.gids.postfix;
+        }
+        { name = setgidGroup;
+          gid = config.ids.gids.postdrop;
+        }
+      ];
+
+    jobs.postfix =
+      # I copy _lots_ of shipped configuration filed
+      # that can be left as is. I am afraid the exact
+      # will list slightly change in next Postfix
+      # release, so listing them all one-by-one in an
+      # accurate way is unlikely to be better.
+      { description = "Postfix mail server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        daemonType = "fork";
+
+        preStart =
+          ''
+            if ! [ -d /var/spool/postfix ]; then
+              ${pkgs.coreutils}/bin/mkdir -p /var/spool/mail /var/postfix/conf /var/postfix/queue
+            fi
+
+            ${pkgs.coreutils}/bin/chown -R ${user}:${group} /var/postfix
+            ${pkgs.coreutils}/bin/chown -R ${user}:${setgidGroup} /var/postfix/queue
+            ${pkgs.coreutils}/bin/chmod -R ug+rwX /var/postfix/queue
+            ${pkgs.coreutils}/bin/chown root:root /var/spool/mail
+            ${pkgs.coreutils}/bin/chmod a+rwxt /var/spool/mail
+
+            ln -sf "${pkgs.postfix}/share/postfix/conf/"* /var/postfix/conf
+
+            ln -sf ${aliasesFile} /var/postfix/conf/aliases
+            ln -sf ${virtualFile} /var/postfix/conf/virtual
+            ln -sf ${mainCfFile} /var/postfix/conf/main.cf
+            ln -sf ${masterCfFile} /var/postfix/conf/master.cf
+
+            ${pkgs.postfix}/sbin/postalias -c /var/postfix/conf /var/postfix/conf/aliases
+            ${pkgs.postfix}/sbin/postmap -c /var/postfix/conf /var/postfix/conf/virtual
+
+            ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf start
+          '';
+
+        preStop = ''
+            ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf stop
+        '';
+
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix
new file mode 100644
index 00000000000..aaf1dfcc210
--- /dev/null
+++ b/nixos/modules/services/mail/spamassassin.nix
@@ -0,0 +1,64 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.spamassassin;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.spamassassin = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to run the SpamAssassin daemon.";
+      };
+
+      debug = mkOption {
+        default = false;
+        description = "Whether to run the SpamAssassin daemon in debug mode.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Allow users to run 'spamc'.
+    environment.systemPackages = [ pkgs.spamassassin ];
+
+    users.extraUsers = singleton {
+    name = "spamd";
+      description = "Spam Assassin Daemon";
+      uid = config.ids.uids.spamd;
+      group = "spamd";
+    };
+
+    users.extraGroups = singleton {
+      name = "spamd";
+      gid = config.ids.gids.spamd;
+    };
+
+    jobs.spamd = {
+      description = "Spam Assassin Server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      exec = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --nouser-config --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/var/run/spamd.pid";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix
new file mode 100644
index 00000000000..50491c556e8
--- /dev/null
+++ b/nixos/modules/services/misc/autofs.nix
@@ -0,0 +1,120 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.autofs;
+
+  autoMaster = pkgs.writeText "auto.master" cfg.autoMaster;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.autofs = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Mount filesystems on demand. Unmount them automatically.
+          You may also be interested in afuese.
+        ";
+      };
+
+      autoMaster = mkOption {
+        example = literalExample ''
+          autoMaster = let
+            mapConf = pkgs.writeText "auto" '''
+             kernel    -ro,soft,intr       ftp.kernel.org:/pub/linux
+             boot      -fstype=ext2        :/dev/hda1
+             windoze   -fstype=smbfs       ://windoze/c
+             removable -fstype=ext2        :/dev/hdd
+             cd        -fstype=iso9660,ro  :/dev/hdc
+             floppy    -fstype=auto        :/dev/fd0
+             server    -rw,hard,intr       / -ro myserver.me.org:/ \
+                                           /usr myserver.me.org:/usr \
+                                           /home myserver.me.org:/home
+            ''';
+          in '''
+            /auto file:''${mapConf}
+          '''
+        '';
+        description = "
+          file contents of /etc/auto.master. See man auto.master
+          See man 5 auto.master and man 5 autofs.
+        ";
+      };
+
+      timeout = mkOption {
+        default = 600;
+        description = "Set the global minimum timeout, in seconds, until directories are unmounted";
+      };
+
+      debug = mkOption {
+        default = false;
+        description = "
+        pass -d and -7 to automount and write log to /var/log/autofs
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc = singleton
+      { target = "auto.master";
+        source = pkgs.writeText "auto.master" cfg.autoMaster;
+      };
+
+    boot.kernelModules = [ "autofs4" ];
+
+    jobs.autofs =
+      { description = "Filesystem automounter";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        path = [ pkgs.nfsUtils pkgs.sshfsFuse ];
+
+        preStop =
+          ''
+            set -e; while :; do pkill -TERM automount; sleep 1; done
+          '';
+
+        # automount doesn't clean up when receiving SIGKILL.
+        # umount -l should unmount the directories recursively when they are no longer used
+        # It does, but traces are left in /etc/mtab. So unmount recursively..
+        postStop =
+          ''
+          PATH=${pkgs.gnused}/bin:${pkgs.coreutils}/bin
+          exec &> /tmp/logss
+          # double quote for sed:
+          escapeSpaces(){ sed 's/ /\\\\040/g'; }
+          unescapeSpaces(){ sed 's/\\040/ /g'; }
+          sed -n 's@^\s*\(\([^\\ ]\|\\ \)*\)\s.*@\1@p' ${autoMaster} | sed 's/[\\]//' | while read mountPoint; do
+            sed -n "s@[^ ]\+\s\+\($(echo "$mountPoint"| escapeSpaces)[^ ]*\).*@\1@p" /proc/mounts | sort -r | unescapeSpaces| while read smountP; do
+              ${pkgs.utillinux}/bin/umount -l "$smountP" || true
+            done
+          done
+          '';
+
+        script =
+          ''
+            ${if cfg.debug then "exec &> /var/log/autofs" else ""}
+            exec ${pkgs.autofs5}/sbin/automount ${if cfg.debug then "-d" else ""} -f -t ${builtins.toString cfg.timeout} "${autoMaster}" ${if cfg.debug then "-l7" else ""}
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/cgminer.nix b/nixos/modules/services/misc/cgminer.nix
new file mode 100644
index 00000000000..890d7a4020b
--- /dev/null
+++ b/nixos/modules/services/misc/cgminer.nix
@@ -0,0 +1,140 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.cgminer;
+
+  convType = with builtins;
+    v: if isBool v then (if v then "true" else "false") else toString v;
+  mergedHwConfig =
+    mapAttrsToList (n: v: ''"${n}": "${(concatStringsSep "," (map convType v))}"'')
+      (foldAttrs (n: a: [n] ++ a) [] cfg.hardware);
+  mergedConfig = with builtins;
+    mapAttrsToList (n: v: ''"${n}":  ${if isBool v then "" else ''"''}${convType v}${if isBool v then "" else ''"''}'')
+      cfg.config;
+
+  cgminerConfig = pkgs.writeText "cgminer.conf" ''
+  {
+  ${concatStringsSep ",\n" mergedHwConfig},
+  ${concatStringsSep ",\n" mergedConfig},
+  "pools": [
+  ${concatStringsSep ",\n"
+    (map (v: ''{"url": "${v.url}", "user": "${v.user}", "pass": "${v.pass}"}'')
+          cfg.pools)}]
+  }
+  '';
+in
+{
+  ###### interface
+  options = {
+
+    services.cgminer = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable cgminer, an ASIC/FPGA/GPU miner for bitcoin and
+          litecoin.
+        '';
+      };
+
+      package = mkOption {
+        default = pkgs.cgminer;
+        description = "Which cgminer derivation to use.";
+      };
+
+      user = mkOption {
+        default = "cgminer";
+        description = "User account under which cgminer runs";
+      };
+
+      pools = mkOption {
+        default = [];  # Run benchmark
+        description = "List of pools where to mine";
+        example = [{
+          url = "http://p2pool.org:9332";
+          username = "17EUZxTvs9uRmPsjPZSYUU3zCz9iwstudk";
+          password="X";
+        }];
+      };
+
+      hardware = mkOption {
+        default = []; # Run without options
+        description= "List of config options for every GPU";
+        example = [
+        {
+          intensity = 9;
+          gpu-engine = "0-985";
+          gpu-fan = "0-85";
+          gpu-memclock = 860;
+          gpu-powertune = 20;
+          temp-cutoff = 95;
+          temp-overheat = 85;
+          temp-target = 75;
+        }
+        {
+          intensity = 9;
+          gpu-engine = "0-950";
+          gpu-fan = "0-85";
+          gpu-memclock = 825;
+          gpu-powertune = 20;
+          temp-cutoff = 95;
+          temp-overheat = 85;
+          temp-target = 75;
+        }];
+      };
+
+      config = mkOption {
+        default = {};
+        description = "Additional config";
+        example = {
+          auto-fan = true;
+          auto-gpu = true;
+          expiry = 120;
+          failover-only = true;
+          gpu-threads = 2;
+          log = 5;
+          queue = 1;
+          scan-time = 60;
+          temp-histeresys = 3;
+        };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.cgminer.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "Cgminer user";
+      };
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.cgminer = {
+      path = [ pkgs.cgminer ];
+
+      after = [ "display-manager.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = { 
+        LD_LIBRARY_PATH = ''/run/opengl-driver/lib:/run/opengl-driver-32/lib'';
+        DISPLAY = ":0";
+        GPU_MAX_ALLOC_PERCENT = "100";
+        GPU_USE_SYNC_OBJECTS = "1";
+      };
+
+      serviceConfig = {
+        ExecStart = "${pkgs.cgminer}/bin/cgminer -T -c ${cgminerConfig}";
+        User = cfg.user;
+        RestartSec = 10;
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
new file mode 100644
index 00000000000..6419e6f8fc7
--- /dev/null
+++ b/nixos/modules/services/misc/disnix.nix
@@ -0,0 +1,164 @@
+# Disnix server
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.disnix;
+
+  dysnomia = pkgs.dysnomia.override (origArgs: {
+    enableApacheWebApplication = config.services.httpd.enable;
+    enableAxis2WebService = config.services.tomcat.axis2.enable;
+    enableEjabberdDump = config.services.ejabberd.enable;
+    enableMySQLDatabase = config.services.mysql.enable;
+    enablePostgreSQLDatabase = config.services.postgresql.enable;
+    enableSubversionRepository = config.services.svnserve.enable;
+    enableTomcatWebApplication = config.services.tomcat.enable;
+  });
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.disnix = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Disnix";
+      };
+
+      useWebServiceInterface = mkOption {
+        default = false;
+        description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
+      };
+
+      publishInfrastructure = {
+        enable = mkOption {
+          default = false;
+          description = "Whether to publish capabilities/properties of this machine in as attributes in the infrastructure option";
+        };
+
+        enableAuthentication = mkOption {
+          default = false;
+          description = "Whether to publish authentication credentials through the infrastructure attribute (not recommended in combination with Avahi)";
+        };
+      };
+
+      infrastructure = mkOption {
+        default = {};
+        description = "List of name value pairs containing properties for the infrastructure model";
+      };
+
+      publishAvahi = mkOption {
+        default = false;
+        description = "Whether to publish capabilities/properties as a Disnix service through Avahi";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
+
+    services.dbus.enable = true;
+    services.dbus.packages = [ pkgs.disnix ];
+
+    services.avahi.enable = cfg.publishAvahi;
+
+    services.tomcat.enable = cfg.useWebServiceInterface;
+    services.tomcat.extraGroups = [ "disnix" ];
+    services.tomcat.javaOpts = "${optionalString cfg.useWebServiceInterface "-Djava.library.path=${pkgs.libmatthew_java}/lib/jni"} ";
+    services.tomcat.sharedLibs = optional cfg.useWebServiceInterface "${pkgs.DisnixWebService}/share/java/DisnixConnection.jar"
+                                 ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
+    services.tomcat.webapps = optional cfg.useWebServiceInterface pkgs.DisnixWebService;
+
+    users.extraGroups = singleton
+      { name = "disnix";
+        gid = config.ids.gids.disnix;
+      };
+
+    services.disnix.infrastructure =
+      optionalAttrs (cfg.publishInfrastructure.enable)
+      ( { hostname = config.networking.hostName;
+          #targetHost = config.deployment.targetHost;
+          system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
+          
+          supportedTypes = (import "${pkgs.stdenv.mkDerivation {
+            name = "supportedtypes";
+            buildCommand = ''
+              ( echo -n "[ "
+                cd ${dysnomia}/libexec/dysnomia
+                for i in *
+                do
+                    echo -n "\"$i\" "
+                done
+                echo -n " ]") > $out
+            '';
+          }}");
+        }
+        #// optionalAttrs (cfg.useWebServiceInterface) { targetEPR = "http://${config.deployment.targetHost}:8080/DisnixWebService/services/DisnixWebService"; }
+        // optionalAttrs (config.services.httpd.enable) { documentRoot = config.services.httpd.documentRoot; }
+        // optionalAttrs (config.services.mysql.enable) { mysqlPort = config.services.mysql.port; }
+        // optionalAttrs (config.services.tomcat.enable) { tomcatPort = 8080; }
+        // optionalAttrs (config.services.svnserve.enable) { svnBaseDir = config.services.svnserve.svnBaseDir; }
+        // optionalAttrs (cfg.publishInfrastructure.enableAuthentication) (
+          optionalAttrs (config.services.mysql.enable) { mysqlUsername = "root"; mysqlPassword = builtins.readFile config.services.mysql.rootPassword; })
+        )
+    ;
+
+    services.disnix.publishInfrastructure.enable = cfg.publishAvahi;
+
+    jobs = {
+      disnix =
+        { description = "Disnix server";
+        
+          wantedBy = [ "multi-user.target" ];
+          after = [ "dbus.service" ]
+            ++ optional config.services.httpd.enable "httpd.service"
+            ++ optional config.services.mysql.enable "mysql.service"
+            ++ optional config.services.tomcat.enable "tomcat.service"
+            ++ optional config.services.svnserve.enable "svnserve.service";
+
+          restartIfChanged = false;
+          
+          path = [ pkgs.nix pkgs.disnix ];
+        
+          script =
+          ''
+            export HOME=/root
+            disnix-service --dysnomia-modules-dir=${dysnomia}/libexec/dysnomia
+          '';
+        };
+    } // optionalAttrs cfg.publishAvahi {
+      disnixAvahi =
+        { description = "Disnix Avahi publisher";
+
+          startOn = "started avahi-daemon";
+
+          exec =
+          ''
+            ${pkgs.avahi}/bin/avahi-publish-service disnix-${config.networking.hostName} _disnix._tcp 22 \
+              "mem=$(grep 'MemTotal:' /proc/meminfo | sed -e 's/kB//' -e 's/MemTotal://' -e 's/ //g')" \
+              ${concatMapStrings (infrastructureAttrName:
+                let infrastructureAttrValue = getAttr infrastructureAttrName (cfg.infrastructure);
+                in
+                if builtins.isInt infrastructureAttrValue then
+                ''${infrastructureAttrName}=${toString infrastructureAttrValue} \
+                ''
+                else
+                ''${infrastructureAttrName}=\"${infrastructureAttrValue}\" \
+                ''
+                ) (attrNames (cfg.infrastructure))}
+          '';
+        };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/felix.nix b/nixos/modules/services/misc/felix.nix
new file mode 100644
index 00000000000..2da50fc8595
--- /dev/null
+++ b/nixos/modules/services/misc/felix.nix
@@ -0,0 +1,110 @@
+# Felix server
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.felix;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.felix = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the Apache Felix OSGi service";
+      };
+
+      bundles = mkOption {
+        default = [ pkgs.felix_remoteshell ];
+        description = "List of bundles that should be activated on startup";
+      };
+
+      user = mkOption {
+        default = "osgi";
+        description = "User account under which Apache Felix runs.";
+      };
+
+      group = mkOption {
+        default = "osgi";
+        description = "Group account under which Apache Felix runs.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    users.extraGroups = singleton
+      { name = "osgi";
+        gid = config.ids.gids.osgi;
+      };
+
+    users.extraUsers = singleton
+      { name = "osgi";
+        uid = config.ids.uids.osgi;
+        description = "OSGi user";
+        home = "/homeless-shelter";
+      };
+
+    jobs.felix =
+      { description = "Felix server";
+
+        preStart =
+	  ''
+	    # Initialise felix instance on first startup
+	    if [ ! -d /var/felix ]
+	    then
+	        # Symlink system files
+
+	        mkdir -p /var/felix
+		chown ${cfg.user}:${cfg.group} /var/felix
+
+		for i in ${pkgs.felix}/*
+		do
+		    if [ "$i" != "${pkgs.felix}/bundle" ]
+		    then
+		        ln -sfn $i /var/felix/$(basename $i)
+		    fi
+		done
+
+		# Symlink bundles
+		mkdir -p /var/felix/bundle
+		chown ${cfg.user}:${cfg.group} /var/felix/bundle
+
+		for i in ${pkgs.felix}/bundle/* ${toString cfg.bundles}
+		do
+		    if [ -f $i ]
+		    then
+		        ln -sfn $i /var/felix/bundle/$(basename $i)
+		    elif [ -d $i ]
+		    then
+		        for j in $i/bundle/*
+			do
+			    ln -sfn $j /var/felix/bundle/$(basename $j)
+			done
+		    fi
+		done
+	    fi
+	  '';
+
+        script =
+          ''
+	    cd /var/felix
+            ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c '${pkgs.jre}/bin/java -jar bin/felix.jar'
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix
new file mode 100644
index 00000000000..9f4c4645279
--- /dev/null
+++ b/nixos/modules/services/misc/folding-at-home.nix
@@ -0,0 +1,74 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  stateDir = "/var/lib/foldingathome";
+  cfg = config.services.foldingAtHome;
+  fahUser = "foldingathome";
+in {
+
+  ###### interface
+
+  options = {
+
+    services.foldingAtHome = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Folding@Home to use idle CPU time.
+        '';
+      };
+
+      nickname = mkOption {
+        default = "Anonymous";
+        description = ''
+          A unique handle for statistics.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to the
+          configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = fahUser;
+        uid = config.ids.uids.foldingAtHome;
+        description = "Folding@Home user";
+        home = stateDir;
+      };
+
+    jobs.foldingAtHome =
+      { name = "foldingathome";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${fahUser} ${stateDir}
+            cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg
+          '';
+        exec = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'";
+      };
+
+      services.foldingAtHome.config = ''
+          [settings]
+          username=${cfg.nickname}
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/gpsd.nix b/nixos/modules/services/misc/gpsd.nix
new file mode 100644
index 00000000000..bc1d1f4575a
--- /dev/null
+++ b/nixos/modules/services/misc/gpsd.nix
@@ -0,0 +1,104 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  uid = config.ids.uids.gpsd;
+  gid = config.ids.gids.gpsd;
+  cfg = config.services.gpsd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gpsd = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable `gpsd', a GPS service daemon.
+        '';
+      };
+
+      device = mkOption {
+        default = "/dev/ttyUSB0";
+        description = ''
+          A device may be a local serial device for GPS input, or a URL of the form:
+               <literal>[{dgpsip|ntrip}://][user:passwd@]host[:port][/stream]</literal>
+          in which case it specifies an input source for DGPS or ntrip data.
+        '';
+      };
+
+      readonly = mkOption {
+        default = true;
+        description = ''
+          Whether to enable the broken-device-safety, otherwise
+          known as read-only mode.  Some popular bluetooth and USB
+          receivers lock up or become totally inaccessible when
+          probed or reconfigured.  This switch prevents gpsd from
+          writing to a receiver.  This means that gpsd cannot
+          configure the receiver for optimal performance, but it
+          also means that gpsd cannot break the receiver.  A better
+          solution would be for Bluetooth to not be so fragile.  A
+          platform independent method to identify
+          serial-over-Bluetooth devices would also be nice.
+        '';
+      };
+
+      port = mkOption {
+        default = 2947;
+        description = ''
+          The port where to listen for TCP connections.
+        '';
+      };
+
+      debugLevel = mkOption {
+        default = 0;
+        description = ''
+          The debugging level.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = "gpsd";
+        inherit uid;
+        description = "gpsd daemon user";
+        home = "/var/empty";
+      };
+
+    users.extraGroups = singleton
+      { name = "gpsd";
+        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}"
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
new file mode 100644
index 00000000000..adf4f145f25
--- /dev/null
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -0,0 +1,370 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.nix;
+
+  inherit (config.environment) nix;
+
+  makeNixBuildUser = nr:
+    { name = "nixbld${toString nr}";
+      description = "Nix build user ${toString nr}";
+
+      /* For consistency with the setgid(2), setuid(2), and setgroups(2)
+         calls in `libstore/build.cc', don't add any supplementary group
+         here except "nixbld".  */
+      uid = builtins.add config.ids.uids.nixbld nr;
+      group = "nixbld";
+      extraGroups = [ "nixbld" ];
+    };
+
+  nixConf =
+    let
+      # Tricky: if we're using a chroot for builds, then we need
+      # /bin/sh in the chroot (our own compromise to purity).
+      # However, since /bin/sh is a symlink to some path in the
+      # Nix store, which furthermore has runtime dependencies on
+      # other paths in the store, we need the closure of /bin/sh
+      # in `build-chroot-dirs' - otherwise any builder that uses
+      # /bin/sh won't work.
+      binshDeps = pkgs.writeReferencesToFile config.system.build.binsh;
+    in
+      pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
+        extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
+        cat > $out <<END
+        # WARNING: this file is generated from the nix.* options in
+        # your NixOS configuration, typically
+        # /etc/nixos/configuration.nix.  Do not edit it!
+        build-users-group = nixbld
+        build-max-jobs = ${toString (cfg.maxJobs)}
+        build-use-chroot = ${if cfg.useChroot then "true" else "false"}
+        build-chroot-dirs = ${toString cfg.chrootDirs} $(echo $extraPaths)
+        binary-caches = ${toString cfg.binaryCaches}
+        trusted-binary-caches = ${toString cfg.trustedBinaryCaches}
+        $extraOptions
+        END
+      '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    environment.nix = mkOption {
+      default = pkgs.nix;
+      merge = mergeOneOption;
+      description = ''
+        This option specifies the Nix package instance to use throughout the system.
+      '';
+    };
+
+    nix = {
+
+      maxJobs = mkOption {
+        default = 1;
+        example = 2;
+        description = "
+          This option defines the maximum number of jobs that Nix will try
+          to build in parallel.  The default is 1.  You should generally
+          set it to the number of CPUs in your system (e.g., 2 on an Athlon
+          64 X2).
+        ";
+      };
+
+      useChroot = mkOption {
+        default = false;
+        example = true;
+        description = "
+          If set, Nix will perform builds in a chroot-environment that it
+          will set up automatically for each build.  This prevents
+          impurities in builds by disallowing access to dependencies
+          outside of the Nix store.
+        ";
+      };
+
+      chrootDirs = mkOption {
+        default = [];
+        example = [ "/dev" "/proc" ];
+        description =
+          ''
+            Directories from the host filesystem to be included
+            in the chroot.
+          '';
+      };
+
+      extraOptions = mkOption {
+        default = "";
+        example = "
+          gc-keep-outputs = true
+          gc-keep-derivations = true
+        ";
+        description = "Additional text appended to <filename>nix.conf</filename>.";
+      };
+
+      distributedBuilds = mkOption {
+        default = false;
+        description = "
+          Whether to distribute builds to the machines listed in
+          <option>nix.buildMachines</option>.
+          If you know that the <option>buildMachines</option> are not
+          always available either use nixos
+          <command>nixos-rebuild --no-build-hook</command>
+          or consider managing <filename>/etc/nix.machines</filename> manually
+          by setting <option>manualNixMachines</option>. Then you can comment
+          unavailable build machines.
+        ";
+      };
+
+      manualNixMachines = mkOption {
+        default = false;
+        description = "
+          Whether to manually manage the list of build machines used in distributed
+          builds in /etc/nix.machines.
+        ";
+      };
+
+      daemonNiceLevel = mkOption {
+        default = 0;
+        description = "
+          Nix daemon process priority. This priority propagates to build processes.
+          0 is the default Unix process priority, 20 is the lowest.
+        ";
+      };
+
+      daemonIONiceLevel = mkOption {
+        default = 0;
+        description = "
+          Nix daemon process I/O priority. This priority propagates to build processes.
+          0 is the default Unix process I/O priority, 7 is the lowest.
+        ";
+      };
+
+      buildMachines = mkOption {
+        example = [
+          { hostName = "voila.labs.cs.uu.nl";
+            sshUser = "nix";
+            sshKey = "/root/.ssh/id_buildfarm";
+            system = "powerpc-darwin";
+            maxJobs = 1;
+          }
+          { hostName = "linux64.example.org";
+            sshUser = "buildfarm";
+            sshKey = "/root/.ssh/id_buildfarm";
+            system = "x86_64-linux";
+            maxJobs = 2;
+            supportedFeatures = "kvm";
+            mandatoryFeatures = "perf";
+          }
+        ];
+        description = "
+          This option lists the machines to be used if distributed
+          builds are enabled (see
+          <option>nix.distributedBuilds</option>).  Nix will perform
+          derivations on those machines via SSH by copying the inputs
+          to the Nix store on the remote machine, starting the build,
+          then copying the output back to the local Nix store.  Each
+          element of the list should be an attribute set containing
+          the machine's host name (<varname>hostname</varname>), the
+          user name to be used for the SSH connection
+          (<varname>sshUser</varname>), the Nix system type
+          (<varname>system</varname>, e.g.,
+          <literal>\"i686-linux\"</literal>), the maximum number of
+          jobs to be run in parallel on that machine
+          (<varname>maxJobs</varname>), the path to the SSH private
+          key to be used to connect (<varname>sshKey</varname>), a
+          list of supported features of the machine
+          (<varname>supportedFeatures</varname>) and a list of
+          mandatory features of the machine
+          (<varname>mandatoryFeatures</varname>). The SSH private key
+          should not have a passphrase, and the corresponding public
+          key should be added to
+          <filename>~<replaceable>sshUser</replaceable>/authorized_keys</filename>
+          on the remote machine.
+        ";
+      };
+
+      proxy = mkOption {
+        default = "";
+        description = "
+          This option specifies the proxy to use for fetchurl. The real effect
+          is just exporting http_proxy, https_proxy and ftp_proxy with that
+          value.
+        ";
+        example = "http://127.0.0.1:3128";
+      };
+
+      # Environment variables for running Nix.
+      envVars = mkOption {
+        internal = true;
+        default = {};
+        type = types.attrs;
+        description = "Environment variables used by Nix.";
+      };
+
+      nrBuildUsers = mkOption {
+        default = 10;
+        description = ''
+          Number of <literal>nixbld</literal> user accounts created to
+          perform secure concurrent builds.  If you receive an error
+          message saying that “all build users are currently in use”,
+          you should increase this value.
+        '';
+      };
+
+      readOnlyStore = mkOption {
+        default = true;
+        description = ''
+          If set, NixOS will enforce the immutability of the Nix store
+          by making <filename>/nix/store</filename> a read-only bind
+          mount.  Nix will automatically make the store writable when
+          needed.
+        '';
+      };
+
+      binaryCaches = mkOption {
+        default = [ http://cache.nixos.org/ ];
+        type = types.listOf types.string;
+        description = ''
+          List of binary cache URLs used to obtain pre-built binaries
+          of Nix packages.
+        '';
+      };
+
+      trustedBinaryCaches = mkOption {
+        default = [ ];
+        example = [ http://hydra.nixos.org/ ];
+        type = types.listOf types.string;
+        description = ''
+          List of binary cache URLs that non-root users can use (in
+          addition to those specified using
+          <option>nix.binaryCaches</option> by passing
+          <literal>--option binary-caches</literal> to Nix commands.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    nix.chrootDirs = [ "/dev" "/dev/pts" "/proc" "/bin" ];
+
+    environment.etc."nix/nix.conf".source = nixConf;
+
+    # List of machines for distributed Nix builds in the format
+    # expected by build-remote.pl.
+    environment.etc."nix.machines" =
+      { enable = cfg.distributedBuilds && !cfg.manualNixMachines;
+        text =
+          concatMapStrings (machine:
+            "${machine.sshUser}@${machine.hostName} "
+            + (if machine ? system then machine.system else concatStringsSep "," machine.systems)
+            + " ${machine.sshKey} ${toString machine.maxJobs} "
+            + (if machine ? speedFactor then toString machine.speedFactor else "1" )
+            + " "
+            + (if machine ? supportedFeatures then concatStringsSep "," machine.supportedFeatures else "" )
+            + " "
+            + (if machine ? mandatoryFeatures then concatStringsSep "," machine.mandatoryFeatures else "" )
+            + "\n"
+          ) cfg.buildMachines;
+      };
+
+    systemd.sockets."nix-daemon" =
+      { description = "Nix Daemon Socket";
+        wantedBy = [ "sockets.target" ];
+        before = [ "multi-user.target" ];
+        socketConfig.ListenStream = "/nix/var/nix/daemon-socket/socket";
+      };
+
+    systemd.services."nix-daemon" =
+      { description = "Nix Daemon";
+
+        path = [ nix pkgs.openssl pkgs.utillinux ]
+          ++ optionals cfg.distributedBuilds [ pkgs.openssh pkgs.gzip ];
+
+        environment = cfg.envVars // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; };
+
+        serviceConfig =
+          { ExecStart = "@${nix}/bin/nix-daemon nix-daemon --daemon";
+            KillMode = "process";
+            Nice = cfg.daemonNiceLevel;
+            IOSchedulingPriority = cfg.daemonIONiceLevel;
+            LimitNOFILE = 4096;
+          };
+
+        restartTriggers = [ nixConf ];
+      };
+
+    nix.envVars =
+      { NIX_CONF_DIR = "/etc/nix";
+
+        # Enable the copy-from-other-stores substituter, which allows builds
+        # to be sped up by copying build results from remote Nix stores.  To
+        # do this, mount the remote file system on a subdirectory of
+        # /var/run/nix/remote-stores.
+        NIX_OTHER_STORES = "/var/run/nix/remote-stores/*/nix";
+      }
+
+      // optionalAttrs cfg.distributedBuilds {
+        NIX_BUILD_HOOK = "${config.environment.nix}/libexec/nix/build-remote.pl";
+        NIX_REMOTE_SYSTEMS = "/etc/nix.machines";
+        NIX_CURRENT_LOAD = "/var/run/nix/current-load";
+      }
+
+      # !!! These should not be defined here, but in some general proxy configuration module!
+      // optionalAttrs (cfg.proxy != "") {
+        http_proxy = cfg.proxy;
+        https_proxy = cfg.proxy;
+        ftp_proxy = cfg.proxy;
+      };
+
+    # Set up the environment variables for running Nix.
+    environment.variables = cfg.envVars;
+
+    environment.extraInit =
+      ''
+        # Set up secure multi-user builds: non-root users build through the
+        # Nix daemon.
+        if test "$USER" != root; then
+            export NIX_REMOTE=daemon
+        else
+            export NIX_REMOTE=
+        fi
+      '';
+
+    users.extraUsers = map makeNixBuildUser (range 1 cfg.nrBuildUsers);
+
+    system.activationScripts.nix = stringAfter [ "etc" "users" ]
+      ''
+        # Nix initialisation.
+        mkdir -m 0755 -p \
+          /nix/var/nix/gcroots \
+          /nix/var/nix/temproots \
+          /nix/var/nix/manifests \
+          /nix/var/nix/userpool \
+          /nix/var/nix/profiles \
+          /nix/var/nix/db \
+          /nix/var/log/nix/drvs \
+          /nix/var/nix/channel-cache \
+          /nix/var/nix/chroots
+        mkdir -m 1777 -p \
+          /nix/var/nix/gcroots/per-user \
+          /nix/var/nix/profiles/per-user \
+          /nix/var/nix/gcroots/tmp
+
+        ln -sf /nix/var/nix/profiles /nix/var/nix/gcroots/
+        ln -sf /nix/var/nix/manifests /nix/var/nix/gcroots/
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
new file mode 100644
index 00000000000..dfdc4db65d5
--- /dev/null
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -0,0 +1,61 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.nix.gc;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    nix.gc = {
+
+      automatic = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Automatically run the garbage collector at a specific time.";
+      };
+
+      dates = mkOption {
+        default = "03:15";
+        type = types.uniq types.string;
+        description = ''
+          Specification (in the format described by
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>) of the time at
+          which the garbage collector will run.
+        '';
+      };
+
+      options = mkOption {
+        default = "";
+        example = "--max-freed $((64 * 1024**3))";
+        type = types.uniq types.string;
+        description = ''
+          Options given to <filename>nix-collect-garbage</filename> when the
+          garbage collector is run automatically.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    systemd.services.nix-gc =
+      { description = "Nix Garbage Collector";
+        serviceConfig.ExecStart = "${config.environment.nix}/bin/nix-collect-garbage ${cfg.options}";
+        startAt = optionalString cfg.automatic cfg.dates;
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
new file mode 100644
index 00000000000..38f1917a46a
--- /dev/null
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -0,0 +1,116 @@
+# This module includes the NixOS man-pages in the system environment,
+# and optionally starts a browser that shows the NixOS manual on one
+# of the virtual consoles.  The latter is useful for the installation
+# CD.
+
+{ config, pkgs, options, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.nixosManual;
+
+  manual = import ../../../doc/manual {
+    inherit (cfg) revision;
+    inherit pkgs options;
+  };
+
+  entry = "${manual.manual}/share/doc/nixos/manual.html";
+
+  help = pkgs.writeScriptBin "nixos-help"
+    ''
+      #! ${pkgs.stdenv.shell} -e
+      browser="$BROWSER"
+      if [ -z "$browser" ]; then
+        browser="$(type -P xdg-open || true)"
+        if [ -z "$browser" ]; then
+          browser="$(type -P w3m || true)"
+          if [ -z "$browser" ]; then
+            echo "$0: unable to start a web browser; please set \$BROWSER"
+            exit 1
+          fi
+        fi
+      fi
+      exec "$browser" ${entry}
+    '';
+
+in
+
+{
+
+  options = {
+
+    services.nixosManual.enable = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to build the NixOS manual pages.
+      '';
+    };
+
+    services.nixosManual.showManual = mkOption {
+      default = false;
+      description = ''
+        Whether to show the NixOS manual on one of the virtual
+        consoles.
+      '';
+    };
+
+    services.nixosManual.ttyNumber = mkOption {
+      default = "8";
+      description = ''
+        Virtual console on which to show the manual.
+      '';
+    };
+
+    services.nixosManual.browser = mkOption {
+      default = "${pkgs.w3m}/bin/w3m";
+      description = ''
+        Browser used to show the manual.
+      '';
+    };
+
+    services.nixosManual.revision = mkOption {
+      default = "local";
+      type = types.uniq types.string;
+      description = ''
+        Revision of the targeted source file.  This value can either be
+        <literal>"local"</literal>, <literal>"HEAD"</literal> or any
+        revision number embedded in a string.
+      '';
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    system.build.manual = manual;
+
+    environment.systemPackages = [ manual.manpages help ];
+
+    boot.extraTTYs = mkIf cfg.showManual ["tty${cfg.ttyNumber}"];
+
+    systemd.services = optionalAttrs cfg.showManual
+      { "nixos-manual" =
+        { description = "NixOS Manual";
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig =
+            { ExecStart = "${cfg.browser} ${entry}";
+              StandardInput = "tty";
+              StandardOutput = "tty";
+              TTYPath = "/dev/tty${cfg.ttyNumber}";
+              TTYReset = true;
+              TTYVTDisallocate = true;
+              Restart = "always";
+            };
+        };
+      };
+
+    services.mingetty.helpLine = mkIf cfg.showManual
+      "\nPress <Alt-F${toString cfg.ttyNumber}> for the NixOS manual.";
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/rogue.nix b/nixos/modules/services/misc/rogue.nix
new file mode 100644
index 00000000000..94fa8850750
--- /dev/null
+++ b/nixos/modules/services/misc/rogue.nix
@@ -0,0 +1,59 @@
+# Execute the game `rogue' on tty 9.  Mostly used by the NixOS
+# installation CD.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.rogue;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.rogue.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the Rogue game on one of the virtual
+        consoles.
+      '';
+    };
+
+    services.rogue.tty = mkOption {
+      default = "tty9";
+      description = ''
+        Virtual console on which to run Rogue.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    boot.extraTTYs = [ cfg.tty ];
+
+    systemd.services.rogue =
+      { description = "Rogue dungeon crawling game";
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig =
+          { ExecStart = "${pkgs.rogue}/bin/rogue";
+            StandardInput = "tty";
+            StandardOutput = "tty";
+            TTYPath = "/dev/${cfg.tty}";
+            TTYReset = true;
+            TTYVTDisallocate = true;
+            Restart = "always";
+          };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/misc/svnserve.nix b/nixos/modules/services/misc/svnserve.nix
new file mode 100644
index 00000000000..b0806d14738
--- /dev/null
+++ b/nixos/modules/services/misc/svnserve.nix
@@ -0,0 +1,46 @@
+# SVN server
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.svnserve;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.svnserve = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable svnserve to serve Subversion repositories through the SVN protocol.";
+      };
+
+      svnBaseDir = mkOption {
+        default = "/repos";
+	description = "Base directory from which Subversion repositories are accessed.";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    jobs.svnserve = {
+      startOn = "started network-interfaces";
+      stopOn = "stopping network-interfaces";
+
+      preStart = "mkdir -p ${cfg.svnBaseDir}";
+
+      exec = "${pkgs.subversion}/bin/svnserve -r ${cfg.svnBaseDir} -d --foreground --pid-file=/var/run/svnserve.pid";
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/synergy.nix b/nixos/modules/services/misc/synergy.nix
new file mode 100644
index 00000000000..91c0acb0bc2
--- /dev/null
+++ b/nixos/modules/services/misc/synergy.nix
@@ -0,0 +1,131 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfgC = config.services.synergy.client;
+  cfgS = config.services.synergy.server;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.synergy = {
+
+      # !!! All these option descriptions needs to be cleaned up.
+
+      client = {
+        enable = mkOption {
+          default = false;
+          description = "
+            Whether to enable the synergy client (receive keyboard and mouse events from a synergy server)
+          ";
+        };
+        screenName = mkOption {
+          default = "";
+          description = "
+            use screen-name instead the hostname to identify
+            ourselves to the server.
+            ";
+        };
+        serverAddress = mkOption {
+          description = "
+            The server address is of the form: [hostname][:port].  The
+            hostname must be the address or hostname of the server.  The
+            port overrides the default port, 24800.
+          ";
+        };
+        autoStart = mkOption {
+          default = true;
+          type = types.bool;
+          description = "Whether synergy-client should be started automatically.";
+        };
+      };
+
+      server = {
+        enable = mkOption {
+          default = false;
+          description = "
+            Whether to enable the synergy server (send keyboard and mouse events)
+          ";
+        };
+        configFile = mkOption {
+          default = "/etc/synergy-server.conf";
+          description = "
+            The synergy server configuration file. open upstart-jobs/synergy.nix to see an example
+          ";
+        };
+        screenName = mkOption {
+          default = "";
+          description = "
+            use screen-name instead the hostname to identify
+            this screen in the configuration.
+            ";
+        };
+        address = mkOption {
+          default = "";
+          description = "listen for clients on the given address";
+        };
+        autoStart = mkOption {
+          default = true;
+          type = types.bool;
+          description = "Whether synergy-server should be started automatically.";
+        };
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    systemd.services."synergy-client" = mkIf cfgC.enable {
+      after = [ "network.target" ];
+      description = "Synergy client";
+      wantedBy = optional cfgC.autoStart "multi-user.target";
+      path = [ pkgs.synergy ];
+      serviceConfig.ExecStart = ''${pkgs.synergy}/bin/synergyc -f ${optionalString (cfgC.screenName != "") "-n ${cfgC.screenName}"} ${cfgC.serverAddress}'';
+    };
+
+    systemd.services."synergy-server" = mkIf cfgS.enable {
+      after = [ "network.target" ];
+      description = "Synergy server";
+      wantedBy = optional cfgS.autoStart "multi-user.target";
+      path = [ pkgs.synergy ];
+      serviceConfig.ExecStart = ''${pkgs.synergy}/bin/synergys -c ${cfgS.configFile} -f ${optionalString (cfgS.address != "") "-a ${cfgS.address}"} ${optionalString (cfgS.screenName != "") "-n ${cfgS.screenName}" }'';
+    };
+
+  };
+
+}
+
+/* SYNERGY SERVER example configuration file
+section: screens
+  laptop:
+  dm:
+  win:
+end
+section: aliases
+    laptop:
+      192.168.5.5
+    dm:
+      192.168.5.78
+    win:
+      192.168.5.54
+end
+section: links
+   laptop:
+       left = dm
+   dm:
+       right = laptop
+       left = win
+  win:
+      right = dm
+end
+*/
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
new file mode 100644
index 00000000000..114bad5c947
--- /dev/null
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -0,0 +1,190 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.apcupsd;
+
+  configFile = pkgs.writeText "apcupsd.conf" ''
+    ## apcupsd.conf v1.1 ##
+    # apcupsd complains if the first line is not like above.
+    ${cfg.configText}
+    SCRIPTDIR ${toString scriptDir}
+  '';
+
+  # List of events from "man apccontrol"
+  eventList = [
+    "annoyme"
+    "battattach"
+    "battdetach"
+    "changeme"
+    "commfailure"
+    "commok"
+    "doreboot"
+    "doshutdown"
+    "emergency"
+    "failing"
+    "killpower"
+    "loadlimit"
+    "mainsback"
+    "onbattery"
+    "offbattery"
+    "powerout"
+    "remotedown"
+    "runlimit"
+    "timeout"
+    "startselftest"
+    "endselftest"
+  ];
+
+  shellCmdsForEventScript = eventname: commands: ''
+    echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}"
+    echo "${commands}" >> "$out/${eventname}"
+    chmod a+x "$out/${eventname}"
+  '';
+
+  eventToShellCmds = event: if builtins.hasAttr event cfg.hooks then (shellCmdsForEventScript event (builtins.getAttr event cfg.hooks)) else "";
+
+  scriptDir = pkgs.runCommand "apcupsd-scriptdir" {} (''
+    mkdir "$out"
+    # Copy SCRIPTDIR from apcupsd package
+    cp -r ${pkgs.apcupsd}/etc/apcupsd/* "$out"/
+    # Make the files writeable (nix will unset the write bits afterwards)
+    chmod u+w "$out"/*
+    # Remove the sample event notification scripts, because they don't work
+    # anyways (they try to send mail to "root" with the "mail" command)
+    (cd "$out" && rm changeme commok commfailure onbattery offbattery)
+    # Remove the sample apcupsd.conf file (we're generating our own)
+    rm "$out/apcupsd.conf"
+    # Set the SCRIPTDIR= line in apccontrol to the dir we're creating now
+    sed -i -e "s|^SCRIPTDIR=.*|SCRIPTDIR=$out|" "$out/apccontrol"
+    '' + concatStringsSep "\n" (map eventToShellCmds eventList)
+
+  );
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.apcupsd = {
+
+      enable = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          Whether to enable the APC UPS daemon. apcupsd monitors your UPS and
+          permits orderly shutdown of your computer in the event of a power
+          failure. User manual: http://www.apcupsd.com/manual/manual.html.
+          Note that apcupsd runs as root (to allow shutdown of computer).
+          You can check the status of your UPS with the "apcaccess" command.
+        '';
+      };
+
+      configText = mkOption {
+        default = ''
+          UPSTYPE usb
+          NISIP 127.0.0.1
+          BATTERYLEVEL 50
+          MINUTES 5
+        '';
+        type = types.string;
+        description = ''
+          Contents of the runtime configuration file, apcupsd.conf. The default
+          settings makes apcupsd autodetect USB UPSes, limit network access to
+          localhost and shutdown the system when the battery level is below 50
+          percent, or when the UPS has calculated that it has 5 minutes or less
+          of remaining power-on time. See man apcupsd.conf for details.
+        '';
+      };
+
+      hooks = mkOption {
+        default = {};
+        example = {
+          doshutdown = ''# shell commands to notify that the computer is shutting down'';
+        };
+        type = types.attrsOf types.string;
+        description = ''
+          Each attribute in this option names an apcupsd event and the string
+          value it contains will be executed in a shell, in response to that
+          event (prior to the default action). See "man apccontrol" for the
+          list of events and what they represent.
+
+          A hook script can stop apccontrol from doing its default action by
+          exiting with value 99. Do not do this unless you know what you're
+          doing.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [ {
+      assertion = let hooknames = builtins.attrNames cfg.hooks; in all (x: elem x eventList) hooknames;
+      message = ''
+        One (or more) attribute names in services.apcupsd.hooks are invalid.
+        Current attribute names: ${toString (builtins.attrNames cfg.hooks)}
+        Valid attribute names  : ${toString eventList}
+      '';
+    } ];
+
+    # Give users access to the "apcaccess" tool
+    environment.systemPackages = [ pkgs.apcupsd ];
+
+    # NOTE 1: apcupsd runs as root because it needs permission to run
+    # "shutdown"
+    #
+    # NOTE 2: When apcupsd calls "wall", it prints an error because stdout is
+    # not connected to a tty (it is connected to the journal):
+    #   wall: cannot get tty name: Inappropriate ioctl for device
+    # The message still gets through.
+    systemd.services.apcupsd = {
+      description = "APC UPS daemon";
+      wantedBy = [ "multi-user.target" ];
+      preStart = "mkdir -p /run/apcupsd/";
+      serviceConfig = {
+        ExecStart = "${pkgs.apcupsd}/bin/apcupsd -b -f ${configFile} -d1";
+        # TODO: When apcupsd has initiated a shutdown, systemd always ends up
+        # waiting for it to stop ("A stop job is running for UPS daemon"). This
+        # is weird, because in the journal one can clearly see that apcupsd has
+        # received the SIGTERM signal and has already quit (or so it seems).
+        # This reduces the wait time from 90 seconds (default) to just 5. Then
+        # systemd kills it with SIGKILL.
+        TimeoutStopSec = 5;
+      };
+    };
+
+    # A special service to tell the UPS to power down/hibernate just before the
+    # computer shuts down. (The UPS has a built in delay before it actually
+    # 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 = {
+      after = [ "shutdown.target" ]; # append umount.target?
+      before = [ "final.target" ];
+      wantedBy = [ "shutdown.target" ];
+      unitConfig = {
+        Description = "APC UPS killpower";
+        ConditionPathExists = "/run/apcupsd/powerfail";
+        DefaultDependencies = "no";
+      };
+      serviceConfig = {
+        Type = "oneshot";
+        ExecStart = "${pkgs.apcupsd}/bin/apcupsd --killpower -f ${configFile}";
+        TimeoutSec = 0;
+        StandardOutput = "tty";
+        RemainAfterExit = "yes";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/dd-agent.nix b/nixos/modules/services/monitoring/dd-agent.nix
new file mode 100644
index 00000000000..ef658523c1f
--- /dev/null
+++ b/nixos/modules/services/monitoring/dd-agent.nix
@@ -0,0 +1,83 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.dd-agent;
+
+  datadog_conf = pkgs.runCommand "datadog.conf" {} ''
+    sed -e 's|^api_key:|api_key: ${cfg.api_key}|' ${optionalString (cfg.hostname != null)
+      "-e 's|^#hostname: mymachine.mydomain|hostname: ${cfg.hostname}|'"
+    } ${pkgs.dd-agent}/etc/dd-agent/datadog.conf.example > $out
+  '';
+in {
+  options.services.dd-agent = {
+    enable = mkOption {
+      description = "Whether to enable the dd-agent montioring service";
+
+      default = false;
+
+      type = types.bool;
+    };
+
+    # !!! This gets stored in the store (world-readable), wish we had https://github.com/NixOS/nix/issues/8
+    api_key = mkOption {
+      description = "The Datadog API key to associate the agent with your account";
+
+      example = "ae0aa6a8f08efa988ba0a17578f009ab";
+
+      type = types.uniq types.string;
+    };
+
+    hostname = mkOption {
+      description = "The hostname to show in the Datadog dashboard (optional)";
+
+      default = null;
+
+      example = "mymachine.mydomain";
+
+      type = types.uniq (types.nullOr types.string);
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc = [ { source = datadog_conf; target = "dd-agent/datadog.conf"; } ];
+    environment.systemPackages = [ pkgs."dd-agent" pkgs.sysstat pkgs.procps ];
+
+    users.extraUsers."dd-agent" = {
+      description = "Datadog Agent User";
+      uid = config.ids.uids.dd-agent;
+      group = "dd-agent";
+      home = "/var/log/datadog/";
+      createHome = true;
+    };
+
+    users.extraGroups.dd-agent.gid = config.ids.gids.dd-agent;
+
+    systemd.services.dd-agent = {
+      description = "Datadog agent monitor";
+      path = [ pkgs."dd-agent" pkgs.python pkgs.sysstat pkgs.procps];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.dd-agent}/bin/dd-agent foreground";
+        User = "dd-agent";
+        Group = "dd-agent";
+      };
+      restartTriggers = [ pkgs.dd-agent datadog_conf ];
+    };
+
+    systemd.services.dogstatsd = {
+      description = "Datadog statsd";
+      path = [ pkgs."dd-agent" pkgs.python ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.dd-agent}/bin/dogstatsd start";
+        User = "dd-agent";
+        Group = "dd-agent";
+        Type = "forking";
+        PIDFile = "/tmp/dogstatsd.pid";
+      };
+      restartTriggers = [ pkgs.dd-agent datadog_conf ];
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
new file mode 100644
index 00000000000..ec36db7b21c
--- /dev/null
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -0,0 +1,265 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.graphite;
+  writeTextOrNull = f: t: if t == null then null else pkgs.writeText f t;
+  dataDir = "/var/db/graphite";
+in {
+
+  ###### interface
+
+  options.services.graphite = {
+    web = {
+      enable = mkOption {
+        description = "Whether to enable graphite web frontend";
+        default = false;
+        type = types.uniq types.bool;
+      };
+
+      host = mkOption {
+        description = "Graphite web frontend listen address";
+        default = "127.0.0.1";
+        types = type.uniq types.string;
+      };
+
+      port = mkOption {
+        description = "Graphite web frontend port";
+        default = "8080";
+        types = type.uniq types.string;
+      };
+    };
+
+    carbon = {
+      config = mkOption {
+        description = "Content of carbon configuration file";
+        default = "";
+        type = types.uniq types.string;
+      };
+
+      enableCache = mkOption {
+        description = "Whether to enable carbon cache, the graphite storage daemon";
+        default = false;
+        type = types.uniq types.bool;
+      };
+
+      storageAggregation = mkOption {
+        description = "Defines how to aggregate data to lower-precision retentions";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = ''
+          [all_min]
+          pattern = \.min$
+          xFilesFactor = 0.1
+         aggregationMethod = min
+        '';
+      };
+
+      storageSchemas = mkOption {
+        description = "Defines retention rates for storing metrics";
+        default = "";
+        type = types.uniq (types.nullOr types.string);
+        example = ''
+          [apache_busyWorkers]
+          pattern = ^servers\.www.*\.workers\.busyWorkers$
+          retentions = 15s:7d,1m:21d,15m:5y
+        '';
+      };
+
+      blacklist = mkOption {
+        description = "Any metrics received which match one of the experssions will be dropped";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = "^some\.noisy\.metric\.prefix\..*";
+      };
+
+      whitelist = mkOption {
+        description = "Only metrics received which match one of the experssions will be persisted";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = ".*";
+      };
+
+      rewriteRules = mkOption {
+        description = "Regular expression patterns that can be used to rewrite metric names in a search and replace fashion";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = ''
+          [post]
+          _sum$ =
+          _avg$ =
+        '';
+      };
+
+      enableRelay = mkOption {
+        description = "Whether to enable carbon relay, the carbon replication and sharding service";
+        default = false;
+        type = types.uniq types.bool;
+      };
+
+      relayRules = mkOption {
+        description = "Relay rules are used to send certain metrics to a certain backend.";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = ''
+          [example]
+          pattern = ^mydata\.foo\..+
+          servers = 10.1.2.3, 10.1.2.4:2004, myserver.mydomain.com
+        '';
+      };
+
+      enableAggregator = mkOption {
+        description = "Whether to enable carbon agregator, the carbon buffering service";
+        default = false;
+        type = types.uniq types.bool;
+      };
+
+      aggregationRules = mkOption {
+        description = "Defines if and how received metrics will be agregated";
+        default = null;
+        type = types.uniq (types.nullOr types.string);
+        example = ''
+          <env>.applications.<app>.all.requests (60) = sum <env>.applications.<app>.*.requests
+          <env>.applications.<app>.all.latency (60) = avg <env>.applications.<app>.*.latency
+        '';
+      };
+    };
+  };
+
+  ###### 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 {
+      description = "Graphite data storage backend";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = {
+        GRAPHITE_CONF_DIR = "/etc/graphite/";
+        GRAPHITE_STORAGE_DIR = "/var/db/graphite/";
+      };
+      serviceConfig = {
+        ExecStart = "${pkgs.pythonPackages.carbon}/bin/carbon-cache.py --pidfile /tmp/carbonCache.pid start";
+        User = "graphite";
+        Group = "graphite";
+      };
+      restartTriggers = [
+        pkgs.pythonPackages.carbon
+        cfg.carbon.config
+        cfg.carbon.storageAggregation
+        cfg.carbon.storageSchemas
+        cfg.carbon.rewriteRules
+      ];
+      preStart = ''
+        mkdir -p ${dataDir}/whisper
+      '';
+    };
+
+    systemd.services.carbonAggregator = mkIf cfg.carbon.enableAggregator {
+      description = "Carbon data aggregator";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = {
+        GRAPHITE_CONF_DIR = "/etc/graphite/";
+        GRAPHITE_STORAGE_DIR = "${dataDir}";
+      };
+      serviceConfig = {
+        ExecStart = "${pkgs.pythonPackages.carbon}/bin/carbon-aggregator.py --pidfile /tmp/carbonAggregator.pid start";
+        User = "graphite";
+        Group = "graphite";
+      };
+      restartTriggers = [
+        pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.aggregationRules
+      ];
+    };
+
+    systemd.services.carbonRelay = mkIf cfg.carbon.enableRelay {
+      description = "Carbon data relay";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = {
+        GRAPHITE_CONF_DIR = "/etc/graphite/";
+        GRAPHITE_STORAGE_DIR = "${dataDir}";
+      };
+      serviceConfig = {
+        ExecStart = "${pkgs.pythonPackages.carbon}/bin/carbon-relay.py --pidfile /tmp/carbonRelay.pid start";
+        User = "graphite";
+        Group = "graphite";
+      };
+      restartTriggers = [
+        pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.relayRules
+      ];
+    };
+
+    systemd.services.graphiteWeb = mkIf cfg.web.enable {
+      description = "Graphite web interface";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = {
+        PYTHONPATH = "${pkgs.python27Packages.graphite_web}/lib/python2.7/site-packages";
+        DJANGO_SETTINGS_MODULE = "graphite.settings";
+        GRAPHITE_CONF_DIR = "/etc/graphite/";
+        GRAPHITE_STORAGE_DIR = "${dataDir}";
+      };
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.python27Packages.waitress}/bin/waitress-serve \
+          --host=${cfg.web.host} --port=${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/}
+
+          # populate database
+          ${pkgs.python27Packages.graphite_web}/bin/manage-graphite.py syncdb --noinput
+
+          # create index
+          ${pkgs.python27Packages.graphite_web}/bin/build-index.sh
+
+          touch ${dataDir}/db-created
+        fi
+      '';
+      restartTriggers = [
+        pkgs.python27Packages.graphite_web
+        pkgs.python27Packages.waitress
+      ];
+    };
+
+    environment.systemPackages = [
+      pkgs.pythonPackages.carbon
+      pkgs.python27Packages.graphite_web
+      pkgs.python27Packages.waitress
+    ];
+
+    users.extraUsers = singleton {
+      name = "graphite";
+      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/monit.nix b/nixos/modules/services/monitoring/monit.nix
new file mode 100644
index 00000000000..2acc51c64a6
--- /dev/null
+++ b/nixos/modules/services/monitoring/monit.nix
@@ -0,0 +1,52 @@
+# Monit system watcher
+# http://mmonit.org/monit/
+
+{config, pkgs, ...}:
+
+let inherit (pkgs.lib) mkOption mkIf;
+in
+
+{
+  options = {
+    services.monit = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run Monit system watcher.
+        '';
+      };
+      config = mkOption {
+        default = "";
+        description = "monit.conf content";
+      };
+      startOn = mkOption {
+        default = "started network-interfaces";
+        description = "What Monit supposes to be already present";
+      };
+    };
+  };
+
+  config = mkIf config.services.monit.enable {
+
+    environment.etc = [
+      {
+        source = pkgs.writeTextFile {
+          name = "monit.conf";
+          text = config.services.monit.config;
+        };
+        target = "monit.conf";
+        mode = "0400";
+      }
+    ];
+
+    jobs.monit = {
+      description = "Monit system watcher";
+
+      startOn = config.services.monit.startOn;
+
+      exec = "${pkgs.monit}/bin/monit -I -c /etc/monit.conf";
+
+      respawn = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/nagios/commands.cfg b/nixos/modules/services/monitoring/nagios/commands.cfg
new file mode 100644
index 00000000000..6efdefcd37d
--- /dev/null
+++ b/nixos/modules/services/monitoring/nagios/commands.cfg
@@ -0,0 +1,34 @@
+define command {
+    command_name host-notify-by-email
+    command_line printf "%b" "To: $CONTACTEMAIL$\nSubject: [Nagios] Host $HOSTSTATE$ alert for $HOSTNAME$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | sendmail $CONTACTEMAIL$
+}
+
+
+define command {
+    command_name notify-by-email
+    command_line printf "%b" "To: $CONTACTEMAIL$\nSubject: [Nagios] $NOTIFICATIONTYPE$ alert - $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$\n\n***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$" | sendmail $CONTACTEMAIL$
+}
+
+
+define command {
+    command_name dummy-ok
+    command_line true
+}
+
+
+define command {
+    command_name check-host-alive
+    command_line check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 1
+}
+
+
+define command {
+    command_name check_local_disk
+    command_line check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$
+}
+
+
+define command {
+    command_name check_ssh
+    command_line check_ssh $HOSTADDRESS$
+}
diff --git a/nixos/modules/services/monitoring/nagios/default.nix b/nixos/modules/services/monitoring/nagios/default.nix
new file mode 100644
index 00000000000..c809a3b8457
--- /dev/null
+++ b/nixos/modules/services/monitoring/nagios/default.nix
@@ -0,0 +1,186 @@
+# Nagios system/network monitoring daemon.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.nagios;
+
+  nagiosUser = "nagios";
+  nagiosGroup = "nogroup";
+
+  nagiosState = "/var/lib/nagios";
+  nagiosLogDir = "/var/log/nagios";
+
+  nagiosObjectDefs =
+    [ ./timeperiods.cfg
+      ./host-templates.cfg
+      ./service-templates.cfg
+      ./commands.cfg
+    ] ++ cfg.objectDefs;
+
+  nagiosObjectDefsDir = pkgs.runCommand "nagios-objects" {inherit nagiosObjectDefs;}
+    "ensureDir $out; ln -s $nagiosObjectDefs $out/";
+
+  nagiosCfgFile = pkgs.writeText "nagios.cfg"
+    ''
+      # Paths for state and logs.
+      log_file=${nagiosLogDir}/current
+      log_archive_path=${nagiosLogDir}/archive
+      status_file=${nagiosState}/status.dat
+      object_cache_file=${nagiosState}/objects.cache
+      comment_file=${nagiosState}/comment.dat
+      downtime_file=${nagiosState}/downtime.dat
+      temp_file=${nagiosState}/nagios.tmp
+      lock_file=/var/run/nagios.lock # Not used I think.
+      state_retention_file=${nagiosState}/retention.dat
+
+      # Configuration files.
+      #resource_file=resource.cfg
+      cfg_dir=${nagiosObjectDefsDir}
+
+      # Uid/gid that the daemon runs under.
+      nagios_user=${nagiosUser}
+      nagios_group=${nagiosGroup}
+
+      # Misc. options.
+      illegal_macro_output_chars=`~$&|'"<>
+      retain_state_information=1
+    ''; # "
+
+  # Plain configuration for the Nagios web-interface with no
+  # authentication.
+  nagiosCGICfgFile = pkgs.writeText "nagios.cgi.conf"
+    ''
+      main_config_file=${nagiosCfgFile}
+      use_authentication=0
+      url_html_path=/nagios
+    '';
+
+  urlPath = cfg.urlPath;
+
+  extraHttpdConfig =
+    ''
+      ScriptAlias ${urlPath}/cgi-bin ${pkgs.nagios}/sbin
+
+      <Directory "${pkgs.nagios}/sbin">
+        Options ExecCGI
+        AllowOverride None
+        Order allow,deny
+        Allow from all
+        SetEnv NAGIOS_CGI_CONFIG ${nagiosCGICfgFile}
+      </Directory>
+
+      Alias ${urlPath} ${pkgs.nagios}/share
+
+      <Directory "${pkgs.nagios}/share">
+        Options None
+        AllowOverride None
+        Order allow,deny
+        Allow from all
+      </Directory>
+    '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.nagios = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to use <link
+          xlink:href='http://www.nagios.org/'>Nagios</link> to monitor
+          your system or network.
+        ";
+      };
+
+      objectDefs = mkOption {
+        description = "
+          A list of Nagios object configuration files that must define
+          the hosts, host groups, services and contacts for the
+          network that you want Nagios to monitor.
+        ";
+      };
+
+      plugins = mkOption {
+        default = [pkgs.nagiosPluginsOfficial pkgs.ssmtp];
+        description = "
+          Packages to be added to the Nagios <envar>PATH</envar>.
+          Typically used to add plugins, but can be anything.
+        ";
+      };
+
+      enableWebInterface = mkOption {
+        default = false;
+        description = "
+          Whether to enable the Nagios web interface.  You should also
+          enable Apache (<option>services.httpd.enable</option>).
+        ";
+      };
+
+      urlPath = mkOption {
+        default = "/nagios";
+        description = "
+          The URL path under which the Nagios web interface appears.
+          That is, you can access the Nagios web interface through
+          <literal>http://<replaceable>server</replaceable>/<replaceable>urlPath</replaceable></literal>.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = nagiosUser;
+        uid = config.ids.uids.nagios;
+        description = "Nagios monitoring daemon";
+        home = nagiosState;
+      };
+
+    # This isn't needed, it's just so that the user can type "nagiostats
+    # -c /etc/nagios.cfg".
+    environment.etc = singleton
+      { source = nagiosCfgFile;
+        target = "nagios.cfg";
+      };
+
+    environment.systemPackages = [ pkgs.nagios ];
+
+    jobs.nagios =
+      { description = "Nagios monitoring daemon";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${nagiosState} ${nagiosLogDir}
+            chown ${nagiosUser} ${nagiosState} ${nagiosLogDir}
+          '';
+
+        script =
+          ''
+            for i in ${toString config.services.nagios.plugins}; do
+              export PATH=$i/bin:$i/sbin:$i/libexec:$PATH
+            done
+            exec ${pkgs.nagios}/bin/nagios ${nagiosCfgFile}
+          '';
+      };
+
+    services.httpd.extraConfig = optionalString cfg.enableWebInterface extraHttpdConfig;
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/nagios/host-templates.cfg b/nixos/modules/services/monitoring/nagios/host-templates.cfg
new file mode 100644
index 00000000000..3a4c269e257
--- /dev/null
+++ b/nixos/modules/services/monitoring/nagios/host-templates.cfg
@@ -0,0 +1,27 @@
+define host {
+    name                            generic-host
+    notifications_enabled           1
+    event_handler_enabled           1
+    flap_detection_enabled          1
+    failure_prediction_enabled      1
+    process_perf_data               1
+    retain_status_information       1
+    retain_nonstatus_information    1
+    notification_period             24x7
+    register                        0
+}
+
+
+define host {
+    name                            generic-server
+    use                             generic-host
+    check_period                    24x7
+    max_check_attempts              10
+    check_command                   check-host-alive
+    notification_period             24x7
+    notification_interval           120
+    notification_options            d,u,r
+    contact_groups                  admins
+    register                        0
+    #check_interval                 1
+}
diff --git a/nixos/modules/services/monitoring/nagios/service-templates.cfg b/nixos/modules/services/monitoring/nagios/service-templates.cfg
new file mode 100644
index 00000000000..e729ea77675
--- /dev/null
+++ b/nixos/modules/services/monitoring/nagios/service-templates.cfg
@@ -0,0 +1,32 @@
+define service {
+    name                            generic-service
+    active_checks_enabled           1
+    passive_checks_enabled          1
+    parallelize_check               1
+    obsess_over_service             1
+    check_freshness                 0
+    notifications_enabled           1
+    event_handler_enabled           1
+    flap_detection_enabled          1
+    failure_prediction_enabled      1
+    process_perf_data               1
+    retain_status_information       1
+    retain_nonstatus_information    1
+    is_volatile                     0
+    register                        0
+}
+
+
+define service {
+    name                            local-service
+    use                             generic-service
+    check_period                    24x7
+    max_check_attempts              4
+    normal_check_interval           5
+    retry_check_interval            1
+    contact_groups                  admins
+    notification_options            w,u,c,r
+    notification_interval           0 # notify only once
+    notification_period             24x7
+    register                        0
+}
diff --git a/nixos/modules/services/monitoring/nagios/timeperiods.cfg b/nixos/modules/services/monitoring/nagios/timeperiods.cfg
new file mode 100644
index 00000000000..2669be54d3d
--- /dev/null
+++ b/nixos/modules/services/monitoring/nagios/timeperiods.cfg
@@ -0,0 +1,11 @@
+define timeperiod {
+    timeperiod_name 24x7
+    alias           24 Hours A Day, 7 Days A Week
+    sunday          00:00-24:00
+    monday          00:00-24:00
+    tuesday         00:00-24:00
+    wednesday       00:00-24:00
+    thursday        00:00-24:00
+    friday          00:00-24:00
+    saturday        00:00-24:00
+}
diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix
new file mode 100644
index 00000000000..de07dc0dbaa
--- /dev/null
+++ b/nixos/modules/services/monitoring/smartd.nix
@@ -0,0 +1,117 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.smartd;
+
+  smartdOpts = { name, ... }: {
+
+    options = {
+
+      device = mkOption {
+        example = "/dev/sda";
+        type = types.string;
+        description = "Location of the device.";
+      };
+
+      options = mkOption {
+        default = "";
+        example = "-d sat";
+        type = types.string;
+        merge = pkgs.lib.concatStringsSep " ";
+        description = "Options that determine how smartd monitors the device";
+      };
+    };
+
+  };
+
+  smartdMail = pkgs.writeScript "smartdmail.sh" ''
+    #! ${pkgs.stdenv.shell}
+    TMPNAM=/tmp/smartd-message.$$.tmp
+    if test -n "$SMARTD_ADDRESS"; then
+      echo  >"$TMPNAM" "From: smartd <root>"
+      echo >>"$TMPNAM" 'To: undisclosed-recipients:;'
+      echo >>"$TMPNAM" "Subject: $SMARTD_SUBJECT"
+      echo >>"$TMPNAM"
+      echo >>"$TMPNAM" "Failure on $SMARTD_DEVICESTRING: $SMARTD_FAILTYPE"
+      echo >>"$TMPNAM"
+      cat  >>"$TMPNAM"
+      ${pkgs.smartmontools}/sbin/smartctl >>"$TMPNAM" -a -d "$SMARTD_DEVICETYPE" "$SMARTD_DEVICE"
+      /var/setuid-wrappers/sendmail  <"$TMPNAM" -f "$SENDER" -i "$SMARTD_ADDRESS"
+    fi
+  '';
+
+  smartdConf = pkgs.writeText "smartd.conf" (concatMapStrings (device:
+    ''
+      ${device.device} -a -m root -M exec ${smartdMail} ${device.options} ${cfg.deviceOpts}
+    ''
+    ) cfg.devices);
+
+  smartdFlags = if (cfg.devices == []) then "" else "--configfile=${smartdConf}";
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.smartd = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        example = "true";
+        description = ''
+          Run smartd from the smartmontools package. Note that e-mail
+          notifications will not be enabled unless you configure the list of
+          devices with <varname>services.smartd.devices</varname> as well.
+        '';
+      };
+
+      deviceOpts = mkOption {
+        default = "";
+        type = types.string;
+        example = "-o on -s (S/../.././02|L/../../7/04)";
+        description = ''
+          Additional options for each device that is monitored. The example
+          turns on SMART Automatic Offline Testing on startup, and schedules short
+          self-tests daily, and long self-tests weekly.
+        '';
+      };
+
+      devices = mkOption {
+        default = [];
+        example = [ { device = "/dev/sda"; } { device = "/dev/sdb"; options = "-d sat"; } ];
+        type = types.listOf types.optionSet;
+        options = [ smartdOpts ];
+        description = ''
+          List of devices to monitor. By default -- if this list is empty --,
+          smartd will monitor all devices connected to the machine at the time
+          it's being run. Configuring this option has the added benefit of
+          enabling e-mail notifications to "root" every time smartd detects an
+          error.
+        '';
+       };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.smartd = {
+      description = "S.M.A.R.T. Daemon";
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd --no-fork ${smartdFlags}";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
new file mode 100644
index 00000000000..a3266605671
--- /dev/null
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.statsd;
+
+  configFile = pkgs.writeText "statsd.conf" ''
+    {
+      host: "${cfg.host}",
+      port: "${toString cfg.port}",
+      backends: [${concatMapStrings (el: ''"./backends/${el}",'') cfg.backends}],
+      graphiteHost: "${cfg.graphiteHost}",
+      graphitePort: "${toString cfg.graphitePort}",
+      ${cfg.extraConfig}
+    }
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options.services.statsd = {
+
+    enable = mkOption {
+      description = "Whether to enable statsd stats aggregation service";
+      default = false;
+      type = types.uniq types.bool;
+    };
+
+    host = mkOption {
+      description = "Address that statsd listens on over UDP";
+      default = "127.0.0.1";
+      type = types.uniq types.string;
+    };
+
+    port = mkOption {
+      description = "Port that stats listens for messages on over UDP";
+      default = 8125;
+      type = types.uniq types.int;
+    };
+
+    backends = mkOption {
+      description = "List of backends statsd will use for data persistance";
+      default = ["graphite"];
+    };
+
+    graphiteHost = mkOption {
+      description = "Hostname or IP of Graphite server";
+      default = "127.0.0.1";
+      type = types.uniq types.string;
+    };
+
+    graphitePort = mkOption {
+      description = "Port of Graphite server";
+      default = 2003;
+      type = types.uniq types.int;
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      description = "Extra configuration options for statsd";
+      type = types.uniq types.string;
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton {
+      name = "statsd";
+      uid = config.ids.uids.statsd;
+      description = "Statsd daemon user";
+    };
+
+    systemd.services.statsd = {
+      description = "Statsd Server";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.nodePackages.statsd}/bin/statsd ${configFile}";
+        User = "statsd";
+      };
+    };
+
+    environment.systemPackages = [pkgs.nodePackages.statsd];
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/systemhealth.nix b/nixos/modules/services/monitoring/systemhealth.nix
new file mode 100644
index 00000000000..0a3e666ad4e
--- /dev/null
+++ b/nixos/modules/services/monitoring/systemhealth.nix
@@ -0,0 +1,133 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.systemhealth;
+
+  systemhealth = with pkgs; stdenv.mkDerivation {
+    name = "systemhealth-1.0";
+    src = fetchurl {
+      url = "http://www.brianlane.com/static/downloads/systemhealth/systemhealth-1.0.tar.bz2";
+      sha256 = "1q69lz7hmpbdpbz36zb06nzfkj651413n9icx0njmyr3xzq1j9qy";
+    };
+    buildInputs = [ python ];
+    installPhase = ''
+      ensureDir $out/bin
+      # Make it work for kernels 3.x, not so different than 2.6
+      sed -i 's/2\.6/4.0/' system_health.py
+      cp system_health.py $out/bin
+    '';
+  };
+
+  rrdDir = "/var/lib/health/rrd";
+  htmlDir = "/var/lib/health/html";
+
+  configFile = rrdDir + "/.syshealthrc";
+  # The program will try to read $HOME/.syshealthrc, so we set the proper home.
+  command = "HOME=${rrdDir} ${systemhealth}/bin/system_health.py";
+
+  cronJob = ''
+    */5 * * * * wwwrun ${command} --log
+    5 * * * * wwwrun ${command} --graph
+  '';
+
+  nameEqualName = s: "${s} = ${s}";
+  interfacesSection = concatStringsSep "\n" (map nameEqualName cfg.interfaces);
+
+  driveLine = d: "${d.path} = ${d.name}";
+  drivesSection = concatStringsSep "\n" (map driveLine cfg.drives);
+
+in
+{
+  options = {
+    services.systemhealth = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable the system health monitor and its generation of graphs.
+        '';
+      };
+
+      urlPrefix = mkOption {
+        default = "/health";
+        description = ''
+          The URL prefix under which the System Health web pages appear in httpd.
+        '';
+      };
+
+      interfaces = mkOption {
+        default = [ "lo" ];
+        example = [ "lo" "eth0" "eth1" ];
+        description = ''
+          Interfaces to monitor (minimum one).
+        '';
+      };
+
+      drives = mkOption {
+        default = [ ];
+        example = [ { name = "root"; path = "/"; } ];
+        description = ''
+          Drives to monitor.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.cron.systemCronJobs = [ cronJob ];
+
+    system.activationScripts.systemhealth = stringAfter [ "var" ]
+      ''
+        mkdir -p ${rrdDir} ${htmlDir}
+        chown wwwrun:wwwrun ${rrdDir} ${htmlDir}
+
+        cat >${configFile} << EOF
+        [paths]
+        rrdtool = ${pkgs.rrdtool}/bin/rrdtool
+        loadavg_rrd = loadavg
+        ps = /run/current-system/sw/bin/ps
+        df = /run/current-system/sw/bin/df
+        meminfo_rrd = meminfo
+        uptime_rrd = uptime
+        rrd_path = ${rrdDir}
+        png_path = ${htmlDir}
+
+        [processes]
+
+        [interfaces]
+        ${interfacesSection}
+
+        [drives]
+        ${drivesSection}
+
+        [graphs]
+        width = 400
+        time = ['-3hours', '-32hours', '-8days', '-5weeks', '-13months']
+        height = 100
+
+        [external]
+
+        EOF
+
+        chown wwwrun:wwwrun ${configFile}
+
+        ${pkgs.su}/bin/su -s "/bin/sh" -c "${command} --check" wwwrun
+        ${pkgs.su}/bin/su -s "/bin/sh" -c "${command} --html" wwwrun
+      '';
+
+    services.httpd.extraSubservices = [
+      { function = f: {
+          extraConfig = ''
+            Alias ${cfg.urlPrefix} ${htmlDir}
+
+            <Directory ${htmlDir}>
+                Order allow,deny
+                Allow from all
+            </Directory>
+          '';
+        };
+      }
+    ];
+  };
+}
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
new file mode 100644
index 00000000000..a7b72e53f0a
--- /dev/null
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -0,0 +1,275 @@
+{config, pkgs, ...}:
+
+# TODO: This is not secure, have a look at the file docs/security.txt inside
+# the project sources.
+with pkgs.lib;
+
+let
+  cfg = config.power.ups;
+in
+
+let
+  upsOptions = {name, config, ...}:
+  {
+    options = {
+      # This can be infered from the UPS model by looking at
+      # /nix/store/nut/share/driver.list
+      driver = mkOption {
+        type = types.uniq types.string;
+        description = ''
+          Specify the program to run to talk to this UPS.  apcsmart,
+          bestups, and sec are some examples.
+        '';
+      };
+
+      port = mkOption {
+        type = types.uniq types.string;
+        description = ''
+          The serial port to which your UPS is connected.  /dev/ttyS0 is
+          usually the first port on Linux boxes, for example.
+        '';
+      };
+
+      shutdownOrder = mkOption {
+        default = 0;
+        type = types.uniq types.int;
+        description = ''
+          When you have multiple UPSes on your system, you usually need to
+          turn them off in a certain order.  upsdrvctl shuts down all the
+          0s, then the 1s, 2s, and so on.  To exclude a UPS from the
+          shutdown sequence, set this to -1.
+        '';
+      };
+
+      maxStartDelay = mkOption {
+        default = null;
+        type = types.uniq (types.nullOr types.int);
+        description = ''
+          This can be set as a global variable above your first UPS
+          definition and it can also be set in a UPS section.  This value
+          controls how long upsdrvctl will wait for the driver to finish
+          starting.  This keeps your system from getting stuck due to a
+          broken driver or UPS.
+        '';
+      };
+
+      description = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Description of the UPS.
+        '';
+      };
+
+      directives = mkOption {
+        default = [];
+        type = types.listOf types.string;
+        description = ''
+          List of configuration directives for this UPS.
+        '';
+      };
+
+      summary = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Lines which would be added inside ups.conf for handling this UPS.
+        '';
+      };
+
+    };
+
+    config = {
+      directives = mkHeader ([
+        "driver = ${config.driver}"
+        "port = ${config.port}"
+        ''desc = "${config.description}"''
+        "sdorder = ${toString config.shutdownOrder}"
+      ] ++ (optional (config.maxStartDelay != null)
+            "maxstartdelay = ${toString config.maxStartDelay}")
+      );
+
+      summary =
+        concatStringsSep "\n      "
+          (["[${name}]"] ++ config.directives);
+    };
+  };
+
+in
+
+
+{
+  options = {
+    # powerManagement.powerDownCommands
+
+    power.ups = {
+      enable = mkOption {
+        default = false;
+        type = with types; bool;
+        description = ''
+          Enables support for Power Devices, such as Uninterruptible Power
+          Supplies, Power Distribution Units and Solar Controllers.
+        '';
+      };
+
+      # This option is not used yet.
+      mode = mkOption {
+        default = "standalone";
+        type = types.uniq types.string;
+        description = ''
+          The MODE determines which part of the NUT is to be started, and
+          which configuration files must be modified.
+
+          The values of MODE can be:
+
+          - none: NUT is not configured, or use the Integrated Power
+            Management, or use some external system to startup NUT
+            components. So nothing is to be started.
+
+          - standalone: This mode address a local only configuration, with 1
+            UPS protecting the local system. This implies to start the 3 NUT
+            layers (driver, upsd and upsmon) and the matching configuration
+            files. This mode can also address UPS redundancy.
+
+          - netserver: same as for the standalone configuration, but also
+            need some more ACLs and possibly a specific LISTEN directive in
+            upsd.conf.  Since this MODE is opened to the network, a special
+            care should be applied to security concerns.
+
+          - netclient: this mode only requires upsmon.
+        '';
+      };
+
+      schedulerRules = mkOption {
+        example = "/etc/nixos/upssched.conf";
+        type = types.uniq types.string;
+        description = ''
+          File which contains the rules to handle UPS events.
+        '';
+      };
+
+
+      maxStartDelay = mkOption {
+        default = 45;
+        type = types.uniq types.int;
+        description = ''
+          This can be set as a global variable above your first UPS
+          definition and it can also be set in a UPS section.  This value
+          controls how long upsdrvctl will wait for the driver to finish
+          starting.  This keeps your system from getting stuck due to a
+          broken driver or UPS.
+        '';
+      };
+
+      ups = mkOption {
+        default = {};
+        # see nut/etc/ups.conf.sample
+        description = ''
+          This is where you configure all the UPSes that this system will be
+          monitoring directly.  These are usually attached to serial ports,
+          but USB devices are also supported.
+        '';
+        type = types.attrsOf types.optionSet;
+        options = [ upsOptions ];
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.nut ];
+
+    jobs.upsmon = {
+      description = "Uninterruptible Power Supplies (Monitor)";
+      startOn = "ip-up";
+      daemonType = "fork";
+      exec = ''${pkgs.nut}/sbin/upsmon'';
+      environment.NUT_CONFPATH = "/etc/nut/";
+      environment.NUT_STATEPATH = "/var/lib/nut/";
+    };
+
+    jobs.upsd = {
+      description = "Uninterruptible Power Supplies (Daemon)";
+      startOn = "started network-interfaces and started upsmon";
+      daemonType = "fork";
+      # TODO: replace 'root' by another username.
+      exec = ''${pkgs.nut}/sbin/upsd -u root'';
+      environment.NUT_CONFPATH = "/etc/nut/";
+      environment.NUT_STATEPATH = "/var/lib/nut/";
+    };
+
+    jobs.upsdrv = {
+      description = "Uninterruptible Power Supplies (Register all UPS)";
+      startOn = "started upsd";
+      # TODO: replace 'root' by another username.
+      exec = ''${pkgs.nut}/bin/upsdrvctl -u root start'';
+      task = true;
+      environment.NUT_CONFPATH = "/etc/nut/";
+      environment.NUT_STATEPATH = "/var/lib/nut/";
+    };
+
+    environment.etc = [
+      { source = pkgs.writeText "nut.conf"
+        ''
+          MODE = ${cfg.mode}
+        '';
+        target = "nut/nut.conf";
+      }
+      { source = pkgs.writeText "ups.conf"
+        ''
+          maxstartdelay = ${toString cfg.maxStartDelay}
+
+          ${flip concatStringsSep (flip map (attrValues cfg.ups) (ups: ups.summary)) "
+
+          "}
+        '';
+        target = "nut/ups.conf";
+      }
+      { source = cfg.schedulerRules;
+        target = "nut/upssched.conf";
+      }
+      # These file are containing private informations and thus should not
+      # be stored inside the Nix store.
+      /*
+      { source = ;
+        target = "nut/upsd.conf";
+      }
+      { source = ;
+        target = "nut/upsd.users";
+      }
+      { source = ;
+        target = "nut/upsmon.conf;
+      }
+      */
+    ];
+
+    power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample";
+
+    system.activationScripts.upsSetup = stringAfter [ "users" "groups" ]
+      ''
+        # Used to store pid files of drivers.
+        mkdir -p /var/state/ups
+      '';
+
+
+/*
+    users.extraUsers = [
+      { name = "nut";
+        uid = 84;
+        home = "/var/lib/nut";
+        createHome = true;
+        group = "nut";
+        description = "UPnP A/V Media Server user";
+      }
+    ];
+
+    users.extraGroups = [
+      { name = "nut";
+        gid = 84;
+      }
+    ];
+*/
+
+  };
+}
diff --git a/nixos/modules/services/monitoring/uptime.nix b/nixos/modules/services/monitoring/uptime.nix
new file mode 100644
index 00000000000..fa3de7d90bc
--- /dev/null
+++ b/nixos/modules/services/monitoring/uptime.nix
@@ -0,0 +1,95 @@
+{ config, pkgs, ... }:
+let
+  inherit (pkgs.lib) mkOption mkEnableOption mkIf mkMerge types optionalAttrs optional;
+
+  cfg = config.services.uptime;
+
+  configDir = pkgs.runCommand "config" {} (if cfg.configFile != null then ''
+    mkdir $out
+    ext=`echo ${cfg.configFile} | grep -o \\..*`
+    ln -sv ${cfg.configFile} $out/default$ext
+    ln -sv /var/lib/uptime/runtime.json $out/runtime.json
+  '' else ''
+    mkdir $out
+    cat ${pkgs.nodePackages.node-uptime}/lib/node_modules/node-uptime/config/default.yaml > $out/default.yaml
+    cat >> $out/default.yaml <<EOF
+
+    autoStartMonitor: false
+
+    mongodb:
+      connectionString: 'mongodb://localhost/uptime'
+    EOF
+    ln -sv /var/lib/uptime/runtime.json $out/runtime.json
+  '');
+in {
+  options.services.uptime = {
+    configFile = mkOption {
+      description = ''
+        The uptime configuration file
+
+        If mongodb: server != localhost, please set usesRemoteMongo = true
+
+        If you only want to run the monitor, please set enableWebService = false
+        and enableSeparateMonitoringService = true
+
+        If autoStartMonitor: false (recommended) and you want to run both
+        services, please set enableSeparateMonitoringService = true
+      '';
+
+      type = types.nullOr types.path;
+
+      default = null;
+    };
+
+    usesRemoteMongo = mkOption {
+      description = "Whether the configuration file specifies a remote mongo instance";
+
+      default = false;
+
+      type = types.bool;
+    };
+
+    enableWebService = mkEnableOption "the uptime monitoring program web service";
+
+    enableSeparateMonitoringService = mkEnableOption "the uptime monitoring service (default: enableWebService == true)" // { default = cfg.enableWebService; };
+
+    nodeEnv = mkOption {
+      description = "The node environment to run in (development, production, etc.)";
+
+      type = types.string;
+
+      default = "production";
+    };
+  };
+
+  config = mkMerge [ (mkIf cfg.enableWebService {
+    systemd.services.uptime = {
+      description = "uptime web service";
+      wantedBy = [ "multi-user.target" ];
+      environment = {
+        NODE_CONFIG_DIR = configDir;
+        NODE_ENV = cfg.nodeEnv;
+        NODE_PATH = "${pkgs.nodePackages.node-uptime}/lib/node_modules/node-uptime/node_modules";
+      };
+      preStart = "mkdir -p /var/lib/uptime";
+      serviceConfig.ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.nodePackages.node-uptime}/lib/node_modules/node-uptime/app.js";
+    };
+
+    services.mongodb.enable = mkIf (!cfg.usesRemoteMongo) true;
+  }) (mkIf cfg.enableSeparateMonitoringService {
+    systemd.services.uptime-monitor = {
+      description = "uptime monitoring service";
+      wantedBy = [ "multi-user.target" ];
+      requires = optional cfg.enableWebService "uptime.service";
+      after = optional cfg.enableWebService "uptime.service";
+      environment = {
+        NODE_CONFIG_DIR = configDir;
+        NODE_ENV = cfg.nodeEnv;
+        NODE_PATH = "${pkgs.nodePackages.node-uptime}/lib/node_modules/node-uptime/node_modules";
+      };
+      # Ugh, need to wait for web service to be up
+      preStart = if cfg.enableWebService then "sleep 1s" else "mkdir -p /var/lib/uptime";
+      serviceConfig.ExecStart = "${pkgs.nodejs}/bin/node ${pkgs.nodePackages.node-uptime}/lib/node_modules/node-uptime/monitor.js";
+    };
+  }) ];
+}
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
new file mode 100644
index 00000000000..229236c1bbd
--- /dev/null
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -0,0 +1,100 @@
+# Zabbix agent daemon.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.zabbixAgent;
+
+  stateDir = "/var/run/zabbix";
+
+  logDir = "/var/log/zabbix";
+
+  pidFile = "${stateDir}/zabbix_agentd.pid";
+
+  configFile = pkgs.writeText "zabbix_agentd.conf"
+    ''
+      Server = ${cfg.server}
+
+      LogFile = ${logDir}/zabbix_agentd
+
+      PidFile = ${pidFile}
+
+      StartAgents = 1
+
+      ${config.services.zabbixAgent.extraConfig}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.zabbixAgent = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the Zabbix monitoring agent on this machine.
+          It will send monitoring data to a Zabbix server.
+        '';
+      };
+
+      server = mkOption {
+        default = "127.0.0.1";
+        description = ''
+          The IP address or hostname of the Zabbix server to connect to.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Configuration that is injected verbatim into the configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = "zabbix";
+        uid = config.ids.uids.zabbix;
+        description = "Zabbix daemon user";
+      };
+
+    systemd.services."zabbix-agent" =
+      { description = "Zabbix Agent";
+
+        wantedBy = [ "multi-user.target" ];
+
+        path = [ pkgs.nettools ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir} ${logDir}
+            chown zabbix ${stateDir} ${logDir}
+          '';
+
+        serviceConfig.ExecStart = "@${pkgs.zabbix.agent}/sbin/zabbix_agentd zabbix_agentd --config ${configFile}";
+        serviceConfig.Type = "forking";
+        serviceConfig.RemainAfterExit = true;
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = 2;
+      };
+
+    environment.systemPackages = [ pkgs.zabbix.agent ];
+
+  };
+
+}
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
new file mode 100644
index 00000000000..6735b4ca327
--- /dev/null
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -0,0 +1,113 @@
+# Zabbix server daemon.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.zabbixServer;
+
+  stateDir = "/var/run/zabbix";
+
+  logDir = "/var/log/zabbix";
+
+  libDir = "/var/lib/zabbix";
+
+  pidFile = "${stateDir}/zabbix_server.pid";
+
+  configFile = pkgs.writeText "zabbix_server.conf"
+    ''
+      LogFile = ${logDir}/zabbix_server
+
+      PidFile = ${pidFile}
+
+      ${optionalString (cfg.dbServer != "localhost") ''
+        DBHost = ${cfg.dbServer}
+      ''}
+
+      DBName = zabbix
+
+      DBUser = zabbix
+
+      ${optionalString (cfg.dbPassword != "") ''
+        DBPassword = ${cfg.dbPassword}
+      ''}
+    '';
+
+  useLocalPostgres = cfg.dbServer == "localhost" || cfg.dbServer == "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.zabbixServer.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to run the Zabbix server on this machine.
+      '';
+    };
+
+    services.zabbixServer.dbServer = mkOption {
+      default = "localhost";
+      description = ''
+        Hostname or IP address of the database server.
+        Use an empty string ("") to use peer authentication.
+      '';
+    };
+
+    services.zabbixServer.dbPassword = mkOption {
+      default = "";
+      description = "Password used to connect to the database server.";
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.postgresql.enable = useLocalPostgres;
+
+    users.extraUsers = singleton
+      { name = "zabbix";
+        uid = config.ids.uids.zabbix;
+        description = "Zabbix daemon user";
+      };
+
+    systemd.services."zabbix-server" =
+      { description = "Zabbix Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = optional useLocalPostgres "postgresql.service";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir} ${logDir} ${libDir}
+            chown zabbix ${stateDir} ${logDir} ${libDir}
+
+            if ! test -e "${libDir}/db-created"; then
+                ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole zabbix || true
+                ${pkgs.postgresql}/bin/createdb --owner zabbix zabbix || true
+                cat ${pkgs.zabbix.server}/share/zabbix/db/schema/postgresql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
+                cat ${pkgs.zabbix.server}/share/zabbix/db/data/images_pgsql.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
+                cat ${pkgs.zabbix.server}/share/zabbix/db/data/data.sql | ${pkgs.su}/bin/su -s "$SHELL" zabbix -c '${pkgs.postgresql}/bin/psql zabbix'
+                touch "${libDir}/db-created"
+            fi
+          '';
+
+        path = [ pkgs.nettools ];
+
+        serviceConfig.ExecStart = "@${pkgs.zabbix.server}/sbin/zabbix_server zabbix_server --config ${configFile}";
+        serviceConfig.Type = "forking";
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = 2;
+        serviceConfig.PIDFile = pidFile;
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/drbd.nix b/nixos/modules/services/network-filesystems/drbd.nix
new file mode 100644
index 00000000000..1a00ccab0a6
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/drbd.nix
@@ -0,0 +1,77 @@
+# Support for DRBD, the Distributed Replicated Block Device.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let cfg = config.services.drbd; in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.drbd.enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable support for DRBD, the Distributed Replicated
+        Block Device.
+      '';
+    };
+
+    services.drbd.config = mkOption {
+      default = "";
+      type = types.string;
+      description = ''
+        Contents of the <filename>drbd.conf</filename> configuration file.
+      '';
+    };
+
+  };
+
+  
+  ###### implementation
+
+  config = mkIf cfg.enable {
+  
+    environment.systemPackages = [ pkgs.drbd ];
+    
+    services.udev.packages = [ pkgs.drbd ];
+
+    boot.kernelModules = [ "drbd" ];
+
+    boot.extraModprobeConfig =
+      ''
+        options drbd usermode_helper=/run/current-system/sw/sbin/drbdadm
+      '';
+
+    environment.etc = singleton
+      { source = pkgs.writeText "drbd.conf" cfg.config;
+        target = "drbd.conf";
+      };
+
+    jobs.drbd_up =
+      { name = "drbd-up";
+        startOn = "stopped udevtrigger or ip-up";
+        task = true;
+        script =
+          ''
+            ${pkgs.drbd}/sbin/drbdadm up all
+          '';
+      };
+    
+    jobs.drbd_down =
+      { name = "drbd-down";
+        startOn = "starting shutdown";
+        task = true;
+        script =
+          ''
+            ${pkgs.drbd}/sbin/drbdadm down all
+          '';
+      };
+    
+  };
+  
+}
diff --git a/nixos/modules/services/network-filesystems/nfsd.nix b/nixos/modules/services/network-filesystems/nfsd.nix
new file mode 100644
index 00000000000..4daa5e9d063
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/nfsd.nix
@@ -0,0 +1,147 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.nfs.server;
+
+  exports = pkgs.writeText "exports" cfg.exports;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.nfs = {
+
+      server = {
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable the kernel's NFS server.
+          '';
+        };
+
+        exports = mkOption {
+          default = "";
+          description = ''
+            Contents of the /etc/exports file.  See
+            <citerefentry><refentrytitle>exports</refentrytitle>
+            <manvolnum>5</manvolnum></citerefentry> for the format.
+          '';
+        };
+
+        hostName = mkOption {
+          default = null;
+          description = ''
+            Hostname or address on which NFS requests will be accepted.
+            Default is all.  See the <option>-H</option> option in
+            <citerefentry><refentrytitle>nfsd</refentrytitle>
+            <manvolnum>8</manvolnum></citerefentry>.
+          '';
+        };
+
+        nproc = mkOption {
+          default = 8;
+          description = ''
+            Number of NFS server threads.  Defaults to the recommended value of 8.
+          '';
+        };
+
+        createMountPoints = mkOption {
+          default = false;
+          description = "Whether to create the mount points in the exports file at startup time.";
+        };
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.rpcbind.enable = true;
+
+    boot.supportedFilesystems = [ "nfs" ]; # needed for statd and idmapd
+
+    environment.systemPackages = [ pkgs.nfsUtils ];
+
+    environment.etc = singleton
+      { source = exports;
+        target = "exports";
+      };
+
+    boot.kernelModules = [ "nfsd" ];
+
+    systemd.services.nfsd =
+      { description = "NFS Server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        requires = [ "rpcbind.service" "mountd.service" ];
+        after = [ "rpcbind.service" "mountd.service" "idmapd.service" ];
+        before = [ "statd.service" ];
+
+        path = [ pkgs.nfsUtils ];
+
+        script =
+          ''
+            # Create a state directory required by NFSv4.
+            mkdir -p /var/lib/nfs/v4recovery
+
+            rpc.nfsd \
+              ${if cfg.hostName != null then "-H ${cfg.hostName}" else ""} \
+              ${builtins.toString cfg.nproc}
+          '';
+
+        postStop = "rpc.nfsd 0";
+
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+      };
+
+    systemd.services.mountd =
+      { description = "NFSv3 Mount Daemon";
+
+        requires = [ "rpcbind.service" ];
+        after = [ "rpcbind.service" ];
+
+        path = [ pkgs.nfsUtils pkgs.sysvtools pkgs.utillinux ];
+
+        preStart =
+          ''
+            mkdir -p /var/lib/nfs
+            touch /var/lib/nfs/rmtab
+
+            mountpoint -q /proc/fs/nfsd || mount -t nfsd none /proc/fs/nfsd
+
+            ${optionalString cfg.createMountPoints
+              ''
+                # create export directories:
+                # skip comments, take first col which may either be a quoted
+                # "foo bar" or just foo (-> man export)
+                sed '/^#.*/d;s/^"\([^"]*\)".*/\1/;t;s/[ ].*//' ${exports} \
+                | xargs -d '\n' mkdir -p
+              ''
+            }
+
+            exportfs -rav
+          '';
+
+        restartTriggers = [ exports ];
+
+        serviceConfig.Type = "forking";
+        serviceConfig.ExecStart = "@${pkgs.nfsUtils}/sbin/rpc.mountd rpc.mountd";
+        serviceConfig.Restart = "always";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
new file mode 100644
index 00000000000..4a888b64bd3
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix
@@ -0,0 +1,90 @@
+{ config, pkgs, ... }:
+
+let
+  inherit (pkgs.lib) mkOption mkIf;
+
+  cfg = config.services.openafsClient;
+
+  cellServDB = pkgs.fetchurl {
+    url = http://dl.central.org/dl/cellservdb/CellServDB.2009-06-29;
+    sha256 = "be566f850e88130333ab8bc3462872ad90c9482e025c60a92f728b5bac1b4fa9";
+  };
+
+  afsConfig = pkgs.runCommand "afsconfig" {} ''
+    ensureDir $out
+    echo ${cfg.cellName} > $out/ThisCell
+    cp ${cellServDB} $out/CellServDB
+    echo "/afs:${cfg.cacheDirectory}:${cfg.cacheSize}" > $out/cacheinfo
+  '';
+
+  openafsPkgs = config.boot.kernelPackages.openafsClient;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.openafsClient = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the OpenAFS client.";
+      };
+
+      cellName = mkOption {
+        default = "grand.central.org";
+        description = "Cell name.";
+      };
+
+      cacheSize = mkOption {
+        default = "100000";
+        description = "Cache size.";
+      };
+
+      cacheDirectory = mkOption {
+        default = "/var/cache/openafs";
+        description = "Cache directory.";
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ openafsPkgs ];
+
+    environment.etc = [
+      { source = afsConfig;
+        target = "openafs";
+      }
+    ];
+
+    jobs.openafsClient =
+      { name = "afsd";
+
+        description = "AFS client";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+	preStart = ''
+	  mkdir -m 0755 /afs || true
+	  mkdir -m 0755 -p ${cfg.cacheDirectory} || true
+          ${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 -shutdown
+	  rmmod libafs
+	'';
+
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
new file mode 100644
index 00000000000..70a14487ea5
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -0,0 +1,234 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.samba;
+
+  user = "smbguest";
+  group = "smbguest";
+
+  logDir = "/var/log/samba";
+  privateDir = "/var/samba/private";
+
+  inherit (pkgs) samba;
+
+  setupScript =
+    ''
+      if ! test -d /home/smbd ; then
+        mkdir -p /home/smbd
+        chown ${user} /home/smbd
+        chmod a+rwx /home/smbd
+      fi
+
+      if ! test -d /var/samba ; then
+        mkdir -p /var/samba/locks /var/samba/cores/nmbd  /var/samba/cores/smbd /var/samba/cores/winbindd
+      fi
+
+      passwdFile="$(${pkgs.gnused}/bin/sed -n 's/^.*smb[ ]\+passwd[ ]\+file[ ]\+=[ ]\+\(.*\)/\1/p' ${configFile})"
+      if [ -n "$passwdFile" ]; then
+        echo 'INFO: [samba] creating directory containing passwd file'
+        mkdir -p "$(dirname "$passwdFile")"
+      fi
+
+      mkdir -p ${logDir}
+      mkdir -p ${privateDir}
+    '';
+
+  configFile = pkgs.writeText "smb.conf"
+    ''
+      [ global ]
+      log file = ${logDir}/log.%m
+      private dir = ${privateDir}
+      ${optionalString cfg.syncPasswordsByPam "pam password change = true"}
+
+      ${if cfg.defaultShare.enable then ''
+      [default]
+      path = /home/smbd
+      read only = ${if cfg.defaultShare.writeable then "no" else "yes"}
+      guest ok = ${if cfg.defaultShare.guest then "yes" else "no"}
+      ''else ""}
+
+      ${cfg.extraConfig}
+    '';
+
+  # This may include nss_ldap, needed for samba if it has to use ldap.
+  nssModulesPath = config.system.nssModules.path;
+
+  daemonService = appName: args:
+    { description = "Samba Service daemon ${appName}";
+
+      wantedBy = [ "samba.target" ];
+      partOf = [ "samba.target" ];
+
+      environment = {
+        LD_LIBRARY_PATH = nssModulesPath;
+        LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+      };
+
+      serviceConfig = {
+        ExecStart = "${samba}/sbin/${appName} ${args}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      };
+
+      restartTriggers = [ configFile ];
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    # !!! clean up the descriptions.
+
+    services.samba = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable Samba, which provides file and print
+          services to Windows clients through the SMB/CIFS protocol.
+        ";
+      };
+
+      syncPasswordsByPam = mkOption {
+        default = false;
+        description = "
+          enabling this will add a line directly after pam_unix.so.
+          Whenever a password is changed the samba password will be updated as well.
+          However you still yave to add the samba password once using smbpasswd -a user
+          If you don't want to maintain an extra pwd database you still can send plain text
+          passwords which is not secure.
+        ";
+      };
+
+      extraConfig = mkOption {
+        # !!! Bad default.
+        default = ''
+          # [global] continuing global section here, section is started by nix to set pids etc
+
+            smb passwd file = /etc/samba/passwd
+
+            # is this useful ?
+            domain master = auto
+
+            encrypt passwords = Yes
+            client plaintext auth = No
+
+            # yes: if you use this you probably also want to enable syncPasswordsByPam
+            # no: You can still use the pam password database. However
+            # passwords will be sent plain text on network (discouraged)
+
+            workgroup = Users
+            server string = %h
+            comment = Samba
+            log file = /var/log/samba/log.%m
+            log level = 10
+            max log size = 50000
+            security = ${cfg.securityType}
+
+            client lanman auth = Yes
+            dns proxy = no
+            invalid users = root
+            passdb backend = tdbsam
+            passwd program = /usr/bin/passwd %u
+        '';
+
+        description = "
+          additional global section and extra section lines go in here.
+        ";
+      };
+
+      configFile = mkOption {
+        description = "
+          internal use to pass filepath to samba pam module
+        ";
+      };
+
+      defaultShare = {
+        enable = mkOption {
+          description = "Whether to share /home/smbd as 'default'.";
+          default = false;
+        };
+        writeable = mkOption {
+          description = "Whether to allow write access to default share.";
+          default = false;
+        };
+        guest = mkOption {
+          description = "Whether to allow guest access to default share.";
+          default = true;
+        };
+      };
+
+      securityType = mkOption {
+        description = "Samba security type";
+        default = "user";
+        example = "share";
+      };
+
+      nsswins = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          Whether to enable the WINS NSS (Name Service Switch) plug-in.
+          Enabling it allows applications to resolve WINS/NetBIOS names (a.k.a.
+          Windows machine names) by transparently querying the winbindd daemon.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkMerge
+    [ { # Always provide a smb.conf to shut up programs like smbclient and smbspool.
+        environment.etc = singleton
+          { source =
+              if cfg.enable then configFile
+              else pkgs.writeText "smb-dummy.conf" "# Samba is disabled.";
+            target = "samba/smb.conf";
+          };
+      }
+
+      (mkIf config.services.samba.enable {
+        users.extraUsers.smbguest = {
+          description = "Samba service user";
+          group = group;
+          uid = config.ids.uids.smbguest;
+        };
+
+        users.extraGroups.smbguest.gid = config.ids.uids.smbguest;
+
+        system.nssModules = optional cfg.nsswins samba;
+
+        systemd = {
+          targets.samba = {
+            description = "Samba server";
+            requires = [ "samba-setup.service" ];
+            after = [ "samba-setup.service" "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+          };
+
+          services = {
+            "samba-nmbd" = daemonService "nmbd" "-F";
+            "samba-smbd" = daemonService "smbd" "-F";
+            "samba-winbindd" = daemonService "winbindd" "-F";
+            "samba-setup" = {
+              description = "Samba setup task";
+              script = setupScript;
+              unitConfig.RequiresMountsFor = "/home/smbd /var/samba /var/log/samba";
+            };
+          };
+        };
+
+      })
+    ];
+
+}
diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix
new file mode 100644
index 00000000000..8652d0daf4c
--- /dev/null
+++ b/nixos/modules/services/networking/amuled.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.amule;
+  user = if cfg.user != null then cfg.user else "amule";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.amule = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the AMule daemon. You need to manually run "amuled --ec-config" to configure the service for the first time.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = ''/home/${user}/'';
+        description = ''
+          The directory holding configuration, incoming and temporary files.
+        '';
+      };
+
+      user = mkOption {
+        default = null;
+        description = ''
+          The user the AMule daemon should run as.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = mkIf (cfg.user == null) [
+      { name = "amule";
+        description = "AMule daemon";
+        group = "amule";
+        uid = config.ids.uids.amule;
+      } ];
+
+    users.extraGroups = mkIf (cfg.user == null) [
+      { name = "amule";
+        gid = config.ids.gids.amule;
+      } ];
+
+    jobs.amuled =
+      { description = "AMule daemon";
+
+        startOn = "ip-up";
+
+        preStart = ''
+            mkdir -p ${cfg.dataDir}
+            chown ${user} ${cfg.dataDir}
+        '';
+
+        exec = ''
+            ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \
+                -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled'
+        '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
new file mode 100644
index 00000000000..3603d677837
--- /dev/null
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -0,0 +1,144 @@
+# Avahi daemon.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.avahi;
+
+  inherit (pkgs) avahi;
+
+  avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
+    [server]
+    ${# Users can set `networking.hostName' to the empty string, when getting
+      # a host name from DHCP.  In that case, let Avahi take whatever the
+      # current host name is; setting `host-name' to the empty string in
+      # `avahi-daemon.conf' would be invalid.
+      if hostName != ""
+      then "host-name=${hostName}"
+      else ""}
+    browse-domains=${concatStringsSep ", " browseDomains}
+    use-ipv4=${if ipv4 then "yes" else "no"}
+    use-ipv6=${if ipv6 then "yes" else "no"}
+
+    [wide-area]
+    enable-wide-area=${if wideArea then "yes" else "no"}
+
+    [publish]
+    disable-publishing=${if publishing then "no" else "yes"}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.avahi = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the Avahi daemon, which allows Avahi clients
+          to use Avahi's service discovery facilities and also allows
+          the local machine to advertise its presence and services
+          (through the mDNS responder implemented by `avahi-daemon').
+        '';
+      };
+
+      hostName = mkOption {
+        type = types.uniq types.string;
+        description = ''Host name advertised on the LAN.'';
+      };
+
+      browseDomains = mkOption {
+        default = [ "0pointer.de" "zeroconf.org" ];
+        description = ''
+          List of non-local DNS domains to be browsed.
+        '';
+      };
+
+      ipv4 = mkOption {
+        default = true;
+        description = ''Whether to use IPv4'';
+      };
+
+      ipv6 = mkOption {
+        default = false;
+        description = ''Whether to use IPv6'';
+      };
+
+      wideArea = mkOption {
+        default = true;
+        description = ''Whether to enable wide-area service discovery.'';
+      };
+
+      publishing = mkOption {
+        default = true;
+        description = ''Whether to allow publishing.'';
+      };
+
+      nssmdns = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the mDNS NSS (Name Service Switch) plug-in.
+          Enabling it allows applications to resolve names in the `.local'
+          domain by transparently querying the Avahi daemon.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.avahi.hostName = mkDefault config.networking.hostName;
+
+    users.extraUsers = singleton
+      { name = "avahi";
+        uid = config.ids.uids.avahi;
+        description = "`avahi-daemon' privilege separation user";
+        home = "/var/empty";
+      };
+
+    users.extraGroups = singleton
+      { name = "avahi";
+        gid = config.ids.gids.avahi;
+      };
+
+    system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
+
+    environment.systemPackages = [ avahi ];
+
+    jobs.avahi_daemon =
+      { name = "avahi-daemon";
+
+        startOn = "ip-up";
+
+        script =
+          ''
+            export PATH="${avahi}/bin:${avahi}/sbin:$PATH"
+
+            # Make NSS modules visible so that `avahi_nss_support ()' can
+            # return a sensible value.
+            export LD_LIBRARY_PATH="${config.system.nssModules.path}"
+
+            mkdir -p /var/run/avahi-daemon
+
+            exec ${avahi}/sbin/avahi-daemon --syslog -f "${avahiDaemonConf}"
+          '';
+      };
+
+    services.dbus.enable = true;
+    services.dbus.packages = [avahi];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix
new file mode 100644
index 00000000000..765dc014dcb
--- /dev/null
+++ b/nixos/modules/services/networking/bind.nix
@@ -0,0 +1,152 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.bind;
+
+  bindUser = "named";
+
+  confFile = pkgs.writeText "named.conf"
+    ''
+      acl cachenetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} };
+      acl badnetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} };
+
+      options {
+        listen-on {any;};
+        listen-on-v6 {any;};
+        allow-query { cachenetworks; };
+        blackhole { badnetworks; };
+        forward first;
+        forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} };
+        directory "/var/run/named";
+        pid-file "/var/run/named/named.pid";
+      };
+
+      ${ concatMapStrings
+          ({ name, file, master ? true, slaves ? [], masters ? [] }:
+            ''
+              zone "${name}" {
+                type ${if master then "master" else "slave"};
+                file "${file}";
+                ${ if master then
+                   ''
+                     allow-transfer {
+                       ${concatMapStrings (ip: "${ip};\n") slaves}
+                     };
+                   ''
+                   else
+                   ''
+                     masters {
+                       ${concatMapStrings (ip: "${ip};\n") masters}
+                     };
+                   ''
+                }
+                allow-query { any; };
+              };
+            '')
+          cfg.zones }
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.bind = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable BIND domain name server.
+        ";
+      };
+
+      cacheNetworks = mkOption {
+        default = ["127.0.0.0/24"];
+        description = "
+          What networks are allowed to use us as a resolver.
+        ";
+      };
+
+      blockedNetworks = mkOption {
+        default = [];
+        description = "
+          What networks are just blocked.
+        ";
+      };
+
+      ipv4Only = mkOption {
+        default = false;
+        description = "
+          Only use ipv4, even if the host supports ipv6.
+        ";
+      };
+
+      forwarders = mkOption {
+        default = config.networking.nameservers;
+        description = "
+          List of servers we should forward requests to.
+        ";
+      };
+
+      zones = mkOption {
+        default = [];
+        description = "
+          List of zones we claim authority over.
+            master=false means slave server; slaves means addresses
+           who may request zone transfer.
+        ";
+        example = [{
+          name = "example.com";
+          master = false;
+          file = "/var/dns/example.com";
+          masters = ["192.168.0.1"];
+          slaves = [];
+        }];
+      };
+
+      configFile = mkOption {
+        default = confFile;
+        description = "
+          Overridable config file to use for named. By default, that
+          generated by nixos.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.bind.enable {
+
+    users.extraUsers = singleton
+      { name = bindUser;
+        uid = config.ids.uids.bind;
+        description = "BIND daemon user";
+      };
+
+    jobs.bind =
+      { description = "BIND name server job";
+
+        startOn = "started network-interfaces";
+
+        preStart =
+          ''
+            ${pkgs.coreutils}/bin/mkdir -p /var/run/named
+            chown ${bindUser} /var/run/named
+          '';
+
+        exec = "${pkgs.bind}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
new file mode 100644
index 00000000000..82e875f5aae
--- /dev/null
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -0,0 +1,123 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.bitlbee;
+  bitlbeeUid = config.ids.uids.bitlbee;
+
+  authModeCheck = v:
+    v == "Open" ||
+    v == "Closed" ||
+    v == "Registered";
+
+  bitlbeeConfig = pkgs.writeText "bitlbee.conf"
+    ''
+    [settings]
+    RunMode = Daemon
+    User = bitlbee  
+    ConfigDir = /var/lib/bitlbee      
+    DaemonInterface = ${cfg.interface}
+    DaemonPort = ${toString cfg.portNumber}
+    AuthMode = ${cfg.authMode}
+    ${cfg.extraSettings}
+
+    [defaults]
+    ${cfg.extraDefaults}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.bitlbee = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the BitlBee IRC to other chat network gateway.
+          Running it allows you to access the MSN, Jabber, Yahoo! and ICQ chat
+          networks via an IRC client.
+        '';
+      };
+
+      interface = mkOption {
+        default = "127.0.0.1";
+        description = ''
+          The interface the BitlBee deamon will be listening to.  If `127.0.0.1',
+          only clients on the local host can connect to it; if `0.0.0.0', clients
+          can access it from any network interface.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 6667;
+        description = ''
+          Number of the port BitlBee will be listening to.
+        '';
+      };
+
+      authMode = mkOption {
+        default = "Open";
+        check = authModeCheck;
+        description = ''
+          The following authentication modes are available:
+            Open -- Accept connections from anyone, use NickServ for user authentication.
+            Closed -- Require authorization (using the PASS command during login) before allowing the user to connect at all.
+            Registered -- Only allow registered users to use this server; this disables the register- and the account command until the user identifies himself.
+        ''; 
+      };
+
+      extraSettings = mkOption {
+        default = "";
+        description = ''
+          Will be inserted in the Settings section of the config file.
+        ''; 
+      };
+
+      extraDefaults = mkOption {
+        default = "";
+        description = ''
+          Will be inserted in the Default section of the config file.
+        ''; 
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.bitlbee.enable {
+
+    users.extraUsers = singleton
+      { name = "bitlbee";
+        uid = bitlbeeUid;
+        description = "BitlBee user";
+        home = "/var/lib/bitlbee";
+        createHome = true;
+      };
+
+    users.extraGroups = singleton
+      { name = "bitlbee";
+        gid = config.ids.gids.bitlbee;
+      };
+
+    systemd.services.bitlbee = 
+      { description = "BitlBee IRC to other chat networks gateway";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig.User = "bitlbee";
+        serviceConfig.ExecStart = "${pkgs.bitlbee}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
+      };
+
+    environment.systemPackages = [ pkgs.bitlbee ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
new file mode 100644
index 00000000000..5e9818858e0
--- /dev/null
+++ b/nixos/modules/services/networking/chrony.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) chrony;
+
+  stateDir = "/var/lib/chrony";
+
+  chronyUser = "chrony";
+
+  cfg = config.services.chrony;
+
+  configFile = pkgs.writeText "chrony.conf" ''
+    ${toString (map (server: "server " + server + "\n") cfg.servers)}
+
+    ${optionalString cfg.initstepslew.enabled ''
+      initstepslew ${toString cfg.initstepslew.threshold} ${toString (map (server: server + " ") cfg.initstepslew.servers)}
+    ''}
+
+    driftfile ${stateDir}/chrony.drift
+
+    ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"}
+
+    ${cfg.extraConfig}
+  '';
+
+  chronyFlags = "-m -f ${configFile} -u ${chronyUser}";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.chrony = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to synchronise your machine's time using chrony.
+          Make sure you disable NTP if you enable this service.
+        '';
+      };
+
+      servers = mkOption {
+        default = [
+          "0.pool.ntp.org"
+          "1.pool.ntp.org"
+          "2.pool.ntp.org"
+        ];
+        description = ''
+          The set of NTP servers from which to synchronise.
+        '';
+      };
+
+      initstepslew = mkOption {
+        default = {
+          enabled = true;
+          threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+          servers = cfg.servers;
+        };
+        description = ''
+          Allow chronyd to make a rapid measurement of the system clock error at
+          boot time, and to correct the system clock by stepping before normal
+          operation begins.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Extra configuration directives that should be added to
+          <literal>chrony.conf</literal>
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.chrony.enable {
+
+    # Make chronyc available in the system path
+    environment.systemPackages = [ pkgs.chrony ];
+
+    users.extraUsers = singleton
+      { name = chronyUser;
+        uid = config.ids.uids.chrony;
+        description = "chrony daemon user";
+        home = stateDir;
+      };
+
+    jobs.chronyd =
+      { description = "chrony daemon";
+
+        wantedBy = [ "ip-up.target" ];
+        partOf = [ "ip-up.target" ];
+
+        path = [ chrony ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${chronyUser} ${stateDir}
+          '';
+
+        exec = "chronyd -n ${chronyFlags}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/cntlm.nix b/nixos/modules/services/networking/cntlm.nix
new file mode 100644
index 00000000000..bfe7209b991
--- /dev/null
+++ b/nixos/modules/services/networking/cntlm.nix
@@ -0,0 +1,115 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.cntlm;
+  uid = config.ids.uids.cntlm;
+
+in
+
+{
+
+  options = {
+
+    services.cntlm = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the cntlm, which start a local proxy.
+        '';
+      };
+
+      username = mkOption {
+        description = ''
+          Proxy account name, without the possibility to include domain name ('at' sign is interpreted literally).
+        '';
+      };
+
+      domain = mkOption {
+        description = ''Proxy account domain/workgroup name.'';
+      };
+
+      password = mkOption {
+        default = "/etc/cntlm.password";
+        type = with pkgs.lib.types; string;
+        description = ''Proxy account password. Note: use chmod 0600 on /etc/cntlm.password for security.'';
+      };
+
+      netbios_hostname = mkOption {
+        type = types.uniq types.string;
+        description = ''
+          The hostname of your machine.
+        '';
+      };
+
+      proxy = mkOption {
+        description = ''
+          A list of NTLM/NTLMv2 authenticating HTTP proxies.
+
+          Parent proxy, which requires authentication. The same as proxy on the command-line, can be used more than  once  to  specify  unlimited
+          number  of  proxies.  Should  one proxy fail, cntlm automatically moves on to the next one. The connect request fails only if the whole
+          list of proxies is scanned and (for each request) and found to be invalid. Command-line takes precedence over the configuration file.
+        '';
+      };
+
+      port = mkOption {
+        default = [3128];
+        description = "Specifies on which ports the cntlm daemon listens.";
+      };
+
+     extraConfig = mkOption {
+        default = "";
+        description = "Verbatim contents of <filename>cntlm.conf</filename>.";
+     };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.cntlm.enable {
+
+    services.cntlm.netbios_hostname = mkDefault config.networking.hostName;
+  
+    users.extraUsers = singleton { 
+      name = "cntlm";
+      description = "cntlm system-wide daemon";
+      home = "/var/empty";
+    };
+
+    jobs.cntlm =
+      { description = "CNTLM is an NTLM / NTLM Session Response / NTLMv2 authenticating HTTP proxy";
+      
+        startOn = "started network-interfaces";
+
+        daemonType = "fork";
+
+        exec =
+          ''
+            ${pkgs.cntlm}/bin/cntlm -U cntlm \
+            -c ${pkgs.writeText "cntlm_config" cfg.extraConfig}
+          '';
+      };
+
+    services.cntlm.extraConfig =
+      ''
+        # Cntlm Authentication Proxy Configuration
+        Username        ${cfg.username}
+        Domain          ${cfg.domain}
+        Password        ${cfg.password}
+        Workstation     ${cfg.netbios_hostname}
+        ${concatMapStrings (entry: "Proxy ${entry}\n") cfg.proxy}
+    
+        ${concatMapStrings (port: ''
+          Listen ${toString port}
+        '') cfg.port}
+      '';
+      
+  };
+  
+}
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
new file mode 100644
index 00000000000..62709a040a1
--- /dev/null
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -0,0 +1,127 @@
+{ config, pkgs, ... }:
+
+let
+
+  inherit (pkgs.lib) mkOption mkIf singleton;
+
+  inherit (pkgs) ddclient;
+
+  stateDir = "/var/spool/ddclient";
+
+  ddclientUser = "ddclient";
+
+  ddclientFlags = "-foreground -file ${ddclientCfg}";
+
+  ddclientCfg = pkgs.writeText "ddclient.conf" ''
+    daemon=600
+    cache=${stateDir}/ddclient.cache
+    pid=${stateDir}/ddclient.pid
+    use=${config.services.ddclient.web}
+    login=${config.services.ddclient.username}
+    password=${config.services.ddclient.password}
+    protocol=${config.services.ddclient.protocol}
+    server=${config.services.ddclient.server}
+    wildcard=YES
+    ${config.services.ddclient.domain}
+    ${config.services.ddclient.extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ddclient = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
+        '';
+      };
+
+      domain = mkOption {
+        default = "";
+        description = ''
+          Domain name to synchronize.
+        '';
+      };
+
+      username = mkOption {
+        default = "";
+        description = ''
+          Username.
+        '';
+      };
+
+      password = mkOption {
+        default = "" ;
+        description = ''
+          Password.
+        '';
+      };
+
+      protocol = mkOption {
+        default = "dyndns2" ;
+        description = ''
+          Protocol to use with dynamic DNS provider. (see also, http://sourceforge.net/apps/trac/ddclient/wiki/Protocols)
+        '';
+      };
+
+      server = mkOption {
+        default = "members.dyndns.org" ;
+        description = ''
+          Server
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "" ;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+
+      web = mkOption {
+        default = "web, web=checkip.dyndns.com/, web-skip='IP Address'" ;
+        description = "";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ddclient.enable {
+  
+    environment.systemPackages = [ ddclient ];
+
+    users.extraUsers = singleton
+      { name = ddclientUser;
+        uid = config.ids.uids.ddclient;
+        description = "ddclient daemon user";
+        home = stateDir;
+      };
+
+    jobs.ddclient =
+      { name = "ddclient";
+
+        startOn = "startup";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${ddclientUser} ${stateDir}
+          '';
+
+        exec = "${ddclient}/bin/ddclient ${ddclientFlags}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/dhclient.nix b/nixos/modules/services/networking/dhclient.nix
new file mode 100644
index 00000000000..1e343443899
--- /dev/null
+++ b/nixos/modules/services/networking/dhclient.nix
@@ -0,0 +1,111 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) nettools dhcp lib;
+
+  # Don't start dhclient on explicitly configured interfaces or on
+  # interfaces that are part of a bridge.
+  ignoredInterfaces =
+    map (i: i.name) (lib.filter (i: i ? ipAddress && i.ipAddress != "" ) config.networking.interfaces)
+    ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges));
+
+  stateDir = "/var/lib/dhcp"; # Don't use /var/state/dhcp; not FHS-compliant.
+
+  dhclientExitHooks = pkgs.writeText "dhclient-exit-hooks"
+    ''
+      #echo "$reason" >> /tmp/dhcp-exit
+      #echo "$exit_status" >> /tmp/dhcp-exit
+
+      if test "$reason" = BOUND -o "$reason" = REBOOT; then
+          # Restart ntpd.  (The "ip-up" event below will trigger the
+          # restart.)  We need to restart it to make sure that it will
+          # actually do something: if ntpd cannot resolve the server
+          # hostnames in its config file, then it will never do
+          # anything ever again ("couldn't resolve ..., giving up on
+          # it"), so we silently lose time synchronisation.
+          ${config.system.build.upstart}/sbin/initctl stop ntpd
+
+          ${config.system.build.upstart}/sbin/initctl emit -n ip-up
+      fi
+
+      if test "$reason" = EXPIRE -o "$reason" = RELEASE; then
+          ${config.system.build.upstart}/sbin/initctl emit -n ip-down
+      fi
+    '';
+
+in
+
+{
+
+  ###### implementation
+
+  config = mkIf config.networking.useDHCP {
+
+    # dhclient barfs if /proc/net/if_inet6 doesn't exist.
+    boot.kernelModules = [ "ipv6" ];
+
+    jobs.dhclient =
+      { startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        path = [ dhcp ];
+
+        script =
+          ''
+            # Determine the interface on which to start dhclient.
+            interfaces=
+
+            for i in $(cd /sys/class/net && ls -d *); do
+                # Only run dhclient on interfaces of type ARPHRD_ETHER
+                # (1), i.e. Ethernet.  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).
+                if [ "$(cat /sys/class/net/$i/type)" = 1 ]; then
+                    if ! for j in ${toString ignoredInterfaces}; do echo $j; done | grep -F -x -q "$i" &&
+                       ! echo "$i" | grep -x -q "peth.*\|vif.*\|tap.*\|virbr.*\|vnet.*";
+		    then
+                        echo "Running dhclient on $i"
+                        interfaces="$interfaces $i"
+                    fi
+                fi
+            done
+
+            if test -z "$interfaces"; then
+                echo 'No interfaces on which to start dhclient!'
+                exit 1
+            fi
+
+            mkdir -m 755 -p ${stateDir}
+
+            exec dhclient -d $interfaces -e "PATH=$PATH" -lf ${stateDir}/dhclient.leases -sf ${dhcp}/sbin/dhclient-script
+          '';
+      };
+
+    environment.systemPackages = [dhcp];
+
+    environment.etc =
+      [ # Dhclient hooks for emitting ip-up/ip-down events.
+        { source = dhclientExitHooks;
+          target = "dhclient-exit-hooks";
+        }
+      ];
+
+    powerManagement.resumeCommands =
+      ''
+        ${config.system.build.upstart}/sbin/restart dhclient
+      '';
+
+    networking.interfaceMonitor.commands =
+      ''
+        if [ "$status" = up ]; then
+          ${config.system.build.upstart}/sbin/restart dhclient
+        fi
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
new file mode 100644
index 00000000000..48803511a5e
--- /dev/null
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -0,0 +1,143 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) dhcpcd;
+
+  # Don't start dhclient on explicitly configured interfaces or on
+  # interfaces that are part of a bridge.
+  ignoredInterfaces =
+    map (i: i.name) (filter (i: i.ipAddress != null) (attrValues config.networking.interfaces))
+    ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
+    ++ config.networking.dhcpcd.denyInterfaces;
+
+  # Config file adapted from the one that ships with dhcpcd.
+  dhcpcdConf = pkgs.writeText "dhcpcd.conf"
+    ''
+      # Inform the DHCP server of our hostname for DDNS.
+      hostname
+
+      # A list of options to request from the DHCP server.
+      option domain_name_servers, domain_name, domain_search, host_name
+      option classless_static_routes, ntp_servers, interface_mtu
+
+      # A ServerID is required by RFC2131.
+      # Commented out because of many non-compliant DHCP servers in the wild :(
+      #require dhcp_server_identifier
+
+      # A hook script is provided to lookup the hostname if not set by
+      # the DHCP server, but it should not be run by default.
+      nohook lookup-hostname
+
+      # 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*
+
+      ${config.networking.dhcpcd.extraConfig}
+    '';
+
+  # Hook for emitting ip-up/ip-down events.
+  exitHook = pkgs.writeText "dhcpcd.exit-hook"
+    ''
+      #exec >> /var/log/dhcpcd 2>&1
+      #set -x
+
+      params="IFACE=$interface REASON=$reason"
+
+      # only works when interface is wireless and wpa_supplicant has a control socket
+      # but we allow it to fail silently
+      ${optionalString config.networking.wireless.enable ''
+        params+=" $(${pkgs.wpa_supplicant}/sbin/wpa_cli -i$interface status 2>/dev/null | grep ssid | sed 's|^b|B|;s|ssid|SSID|' | xargs)"
+      ''}
+
+      if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
+          # Restart ntpd.  We need to restart it to make sure that it
+          # will actually do something: if ntpd cannot resolve the
+          # server hostnames in its config file, then it will never do
+          # anything ever again ("couldn't resolve ..., giving up on
+          # it"), so we silently lose time synchronisation.
+          ${config.systemd.package}/bin/systemctl try-restart ntpd.service
+
+          ${config.systemd.package}/bin/systemctl start ip-up.target
+      fi
+
+      #if [ "$reason" = EXPIRE -o "$reason" = RELEASE -o "$reason" = NOCARRIER ] ; then
+      #    ${config.systemd.package}/bin/systemctl start ip-down.target
+      #fi
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.dhcpcd.denyInterfaces = mkOption {
+      default = [];
+      description = ''
+         Disable the DHCP client for any interface whose name matches
+         any of the shell glob patterns in this list. The purpose of
+         this option is to blacklist virtual interfaces such as those
+         created by Xen, libvirt, LXC, etc.
+      '';
+    };
+
+    networking.dhcpcd.extraConfig = mkOption {
+      default = "";
+      description = ''
+         Literal string to append to the config file generated for dhcpcd.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.networking.useDHCP {
+
+    systemd.services.dhcpcd =
+      { description = "DHCP Client";
+
+        wantedBy = [ "network.target" ];
+        after = [ "systemd-udev-settle.service" ];
+
+        # Stopping dhcpcd during a reconfiguration is undesirable
+        # because it brings down the network interfaces configured by
+        # dhcpcd.  So do a "systemctl restart" instead.
+        stopIfChanged = false;
+
+        path = [ dhcpcd pkgs.nettools pkgs.openresolv ];
+
+        serviceConfig =
+          { Type = "forking";
+            PIDFile = "/run/dhcpcd.pid";
+            ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --config ${dhcpcdConf}";
+            ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
+            StandardError = "null";
+            Restart = "always";
+          };
+      };
+
+    environment.systemPackages = [ dhcpcd ];
+
+    environment.etc =
+      [ { source = exitHook;
+          target = "dhcpcd.exit-hook";
+        }
+      ];
+
+    powerManagement.resumeCommands =
+      ''
+        # Tell dhcpcd to rebind its interfaces if it's running.
+        ${config.systemd.package}/bin/systemctl reload dhcpcd.service
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix
new file mode 100644
index 00000000000..43e0843cb97
--- /dev/null
+++ b/nixos/modules/services/networking/dhcpd.nix
@@ -0,0 +1,131 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.dhcpd;
+
+  stateDir = "/var/lib/dhcp"; # Don't use /var/state/dhcp; not FHS-compliant.
+
+  configFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "dhcpd.conf"
+    ''
+      default-lease-time 600;
+      max-lease-time 7200;
+      authoritative;
+      ddns-update-style ad-hoc;
+      log-facility local1; # see dhcpd.nix
+      
+      ${cfg.extraConfig}
+
+      ${pkgs.lib.concatMapStrings
+          (machine: ''
+            host ${machine.hostName} {
+              hardware ethernet ${machine.ethernetAddress};
+              fixed-address ${machine.ipAddress};
+            }
+          '')
+          cfg.machines
+      }
+    '';
+
+in
+  
+{
+
+  ###### interface
+
+  options = {
+  
+    services.dhcpd = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the DHCP server.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        example = "
+          option subnet-mask 255.255.255.0;
+          option broadcast-address 192.168.1.255;
+          option routers 192.168.1.5;
+          option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1;
+          option domain-name \"example.org\";
+          subnet 192.168.1.0 netmask 255.255.255.0 {
+            range 192.168.1.100 192.168.1.200;
+          }
+        ";
+        description = "
+          Extra text to be appended to the DHCP server configuration
+          file.  Currently, you almost certainly need to specify
+          something here, such as the options specifying the subnet
+          mask, DNS servers, etc.
+        ";
+      };
+
+      configFile = mkOption {
+        default = null;
+        description = "
+          The path of the DHCP server configuration file.  If no file
+          is specified, a file is generated using the other options.
+        ";
+      };
+
+      interfaces = mkOption {
+        default = ["eth0"];
+        description = "
+          The interfaces on which the DHCP server should listen.
+        ";
+      };
+
+      machines = mkOption {
+        default = [];
+        example = [
+          { hostName = "foo";
+            ethernetAddress = "00:16:76:9a:32:1d";
+            ipAddress = "192.168.1.10";
+          }
+          { hostName = "bar";
+            ethernetAddress = "00:19:d1:1d:c4:9a";
+            ipAddress = "192.168.1.11";
+          }
+        ];
+        description = "
+          A list mapping ethernet addresses to IP addresses for the
+          DHCP server.
+        ";
+      };
+
+    };
+    
+  };
+  
+
+  ###### implementation
+
+  config = mkIf config.services.dhcpd.enable {
+
+    jobs.dhcpd =
+      { description = "DHCP server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        script =
+          ''
+            mkdir -m 755 -p ${stateDir}
+
+            touch ${stateDir}/dhcpd.leases
+
+            exec ${pkgs.dhcp}/sbin/dhcpd -f -cf ${configFile} \
+                -lf ${stateDir}/dhcpd.leases \
+                ${toString cfg.interfaces}
+          '';
+      };
+
+  };
+  
+}
diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix
new file mode 100644
index 00000000000..b726493d421
--- /dev/null
+++ b/nixos/modules/services/networking/dnsmasq.nix
@@ -0,0 +1,70 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.dnsmasq;
+  dnsmasq = pkgs.dnsmasq;
+
+  serversParam = concatMapStrings (s: "-S ${s} ") cfg.servers;
+
+  dnsmasqConf = pkgs.writeText "dnsmasq.conf" ''
+    ${cfg.extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dnsmasq = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run dnsmasq.
+        '';
+      };
+
+      servers = mkOption {
+        default = [];
+        example = [ "8.8.8.8" "8.8.4.4" ];
+        description = ''
+          The parameter to dnsmasq -S.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.string;
+        default = "";
+        description = ''
+          Extra configuration directives that should be added to
+          <literal>dnsmasq.conf</literal>
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.dnsmasq.enable {
+
+    jobs.dnsmasq =
+      { description = "dnsmasq daemon";
+
+        startOn = "ip-up";
+
+        daemonType = "daemon";
+
+        exec = "${dnsmasq}/bin/dnsmasq -R ${serversParam} -o -C ${dnsmasqConf}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
new file mode 100644
index 00000000000..6d233e543e2
--- /dev/null
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -0,0 +1,135 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.ejabberd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ejabberd = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable ejabberd server";
+      };
+
+      spoolDir = mkOption {
+        default = "/var/lib/ejabberd";
+        description = "Location of the spooldir of ejabberd";
+      };
+
+      logsDir = mkOption {
+        default = "/var/log/ejabberd";
+        description = "Location of the logfile directory of ejabberd";
+      };
+
+      confDir = mkOption {
+        default = "/var/ejabberd";
+        description = "Location of the config directory of ejabberd";
+      };
+
+      virtualHosts = mkOption {
+        default = "\"localhost\"";
+        description = "Virtualhosts that ejabberd should host. Hostnames are surrounded with doublequotes and separated by commas";
+      };
+
+      loadDumps = mkOption {
+        default = [];
+        description = "Configuration dump that should be loaded on the first startup";
+        example = [ ./myejabberd.dump ];
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.ejabberd ];
+
+    jobs.ejabberd =
+      { description = "EJabberd server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        environment = {
+          PATH = "$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin";
+        };
+
+        preStart =
+          ''
+            # Initialise state data
+            mkdir -p ${cfg.logsDir}
+
+            if ! test -d ${cfg.spoolDir}
+            then
+                initialize=1
+                cp -av ${pkgs.ejabberd}/var/lib/ejabberd /var/lib
+            fi
+
+            if ! test -d ${cfg.confDir}
+            then
+                mkdir -p ${cfg.confDir}
+                cp ${pkgs.ejabberd}/etc/ejabberd/* ${cfg.confDir}
+                sed -e 's|{hosts, \["localhost"\]}.|{hosts, \[${cfg.virtualHosts}\]}.|' ${pkgs.ejabberd}/etc/ejabberd/ejabberd.cfg > ${cfg.confDir}/ejabberd.cfg
+            fi
+
+            ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start
+
+            ${if cfg.loadDumps == [] then "" else
+              ''
+                if [ "$initialize" = "1" ]
+                then
+                    # Wait until the ejabberd server is available for use
+                    count=0
+                    while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status
+                    do
+                        if [ $count -eq 30 ]
+                        then
+                            echo "Tried 30 times, giving up..."
+                            exit 1
+                        fi
+
+                        echo "Ejabberd daemon not yet started. Waiting for 1 second..."
+                        count=$((count++))
+                        sleep 1
+                    done
+
+                    ${concatMapStrings (dump:
+                      ''
+                        echo "Importing dump: ${dump}"
+
+                        if [ -f ${dump} ]
+                        then
+                            ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump}
+                        elif [ -d ${dump} ]
+                        then
+                            for i in ${dump}/ejabberd-dump/*
+                            do
+                                ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load $i
+                            done
+                        fi
+                      '') cfg.loadDumps}
+                fi
+              ''}
+          '';
+
+        postStop =
+          ''
+            ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} stop
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
new file mode 100644
index 00000000000..b24ac2d7032
--- /dev/null
+++ b/nixos/modules/services/networking/firewall.nix
@@ -0,0 +1,368 @@
+/* This module enables a simple firewall.
+
+   The firewall can be customised in arbitrary ways by setting
+   ‘networking.firewall.extraCommands’.  For modularity, the firewall
+   uses several chains:
+
+   - ‘nixos-fw-input’ is the main chain for input packet processing.
+
+   - ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for
+     refused packets.  (The former jumps to the latter after logging
+     the packet.)  If you want additional logging, or want to accept
+     certain packets anyway, you can insert rules at the start of
+     these chain.
+
+   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
+     additional logging, or want to reject certain packets anyway, you
+     can insert rules at the start of this chain.
+
+*/
+
+
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking.firewall;
+
+  helpers =
+    ''
+      # Helper command to manipulate both the IPv4 and IPv6 tables.
+      ip46tables() {
+        iptables "$@"
+        ${optionalString config.networking.enableIPv6 ''
+          ip6tables "$@"
+        ''}
+      }
+    '';
+
+  kernelPackages = config.boot.kernelPackages;
+
+  kernelHasRPFilter = kernelPackages.kernel.features.netfilterRPFilter or false;
+  kernelCanDisableHelpers = kernelPackages.kernel.features.canDisableNetfilterConntrackHelpers or false;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.firewall.enable = mkOption {
+      default = false;
+      description =
+        ''
+          Whether to enable the firewall.  This is a simple stateful
+          firewall that blocks connection attempts to unauthorised TCP
+          or UDP ports on this machine.  It does not affect packet
+          forwarding.
+        '';
+    };
+
+    networking.firewall.logRefusedConnections = mkOption {
+      default = true;
+      description =
+        ''
+          Whether to log rejected or dropped incoming connections.
+        '';
+    };
+
+    networking.firewall.logRefusedPackets = mkOption {
+      default = false;
+      description =
+        ''
+          Whether to log all rejected or dropped incoming packets.
+          This tends to give a lot of log messages, so it's mostly
+          useful for debugging.
+        '';
+    };
+
+    networking.firewall.logRefusedUnicastsOnly = mkOption {
+      default = true;
+      description =
+        ''
+          If <option>networking.firewall.logRefusedPackets</option>
+          and this option are enabled, then only log packets
+          specifically directed at this machine, i.e., not broadcasts
+          or multicasts.
+        '';
+    };
+
+    networking.firewall.rejectPackets = mkOption {
+      default = false;
+      description =
+        ''
+          If set, forbidden packets are rejected rather than dropped
+          (ignored).  This means that an ICMP "port unreachable" error
+          message is sent back to the client.  Rejecting packets makes
+          port scanning somewhat easier.
+        '';
+    };
+
+    networking.firewall.trustedInterfaces = mkOption {
+      type = types.listOf types.string;
+      description =
+        ''
+          Traffic coming in from these interfaces will be accepted
+          unconditionally.
+        '';
+    };
+
+    networking.firewall.allowedTCPPorts = mkOption {
+      default = [];
+      example = [ 22 80 ];
+      type = types.listOf types.int;
+      description =
+        ''
+          List of TCP ports on which incoming connections are
+          accepted.
+        '';
+    };
+
+    networking.firewall.allowedUDPPorts = mkOption {
+      default = [];
+      example = [ 53 ];
+      type = types.listOf types.int;
+      description =
+        ''
+          List of open UDP ports.
+        '';
+    };
+
+    networking.firewall.allowPing = mkOption {
+      default = false;
+      type = types.bool;
+      description =
+        ''
+          Whether to respond to incoming ICMPv4 echo requests
+          ("pings").  ICMPv6 pings are always allowed because the
+          larger address space of IPv6 makes network scanning much
+          less effective.
+        '';
+    };
+
+    networking.firewall.checkReversePath = mkOption {
+      default = kernelHasRPFilter;
+      type = types.bool;
+      description =
+        ''
+          Performs a reverse path filter test on a packet.
+          If a reply to the packet would not be sent via the same interface
+          that the packet arrived on, it is refused.
+
+          If using asymmetric routing or other complicated routing,
+          disable this setting and setup your own counter-measures.
+
+          (needs kernel 3.3+)
+        '';
+    };
+
+    networking.firewall.connectionTrackingModules = mkOption {
+      default = [ "ftp" ];
+      example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
+      type = types.listOf types.string;
+      description =
+        ''
+          List of connection-tracking helpers that are auto-loaded.
+          The complete list of possible values is given in the example.
+
+          As helpers can pose as a security risk, it is advised to
+          set this to an empty list and disable the setting
+          networking.firewall.autoLoadConntrackHelpers
+
+          Loading of helpers is recommended to be done through the new
+          CT target. More info:
+          https://home.regit.org/netfilter-en/secure-use-of-helpers/
+        '';
+    };
+
+    networking.firewall.autoLoadConntrackHelpers = mkOption {
+      default = true;
+      type = types.bool;
+      description =
+        ''
+          Whether to auto-load connection-tracking helpers.
+          See the description at networking.firewall.connectionTrackingModules
+
+          (needs kernel 3.5+)
+        '';
+    };
+
+    networking.firewall.extraCommands = mkOption {
+      default = "";
+      example = "iptables -A INPUT -p icmp -j ACCEPT";
+      description =
+        ''
+          Additional shell commands executed as part of the firewall
+          initialisation script.  These are executed just before the
+          final "reject" firewall rule is added, so they can be used
+          to allow packets that would otherwise be refused.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  # !!! Maybe if `enable' is false, the firewall should still be built
+  # but not started by default.  However, currently nixos-rebuild
+  # doesn't deal with such Upstart jobs properly (it starts them if
+  # they are changed, regardless of whether the start condition
+  # holds).
+  config = mkIf cfg.enable {
+
+    networking.firewall.trustedInterfaces = [ "lo" ];
+
+    environment.systemPackages = [ pkgs.iptables ];
+
+    boot.kernelModules = map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
+    boot.extraModprobeConfig = optionalString (!cfg.autoLoadConntrackHelpers) ''
+      options nf_conntrack nf_conntrack_helper=0
+    '';
+
+    assertions = [ { assertion = ! cfg.checkReversePath || kernelHasRPFilter;
+                     message = "This kernel does not support rpfilter"; }
+                   { assertion = cfg.autoLoadConntrackHelpers || kernelCanDisableHelpers;
+                     message = "This kernel does not support disabling conntrack helpers"; }
+                 ];
+
+    jobs.firewall =
+      { description = "Firewall";
+
+        startOn = "started network-interfaces";
+
+        path = [ pkgs.iptables ];
+
+        preStart =
+          ''
+            ${helpers}
+
+            # Flush the old firewall rules.  !!! Ideally, updating the
+            # firewall would be atomic.  Apparently that's possible
+            # with iptables-restore.
+            ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
+            for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse FW_REFUSE; do
+              ip46tables -F "$chain" 2> /dev/null || true
+              ip46tables -X "$chain" 2> /dev/null || true
+            done
+
+
+            # The "nixos-fw-accept" chain just accepts packets.
+            ip46tables -N nixos-fw-accept
+            ip46tables -A nixos-fw-accept -j ACCEPT
+
+
+            # The "nixos-fw-refuse" chain rejects or drops packets.
+            ip46tables -N nixos-fw-refuse
+
+            ${if cfg.rejectPackets then ''
+              # Send a reset for existing TCP connections that we've
+              # somehow forgotten about.  Send ICMP "port unreachable"
+              # for everything else.
+              ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset
+              ip46tables -A nixos-fw-refuse -j REJECT
+            '' else ''
+              ip46tables -A nixos-fw-refuse -j DROP
+            ''}
+
+
+            # The "nixos-fw-log-refuse" chain performs logging, then
+            # jumps to the "nixos-fw-refuse" chain.
+            ip46tables -N nixos-fw-log-refuse
+
+            ${optionalString cfg.logRefusedConnections ''
+              ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "rejected connection: "
+            ''}
+            ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
+              ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
+                -j LOG --log-level info --log-prefix "rejected broadcast: "
+              ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
+                -j LOG --log-level info --log-prefix "rejected multicast: "
+            ''}
+            ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
+            ${optionalString cfg.logRefusedPackets ''
+              ip46tables -A nixos-fw-log-refuse \
+                -j LOG --log-level info --log-prefix "rejected packet: "
+            ''}
+            ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
+
+
+            # The "nixos-fw" chain does the actual work.
+            ip46tables -N nixos-fw
+
+            # Perform a reverse-path test to refuse spoofers
+            # For now, we just drop, as the raw table doesn't have a log-refuse yet
+            ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
+              if ! ip46tables -A PREROUTING -t raw -m rpfilter --invert -j DROP; then
+                echo "<2>failed to initialise rpfilter support" >&2
+              fi
+            ''}
+
+            # Accept all traffic on the trusted interfaces.
+            ${flip concatMapStrings cfg.trustedInterfaces (iface: ''
+              ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept
+            '')}
+
+            # Accept packets from established or related connections.
+            ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
+
+            # Accept connections to the allowed TCP ports.
+            ${concatMapStrings (port:
+                ''
+                  ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept
+                ''
+              ) cfg.allowedTCPPorts
+            }
+
+            # Accept packets on the allowed UDP ports.
+            ${concatMapStrings (port:
+                ''
+                  ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept
+                ''
+              ) cfg.allowedUDPPorts
+            }
+
+            # 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
+            ''}
+
+            # Accept all ICMPv6 messages except redirects and node
+            # information queries (type 139).  See RFC 4890, section
+            # 4.4.
+            ${optionalString config.networking.enableIPv6 ''
+              ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
+              ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
+              ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
+            ''}
+
+            ${cfg.extraCommands}
+
+            # Reject/drop everything else.
+            ip46tables -A nixos-fw -j nixos-fw-log-refuse
+
+
+            # Enable the firewall.
+            ip46tables -A INPUT -j nixos-fw
+          '';
+
+        postStop =
+          ''
+            ${helpers}
+            ip46tables -D INPUT -j nixos-fw || true
+            #ip46tables -P INPUT ACCEPT
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix
new file mode 100644
index 00000000000..f5bc550ab5f
--- /dev/null
+++ b/nixos/modules/services/networking/flashpolicyd.nix
@@ -0,0 +1,84 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.flashpolicyd;
+
+  flashpolicyd = pkgs.stdenv.mkDerivation {
+    name = "flashpolicyd-0.6";
+
+    src = pkgs.fetchurl {
+      name = "flashpolicyd_v0.6.zip";
+      url = "http://www.adobe.com/content/dotcom/en/devnet/flashplayer/articles/socket_policy_files/_jcr_content/articlePrerequistes/multiplefiles/node_1277808777771/file.res/flashpolicyd_v0.6%5B1%5D.zip";
+      sha256 = "16zk237233npwfq1m4ksy4g5lzy1z9fp95w7pz0cdlpmv0fv9sm3";
+    };
+
+    buildInputs = [ pkgs.unzip pkgs.perl ];
+
+    installPhase = "mkdir $out; cp -pr * $out/; chmod +x $out/*/*.pl";
+  };
+
+  flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd"
+    ''
+      #! ${pkgs.stdenv.shell}
+      exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \
+        --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \
+        2> /dev/null
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+  
+    services.flashpolicyd = {
+    
+      enable = mkOption {
+        default = false;
+        description =
+          ''
+            Whether to enable the Flash Policy server.  This is
+            necessary if you want Flash applications to make
+            connections to your server.
+          '';
+      };
+      
+      policy = mkOption {
+        default =
+          ''
+            <?xml version="1.0"?>
+            <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
+            <cross-domain-policy> 
+              <site-control permitted-cross-domain-policies="master-only"/>
+              <allow-access-from domain="*" to-ports="*" />
+            </cross-domain-policy>
+          '';
+        description = "The policy to be served.  The default is to allow connections from any domain to any port.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xinetd.enable = true;
+
+    services.xinetd.services = singleton
+      { name = "flashpolicy";
+        port = 843;
+        unlisted = true;
+        server = "${flashpolicydWrapper}/bin/flashpolicyd";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/freenet.nix b/nixos/modules/services/networking/freenet.nix
new file mode 100644
index 00000000000..a4bd2098986
--- /dev/null
+++ b/nixos/modules/services/networking/freenet.nix
@@ -0,0 +1,64 @@
+# NixOS module for Freenet daemon
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.freenet;
+  varDir = "/var/lib/freenet";
+
+in
+
+{
+
+  ### configuration
+
+  options = {
+
+    services.freenet = {
+
+      enable = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = "Enable the Freenet daemon";
+      };
+
+      nice = mkOption {
+        type = types.uniq types.int;
+        default = 10;
+        description = "Set the nice level for the Freenet daemon";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.services.freenet = {
+      description = "Freenet daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.freenet}/bin/freenet";
+      serviceConfig.User = "freenet";
+      serviceConfig.UMask = "0007";
+      serviceConfig.WorkingDirectory = varDir;
+      serviceConfig.Nice = cfg.nice;
+    };
+
+    users.extraUsers.freenet = {
+      group = "freenet";
+      description = "Freenet daemon user";
+      home = varDir;
+      createHome = true;
+      uid = config.ids.uids.freenet;
+    };
+
+    users.extraGroups.freenet.gid = config.ids.gids.freenet;
+  };
+
+}
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
new file mode 100644
index 00000000000..a7c7c206198
--- /dev/null
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -0,0 +1,112 @@
+{pkgs, config, ...}:
+with pkgs.lib;
+let
+
+  cfg = config.services.gitDaemon;
+  gitUser = "git";
+
+in
+{
+
+  ###### interface
+
+  options = {
+    services.gitDaemon = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable Git daemon, which allows public hosting  of git repositories
+          without any access controls. This is mostly intended for read-only access.
+
+          You can allow write access by setting daemon.receivepack configuration
+          item of the repository to true. This is solely meant for a closed LAN setting
+          where everybody is friendly.
+
+          If you need any access controls, use something else.
+        '';
+      };
+
+      basePath = mkOption {
+        default = "";
+        example = "/srv/git/";
+        description = ''
+          Remap all the path requests as relative to the given path. For example,
+          if you set base-path to /srv/git, then if you later try to pull
+          git://example.com/hello.git, Git daemon will interpret the path as /srv/git/hello.git.
+        '';
+      };
+
+      exportAll = mkOption {
+        default = false;
+        description = ''
+          Publish all directories that look like Git repositories (have the objects
+          and refs subdirectories), even if they do not have the git-daemon-export-ok file.
+
+          If disabled, you need to touch .git/git-daemon-export-ok in each repository
+          you want the daemon to publish.
+
+          Warning: enabling this without a repository whitelist or basePath
+          publishes every git repository you have.
+        '';
+      };
+
+      repositories = mkOption {
+        default = [];
+        example = [ "/srv/git" "/home/user/git/repo2" ];
+        description = ''
+          A whitelist of paths of git repositories, or directories containing repositories
+          all of which would be published. Paths must not end in "/".
+
+          Warning: leaving this empty and enabling exportAll publishes all
+          repositories in your filesystem or basePath if specified.
+        '';
+      };
+
+      listenAddress = mkOption {
+        default = "";
+        example = "example.com";
+        description = "Listen on a specific IP address or hostname.";
+      };
+
+      port = mkOption {
+        default = 9418;
+        description = "Port to listen on.";
+      };
+
+      options = mkOption {
+        default = "";
+        description = "Extra configuration options to be passed to Git daemon.";
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = gitUser;
+        uid = config.ids.uids.git;
+        description = "Git daemon user";
+      };
+
+    users.extraGroups = singleton
+      { name = gitUser;
+        gid = config.ids.gids.git;
+      };
+
+    jobs.gitDaemon = {
+      name = "git-daemon";
+      startOn = "ip-up";
+      exec = "${pkgs.git}/bin/git daemon --reuseaddr "
+        + (optionalString (cfg.basePath != "") "--basepath=${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
new file mode 100644
index 00000000000..421c0d9bb69
--- /dev/null
+++ b/nixos/modules/services/networking/gnunet.nix
@@ -0,0 +1,148 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.gnunet;
+
+  homeDir = "/var/lib/gnunet";
+
+  configFile = with cfg; pkgs.writeText "gnunetd.conf"
+    ''
+      [PATHS]
+      SERVICEHOME = ${homeDir}
+
+      [ats]
+      WAN_QUOTA_IN = ${toString load.maxNetDownBandwidth} b
+      WAN_QUOTA_OUT = ${toString load.maxNetUpBandwidth} b
+
+      [datastore]
+      QUOTA = ${toString fileSharing.quota} MB
+
+      [transport-udp]
+      PORT = ${toString udp.port}
+      ADVERTISED_PORT = ${toString udp.port}
+
+      [transport-tcp]
+      PORT = ${toString tcp.port}
+      ADVERTISED_PORT = ${toString tcp.port}
+
+      ${extraOptions}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gnunet = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the GNUnet daemon.  GNUnet is GNU's anonymous
+          peer-to-peer communication and file sharing framework.
+        '';
+      };
+
+      fileSharing = {
+        quota = mkOption {
+          default = 1024;
+          description = ''
+            Maximum file system usage (in MiB) for file sharing.
+          '';
+        };
+      };
+
+      udp = {
+        port = mkOption {
+          default = 2086;  # assigned by IANA
+          description = ''
+            The UDP port for use by GNUnet.
+          '';
+        };
+      };
+
+      tcp = {
+        port = mkOption {
+          default = 2086;  # assigned by IANA
+          description = ''
+            The TCP port for use by GNUnet.
+          '';
+        };
+      };
+
+      load = {
+        maxNetDownBandwidth = mkOption {
+          default = 50000;
+          description = ''
+            Maximum bandwidth usage (in bits per second) for GNUnet
+            when downloading data.
+          '';
+        };
+
+        maxNetUpBandwidth = mkOption {
+          default = 50000;
+          description = ''
+            Maximum bandwidth usage (in bits per second) for GNUnet
+            when downloading data.
+          '';
+        };
+
+        hardNetUpBandwidth = mkOption {
+          default = 0;
+          description = ''
+            Hard bandwidth limit (in bits per second) when uploading
+            data.
+          '';
+        };
+      };
+
+      extraOptions = mkOption {
+        default = "";
+        description = ''
+          Additional options that will be copied verbatim in `gnunet.conf'.
+          See `gnunet.conf(5)' for details.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.gnunet.enable {
+
+    users.extraUsers.gnunet = {
+      group = "gnunet";
+      description = "GNUnet User";
+      home = homeDir;
+      createHome = true; 
+      uid = config.ids.uids.gnunet;
+    };
+
+    users.extraGroups.gnunet.gid = config.ids.gids.gnunet;
+
+    # The user tools that talk to `gnunetd' should come from the same source,
+    # so install them globally.
+    environment.systemPackages = [ pkgs.gnunet ];
+
+    systemd.services.gnunet = {
+      description = "GNUnet";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.gnunet pkgs.miniupnpc ];
+      serviceConfig.ExecStart = "${pkgs.gnunet}/lib/gnunet/libexec/gnunet-service-arm -c ${configFile}";
+      serviceConfig.User = "gnunet";
+      serviceConfig.UMask = "0007";
+      serviceConfig.WorkingDirectory = homeDir;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/gogoclient.nix b/nixos/modules/services/networking/gogoclient.nix
new file mode 100644
index 00000000000..07c35e3cb3d
--- /dev/null
+++ b/nixos/modules/services/networking/gogoclient.nix
@@ -0,0 +1,88 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let cfg = config.services.gogoclient;
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.gogoclient = {
+      enable = mkOption {
+        default = false;
+        type =  types.bool;
+        description = ''
+          Enable the gogoclient ipv6 tunnel.
+        '';
+      };
+      autorun = mkOption {
+        default = true;
+        description = "
+          Switch to false to create upstart-job and configuration,
+          but not run it automatically
+        ";
+      };
+
+      username = mkOption {
+        default = "";
+        description = "
+          Your Gateway6 login name, if any.
+        ";
+      };
+
+      password = mkOption {
+        default = "";
+        type = types.string;
+        description = "
+          Path to a file (as a string), containing your gogonet password, if any.
+        ";
+      };
+
+      server = mkOption {
+        default = "anonymous.freenet6.net";
+        example = "broker.freenet6.net";
+        description = "
+          Used Gateway6 server.
+        ";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "tun" ];
+
+    networking.enableIPv6 = true;
+
+    systemd.services.gogoclient = {
+      description = "ipv6 tunnel";
+
+      after = [ "network.target" ];
+      requires = [ "network.target" ];
+
+      unitConfig.RequiresMountsFor = "/var/lib/gogoc";
+
+      script = let authMethod = if cfg.password == "" then "anonymous" else "any"; in ''
+        mkdir -p -m 700 /var/lib/gogoc
+        cat ${pkgs.gogoclient}/share/${pkgs.gogoclient.name}/gogoc.conf.sample | \
+          ${pkgs.gnused}/bin/sed \
+            -e "s|^userid=|&${cfg.username}|" \
+            -e "s|^passwd=|&${optionalString (cfg.password != "") "$(cat ${cfg.password})"}|" \
+            -e "s|^server=.*|server=${cfg.server}|" \
+            -e "s|^auth_method=.*|auth_method=${authMethod}|" \
+            -e "s|^#log_file=|log_file=1|" > /var/lib/gogoc/gogoc.conf
+        cd /var/lib/gogoc
+        exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf
+      '';
+    } // optionalAttrs cfg.autorun {
+      wantedBy = [ "ip-up.target" ];
+      partOf = [ "ip-up.target" ];
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/gvpe.nix b/nixos/modules/services/networking/gvpe.nix
new file mode 100644
index 00000000000..594a2e80f34
--- /dev/null
+++ b/nixos/modules/services/networking/gvpe.nix
@@ -0,0 +1,144 @@
+# GNU Virtual Private Ethernet
+
+{config, pkgs, ...}:
+
+let
+  inherit (pkgs.lib) mkOption mkIf;
+
+  cfg = config.services.gvpe;
+
+  finalConfig = if cfg.configFile != null then
+    cfg.configFile
+  else if cfg.configText != null then
+    pkgs.writeTextFile {
+      name = "gvpe.conf";
+      text = cfg.configText;
+    }
+  else
+    throw "You must either specify contents of the config file or the config file itself for GVPE";
+
+  ifupScript = if cfg.ipAddress == null || cfg.subnet == null then
+     throw "Specify IP address and subnet (with mask) for GVPE"
+   else if cfg.nodename == null then
+     throw "You must set node name for GVPE"
+   else
+   (pkgs.writeTextFile {
+    name = "gvpe-if-up";
+    text = ''
+      #! /bin/sh
+
+      export PATH=$PATH:${pkgs.iproute}/sbin
+
+      ip link set $IFNAME up
+      ip address add ${cfg.ipAddress} dev $IFNAME
+      ip route add ${cfg.subnet} dev $IFNAME
+
+      ${cfg.customIFSetup}
+    '';
+    executable = true;
+  });
+
+  exec = "${pkgs.gvpe}/sbin/gvpe -c /var/gvpe -D ${cfg.nodename} "
+    + " ${cfg.nodename}.pid-file=/var/gvpe/gvpe.pid"
+    + " ${cfg.nodename}.if-up=if-up"
+    + " &> /var/log/gvpe";
+
+  inherit (cfg) startOn stopOn;
+in
+
+{
+  options = {
+    services.gvpe = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run gvpe
+        '';
+      };
+      startOn = mkOption {
+        default = "started network-interfaces";
+        description = ''
+          Condition to start GVPE
+        '';
+      };
+      stopOn = mkOption {
+        default = "stopping network-interfaces";
+        description = ''
+          Condition to stop GVPE
+        '';
+      };
+      nodename = mkOption {
+        default = null;
+        description =''
+          GVPE node name
+        '';
+      };
+      configText = mkOption {
+        default = null;
+        example = ''
+          tcp-port = 655
+          udp-port = 655
+          mtu = 1480
+          ifname = vpn0
+
+          node = alpha
+          hostname = alpha.example.org
+          connect = always
+          enable-udp = true
+          enable-tcp = true
+          on alpha if-up = if-up-0
+          on alpha pid-file = /var/gvpe/gvpe.pid
+        '';
+        description = ''
+          GVPE config contents
+        '';
+      };
+      configFile = mkOption {
+        default = null;
+        example = "/root/my-gvpe-conf";
+        description = ''
+          GVPE config file, if already present
+        '';
+      };
+      ipAddress = mkOption {
+        default = null;
+        description = ''
+          IP address to assign to GVPE interface
+        '';
+      };
+      subnet = mkOption {
+        default = null;
+        example = "10.0.0.0/8";
+        description = ''
+          IP subnet assigned to GVPE network
+        '';
+      };
+      customIFSetup = mkOption {
+        default = "";
+        description = ''
+          Additional commands to apply in ifup script
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    jobs.gvpe = {
+      description = "GNU Virtual Private Ethernet node";
+
+      inherit startOn stopOn;
+
+      preStart = ''
+        mkdir -p /var/gvpe
+        mkdir -p /var/gvpe/pubkey
+        chown root /var/gvpe
+        chmod 700 /var/gvpe
+        cp ${finalConfig} /var/gvpe/gvpe.conf
+        cp ${ifupScript} /var/gvpe/if-up
+      '';
+
+      inherit exec;
+
+      respawn = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix
new file mode 100644
index 00000000000..4edea12b6be
--- /dev/null
+++ b/nixos/modules/services/networking/hostapd.nix
@@ -0,0 +1,163 @@
+{ config, pkgs, ... }:
+
+# TODO:
+#
+# asserts 
+#   ensure that the nl80211 module is loaded/compiled in the kernel
+#   hwMode must be a/b/g
+#   channel must be between 1 and 13 (maybe)
+#   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;
+
+let
+
+  cfg = config.services.hostapd;
+  
+  configFile = pkgs.writeText "hostapd.conf"  
+    ''
+    interface=${cfg.interface}
+    driver=${cfg.driver}
+    ssid=${cfg.ssid}
+    hw_mode=${cfg.hwMode}
+    channel=${toString cfg.channel}
+
+    # logging (debug level)
+    logger_syslog=-1
+    logger_syslog_level=2
+    logger_stdout=-1
+    logger_stdout_level=2
+
+    ctrl_interface=/var/run/hostapd
+    ctrl_interface_group=${cfg.group}
+
+    ${if cfg.wpa then ''
+      wpa=1
+      wpa_passphrase=${cfg.wpaPassphrase}
+      '' else ""}
+
+    ${cfg.extraCfg}
+    '' ;
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    services.hostapd = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable putting a wireless interface into infrastructure mode,
+          allowing other wireless devices to associate with the wireless interface and do
+          wireless networking. A simple access point will enable hostapd.wpa, and
+          hostapd.wpa_passphrase, hostapd.ssid, dhcpd on the wireless interface to
+          provide IP addresses to the associated stations, and nat (from the wireless
+          interface to an upstream interface). 
+        '';
+      };
+
+      interface = mkOption {
+        default = "";
+        example = "wlan0";
+        description = ''
+          The interfaces <command>hostapd</command> will use. 
+        '';
+      };
+
+      driver = mkOption {
+        default = "nl80211";
+        example = "hostapd";
+        type = types.string;
+        description = "Which driver hostapd will use. Most things will probably use the default.";
+      };
+
+      ssid = mkOption {
+        default = "nixos";
+        example = "mySpecialSSID";
+        type = types.string;
+        description = "SSID to be used in IEEE 802.11 management frames.";
+      };
+
+      hwMode = mkOption {
+        default = "b";
+        example = "g";
+        type = types.string;
+        description = "Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g";
+      };
+
+      channel = mkOption { 
+        default = 7;
+        example = 11;
+        type = types.int;
+        description = 
+          ''
+          Channel number (IEEE 802.11)
+          Please note that some drivers do not use this value from hostapd and the
+          channel will need to be configured separately with iwconfig.
+          '';
+      };
+
+      group = mkOption {
+        default = "wheel";
+        example = "network";
+        type = types.string;
+        description = "members of this group can control hostapd";
+      };
+
+      wpa = mkOption {
+        default = true;
+        description = "enable WPA (IEEE 802.11i/D3.0) to authenticate to the access point";
+      };
+
+      wpaPassphrase = mkOption {
+        default = "my_sekret";
+        example = "any_64_char_string";
+        type = types.string;
+        description = 
+          ''
+          WPA-PSK (pre-shared-key) passphrase. Clients will need this
+          passphrase to associate with this access point. Warning: This passphrase will
+          get put into a world-readable file in the nix store. 
+          '';
+      };
+
+      extraCfg = mkOption {
+        default = "";
+        example = ''
+          auth_algo=0
+          ieee80211n=1
+          ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
+          '';
+        type = types.string;
+        description = "Extra configuration options to put in the hostapd.conf";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages =  [ pkgs.hostapd ];
+
+    systemd.services.hostapd =
+      { description = "hostapd wireless AP";
+
+        path = [ pkgs.hostapd ]; 
+        wantedBy = [ "network.target" ];
+
+        after = [ "${cfg.interface}-cfg.service" "nat.service" "bind.service" "dhcpd.service"];
+
+        serviceConfig = 
+          { ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}";
+            Restart = "always";
+          };
+      };
+  };
+}
diff --git a/nixos/modules/services/networking/ifplugd.nix b/nixos/modules/services/networking/ifplugd.nix
new file mode 100644
index 00000000000..df50e9807a9
--- /dev/null
+++ b/nixos/modules/services/networking/ifplugd.nix
@@ -0,0 +1,88 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) ifplugd;
+
+  cfg = config.networking.interfaceMonitor;
+
+  # The ifplugd action script, which is called whenever the link
+  # status changes (i.e., a cable is plugged in or unplugged).  We do
+  # nothing when a cable is unplugged.  When a cable is plugged in, we
+  # restart dhclient, which will hopefully give us a new IP address
+  # if appropriate.
+  plugScript = pkgs.writeScript "ifplugd.action"
+    ''
+      #! ${pkgs.stdenv.shell}
+      iface="$1"
+      status="$2"
+      ${cfg.commands}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.interfaceMonitor.enable = mkOption {
+      default = false;
+      description = ''
+        If <literal>true</literal>, monitor Ethernet interfaces for
+        cables being plugged in or unplugged.  When this occurs, the
+        <command>dhclient</command> service is restarted to
+        automatically obtain a new IP address.  This is useful for
+        roaming users (laptops).
+      '';
+    };
+
+    networking.interfaceMonitor.beep = mkOption {
+      default = false;
+      description = ''
+        If <literal>true</literal>, beep when an Ethernet cable is
+        plugged in or unplugged.
+      '';
+    };
+
+    networking.interfaceMonitor.commands = mkOption {
+      default = "";
+      description = ''
+        Shell commands to be executed when the link status of an
+        interface changes.  On invocation, the shell variable
+        <varname>iface</varname> contains the name of the interface,
+        while the variable <varname>status</varname> contains either
+        <literal>up</literal> or <literal>down</literal> to indicate
+        the new status.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    jobs.ifplugd =
+      { description = "Network interface connectivity monitor";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        exec =
+          ''
+            ${ifplugd}/sbin/ifplugd --no-daemon --no-startup --no-shutdown \
+              ${if config.networking.interfaceMonitor.beep then "" else "--no-beep"} \
+              --run ${plugScript}
+          '';
+      };
+
+    environment.systemPackages = [ ifplugd ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/iodined.nix b/nixos/modules/services/networking/iodined.nix
new file mode 100644
index 00000000000..1b3473ee0ee
--- /dev/null
+++ b/nixos/modules/services/networking/iodined.nix
@@ -0,0 +1,87 @@
+# NixOS module for iodine, ip over dns daemon
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.iodined;
+
+  iodinedUser = "iodined";
+
+in
+
+{
+
+  ### configuration
+
+  options = {
+
+    services.iodined = {
+
+      enable = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = "Enable iodine, ip over dns daemon";
+      };
+
+      client = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = "Start iodine in client mode";
+      };
+
+      ip = mkOption {
+        type = types.uniq types.string;
+        default = "";
+        description = "Assigned ip address or ip range";
+        example = "172.16.10.1/24";
+      };
+
+      domain = mkOption {
+        type = types.uniq types.string;
+        default = "";
+        description = "Domain or subdomain of which nameservers point to us";
+        example = "tunnel.mydomain.com";
+      };
+
+      extraConfig = mkOption {
+        type = types.uniq types.string;
+        default = "";
+        description = "Additional command line parameters";
+        example = "-P mysecurepassword -l 192.168.1.10 -p 23";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.iodine ];
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services.iodined = {
+      description = "iodine, ip over dns daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig.ExecStart = "${pkgs.iodine}/sbin/iodined -f -u ${iodinedUser} ${cfg.extraConfig} ${cfg.ip} ${cfg.domain}";
+    };
+
+
+    users.extraUsers = singleton {
+      name = iodinedUser;
+      uid = config.ids.uids.iodined;
+      description = "Iodine daemon user";
+    };
+    users.extraGroups.iodined.gid = config.ids.gids.iodined;
+
+    assertions = [{ assertion = if !cfg.client then cfg.ip != "" else true;
+                    message = "cannot start iodined without ip set";}
+                  { assertion = cfg.domain != "";
+                    message = "cannot start iodined without domain name set";}];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixos/modules/services/networking/ircd-hybrid/builder.sh
new file mode 100644
index 00000000000..b8cb836db95
--- /dev/null
+++ b/nixos/modules/services/networking/ircd-hybrid/builder.sh
@@ -0,0 +1,31 @@
+source $stdenv/setup
+
+doSub() {
+    local src=$1
+    local dst=$2
+    ensureDir $(dirname $dst)
+    substituteAll $src $dst
+}
+
+subDir=/
+for i in $scripts; do
+    if test "$(echo $i | cut -c1-2)" = "=>"; then
+        subDir=$(echo $i | cut -c3-)
+    else
+        dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//')
+        doSub $i $dst
+        chmod +x $dst # !!!
+    fi
+done
+
+subDir=/
+for i in $substFiles; do
+    if test "$(echo $i | cut -c1-2)" = "=>"; then
+        subDir=$(echo $i | cut -c3-)
+    else
+        dst=$out/$subDir/$((stripHash $i; echo $strippedName) | sed 's/\.in//')
+        doSub $i $dst
+    fi
+done
+
+ensureDir $out/bin
diff --git a/nixos/modules/services/networking/ircd-hybrid/control.in b/nixos/modules/services/networking/ircd-hybrid/control.in
new file mode 100644
index 00000000000..312dfaada32
--- /dev/null
+++ b/nixos/modules/services/networking/ircd-hybrid/control.in
@@ -0,0 +1,26 @@
+#! @shell@ -e
+
+# Make sure that the environment is deterministic.
+export PATH=@coreutils@/bin
+
+if test "$1" = "start"; then
+	if ! @procps@/bin/pgrep ircd; then
+	if @ipv6Enabled@; then 
+		while ! @iproute@/sbin/ip addr | 
+			@gnugrep@/bin/grep inet6 | 
+			@gnugrep@/bin/grep global; do
+			sleep 1;
+		done;
+	fi;
+	rm -rf /home/ircd
+	mkdir -p /home/ircd
+	chown ircd: /home/ircd
+	cd /home/ircd
+    env - HOME=/homeless-shelter $extraEnv \
+        @su@/bin/su ircd --shell=/bin/sh -c ' @ircdHybrid@/bin/ircd -configfile @out@/conf/ircd.conf </dev/null -logfile /home/ircd/ircd.log' 2>&1 >/var/log/ircd-hybrid.out
+	fi;
+fi
+
+if test "$1" = "stop" ; then 
+	@procps@/bin/pkill ircd;
+fi;
diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix
new file mode 100644
index 00000000000..cd82a41ef7a
--- /dev/null
+++ b/nixos/modules/services/networking/ircd-hybrid/default.nix
@@ -0,0 +1,137 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.ircdHybrid;
+
+  ircdService = pkgs.stdenv.mkDerivation rec {
+    name = "ircd-hybrid-service";
+    scripts = [ "=>/bin" ./control.in ];
+    substFiles = [ "=>/conf" ./ircd.conf ];
+    inherit (pkgs) ircdHybrid coreutils su iproute gnugrep procps;
+
+    ipv6Enabled = if config.networking.enableIPv6 then "true" else "false";
+
+    inherit (cfg) serverName sid description adminEmail
+            extraPort;
+
+    cryptoSettings =
+      (optionalString (cfg.rsaKey != null) "rsa_private_key_file = \"${cfg.rsaKey}\";\n") +
+      (optionalString (cfg.certificate != null) "ssl_certificate_file = \"${cfg.certificate}\";\n");
+
+    extraListen = map (ip: "host = \""+ip+"\";\nport = 6665 .. 6669, "+extraPort+"; ") cfg.extraIPs;
+
+    builder = ./builder.sh;
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ircdHybrid = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Enable IRCD.
+        ";
+      };
+
+      serverName = mkOption {
+        default = "hades.arpa";
+        description = "
+          IRCD server name.
+        ";
+      };
+
+      sid = mkOption {
+        default = "0NL";
+        description = "
+          IRCD server unique ID in a net of servers.
+        ";
+      };
+
+      description = mkOption {
+        default = "Hybrid-7 IRC server.";
+        description = "
+          IRCD server description.
+        ";
+      };
+
+      rsaKey = mkOption {
+        default = null;
+        example = /root/certificates/irc.key;
+        description = "
+          IRCD server RSA key.
+        ";
+      };
+
+      certificate = mkOption {
+        default = null;
+        example = /root/certificates/irc.pem;
+        description = "
+          IRCD server SSL certificate. There are some limitations - read manual.
+        ";
+      };
+
+      adminEmail = mkOption {
+        default = "<bit-bucket@example.com>";
+        example = "<name@domain.tld>";
+        description = "
+          IRCD server administrator e-mail.
+        ";
+      };
+
+      extraIPs = mkOption {
+        default = [];
+        example = ["127.0.0.1"];
+        description = "
+          Extra IP's to bind.
+        ";
+      };
+
+      extraPort = mkOption {
+        default = "7117";
+        description = "
+          Extra port to avoid filtering.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ircdHybrid.enable {
+
+    users.extraUsers = singleton
+      { name = "ircd";
+        description = "IRCD owner";
+        group = "ircd";
+        uid = config.ids.uids.ircd;
+      };
+
+    users.extraGroups.ircd.gid = config.ids.gids.ircd;
+
+    jobs.ircd_hybrid =
+      { name = "ircd-hybrid";
+
+        description = "IRCD Hybrid server";
+
+        startOn = "started networking";
+        stopOn = "stopping networking";
+
+        exec = "${ircdService}/bin/control start";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/ircd-hybrid/ircd.conf b/nixos/modules/services/networking/ircd-hybrid/ircd.conf
new file mode 100644
index 00000000000..bb22832dbdb
--- /dev/null
+++ b/nixos/modules/services/networking/ircd-hybrid/ircd.conf
@@ -0,0 +1,1051 @@
+/* doc/example.conf - ircd-hybrid-7 Example configuration file
+ * Copyright (C) 2000-2006 Hybrid Development Team
+ *
+ * Written by ejb, wcampbel, db, leeh and others
+ * Other example configurations can be found in the source dir under
+ * etc/.
+ *
+ * $Id: example.conf 639 2006-06-01 14:12:21Z michael $
+ */
+
+/* IMPORTANT NOTES:
+ *
+ * auth {} blocks MUST be specified in order of precedence.  The first one
+ * that matches a user will be used.  So place spoofs first, then specials,
+ * then general access.
+ *
+ * Shell style (#), C++ style (//) and C style comments are supported.
+ *
+ * Files may be included by either:
+ *        .include "filename"
+ *        .include <filename>
+ *
+ * Times/durations are written as:
+ *        12 hours 30 minutes 1 second
+ *        
+ * Valid units of time:
+ *        month, week, day, hour, minute, second
+ *
+ * Valid units of size:
+ *        megabyte/mbyte/mb, kilobyte/kbyte/kb, byte
+ *
+ * Sizes and times may be singular or plural.  
+ */ 
+
+/* EFNET NOTE:
+ *
+ * This config file is NOT suitable for EFNet.  EFNet admins should use
+ * example.efnet.conf
+ */
+ 
+/*
+ * serverinfo {}:  contains information about the server. (OLD M:)
+ */
+serverinfo {
+	/*
+	 * name: the name of our server.  This cannot be changed at runtime.
+	 */
+	name = "@serverName@";
+
+	/*
+	 * sid: a server's unique ID.  This is three characters long and must
+	 * be in the form [0-9][A-Z0-9][A-Z0-9].  The first character must be
+	 * a digit, followed by 2 alpha-numerical letters.
+	 * NOTE: The letters must be capitalized.  This cannot be changed at runtime.
+	 */
+	sid = "@sid@";
+
+	/*
+	 * description: the description of the server.  '[' and ']' may not
+	 * be used here for compatibility with older servers.
+	 */
+	description = "@description@";
+
+	/*
+	 * network info: the name and description of the network this server
+	 * is on.  Shown in the 005 reply and used with serverhiding.
+	 */
+	network_name = "JustIRCNetwork";
+	network_desc = "This is My Network";
+
+	/*
+	 * hub: allow this server to act as a hub and have multiple servers
+	 * connected to it.  This may not be changed if there are active
+	 * LazyLink servers.
+	 */
+	hub = no;
+
+	/*
+	 * vhost: the IP to bind to when we connect outward to ipv4 servers.
+	 * This should be an ipv4 IP only, or "* for INADDR_ANY.
+	 */
+	#vhost = "192.169.0.1";
+
+	/*
+	 * vhost6: the IP to bind to when we connect outward to ipv6 servers.
+	 * This should be an ipv6 IP only, or "* for INADDR_ANY.
+	 */
+	#vhost6 = "3ffe:80e8:546::2";
+
+	/* max_clients: the maximum number of clients allowed to connect */
+	max_clients = 512;
+
+	/*
+	 * rsa key: the path to the file containing our rsa key for cryptlink.
+	 *
+	 * Example command to store a 2048 bit RSA keypair in
+	 * rsa.key, and the public key in rsa.pub:
+	 * 
+	 * 	openssl genrsa -out rsa.key 2048
+	 *	openssl rsa -in rsa.key -pubout -out rsa.pub
+	 *	chown <ircd-user>.<ircd.group> rsa.key rsa.pub
+	 *	chmod 0600 rsa.key
+	 *	chmod 0644 rsa.pub
+	 */
+	#rsa_private_key_file = "/usr/local/ircd/etc/rsa.key";
+
+	/*
+	 * ssl certificate: the path to the file containing our ssl certificate
+	 * for encrypted client connection.
+	 *
+	 * This assumes your private RSA key is stored in rsa.key. You
+	 * MUST have an RSA key in order to generate the certificate
+	 *
+	 *	openssl req -new -days 365 -x509 -key rsa.key -out cert.pem
+	 *
+	 * See http://www.openssl.org/docs/HOWTO/certificates.txt
+	 *
+	 * Please use the following values when generating the cert
+	 *
+	 *	Organization Name: Network Name
+	 *	Organization Unit Name: changme.someirc.net
+	 *	Common Name: irc.someirc.net
+	 *	E-mail: you@domain.com
+	 */
+	#ssl_certificate_file = "/usr/local/ircd/etc/cert.pem";
+
+	@cryptoSettings@
+};
+
+/*
+ * admin {}:  contains admin information about the server. (OLD A:)
+ */
+admin {
+	name = "Anonymous Hero";
+	description = "Main Server Administrator";
+	email = "@adminEmail@";
+};
+
+/*
+ * log {}:  contains information about logfiles.
+ */
+log {
+	/* Do you want to enable logging to ircd.log? */
+	use_logging = yes;
+
+	/*
+	 * logfiles: the logfiles to use for user connects, /oper uses,
+	 * and failed /oper.  These files must exist for logging to be used.
+	 */
+	fname_userlog = "/home/ircd/logs/userlog";
+	fname_operlog = "/home/ircd/logs/operlog";
+	fname_killlog = "/home/ircd/logs/kill";
+	fname_klinelog = "/home/ircd/logs/kline";
+	fname_glinelog = "/home/ircd/logs/gline";
+
+	/*
+	 * log_level: the amount of detail to log in ircd.log.  The
+	 * higher, the more information is logged.  May be changed
+	 * once the server is running via /quote SET LOG.  Either:
+	 * L_CRIT, L_ERROR, L_WARN, L_NOTICE, L_TRACE, L_INFO or L_DEBUG
+	 */
+	log_level = L_INFO;
+};
+
+/*
+ * class {}:  contains information about classes for users (OLD Y:)
+ */
+class {
+	/* name: the name of the class.  classes are text now */
+	name = "users";
+
+	/*
+	 * ping_time: how often a client must reply to a PING from the
+	 * server before they are dropped.
+	 */
+	ping_time = 90 seconds;
+
+	/*
+	 * number_per_ip: how many local users are allowed to connect
+	 * from one IP  (optional)
+	 */
+	number_per_ip = 10;
+
+	/*
+	 * max_local: how many local users are allowed to connect
+	 * from one ident@host  (optional)
+	 */
+	max_local = 50;
+
+	/*
+	 * max_global: network-wide limit of users per ident@host  (optional)
+	 */
+	max_global = 50;
+
+	/*
+	 * max_number: the maximum number of users allowed in this class (optional)
+	 */
+	max_number = 10000;
+
+	/*
+	 * the following lines are optional and allow you to define
+	 * how many users can connect from one /NN subnet
+	 */
+	/*cidr_bitlen_ipv4 = 24;
+	 *cidr_bitlen_ipv6 = 120;
+	 *number_per_cidr = 16;*/
+
+	/*
+	 * sendq: the amount of data allowed in a clients queue before
+	 * they are dropped.
+	 */
+	sendq = 100 kbytes;
+};
+
+class {
+	name = "opers";
+	ping_time = 90 seconds;
+	number_per_ip = 10;
+	max_number = 100;
+	sendq = 100kbytes;
+};
+
+class {
+	name = "server";
+	ping_time = 90 seconds;
+
+	/*
+	 * ping_warning: how fast a server must reply to a PING before
+	 * a warning to opers is generated.
+	 */
+	ping_warning = 15 seconds;
+
+	/*
+	 * connectfreq: only used in server classes.  Specifies the delay
+	 * between autoconnecting to servers.
+	 */
+	connectfreq = 5 minutes;
+
+	/* max number: the amount of servers to autoconnect to */
+	max_number = 1;
+
+	/* sendq: servers need a higher sendq as they send more data */
+	sendq = 2 megabytes;
+};
+
+/*
+ * listen {}:  contains information about the ports ircd listens on (OLD P:)
+ */
+listen {
+	/*
+	 * port: the specific port to listen on.  If no host is specified
+	 * before, it will listen on all available IPs.
+	 *
+	 * Ports are separated via a comma, a range may be specified using ".."
+	 */
+	
+	/* port: listen on all available IPs, ports 6665 to 6669 */
+	port = 6665 .. 6669;
+
+	/*
+	 * Listen on 192.168.0.1/6697 with ssl enabled and hidden from STATS P
+	 * unless you are an administrator.
+	 *
+	 * NOTE: The "flags" directive has to come before "port".  Always!
+	 */
+	#flags = hidden, ssl;
+	#host = "192.168.0.1";
+	#port = 6697;
+
+	/*
+	 * host: set a specific IP/host the ports after the line will listen 
+	 * on.  This may be ipv4 or ipv6.
+	 */
+	#host = "1.2.3.4";
+	#port = 7000, 7001;
+
+	#host = "3ffe:1234:a:b:c::d";
+	#port = 7002;
+	
+	@extraListen@
+};
+
+auth {
+	user = "*@*";
+	class = "users";
+	#flags = need_ident;
+};
+
+/*
+ * operator {}:  defines ircd operators. (OLD O:)
+ *
+ * ircd-hybrid no longer supports local operators, privileges are
+ * controlled via flags.
+ */
+operator {
+	/* name: the name of the oper */
+	/* NOTE: operator "opername"{} is also supported */
+	name = "god";
+
+	/*
+	 * user: the user@host required for this operator.  CIDR is not
+	 * supported.  Multiple user="" lines are supported.
+	 */
+	user = "*god@*";
+	user = "*@127.0.0.1";
+
+	/*
+	 * password: the password required to oper.  By default this will
+	 * need to be encrypted using 'mkpasswd'.  MD5 is supported.
+	 */
+	password = "iamoperator";
+
+	/*
+	 * encrypted: controls whether the oper password above has been
+	 * encrypted.  (OLD CRYPT_OPER_PASSWORD now optional per operator)
+	 */
+	encrypted = no;
+
+	/*
+	 * rsa_public_key_file: the public key for this oper when using Challenge.
+	 * A password should not be defined when this is used, see 
+	 * doc/challenge.txt for more information.
+	 */
+#	rsa_public_key_file = "/usr/local/ircd/etc/oper.pub";
+
+	/* class: the class the oper joins when they successfully /oper */
+	class = "opers";
+
+	/*
+	 * umodes: default usermodes opers get when they /oper.  If defined,
+	 * it will override oper_umodes settings in general {}.
+	 * Available usermodes:
+	 *
+	 * +b - bots         - See bot and drone flooding notices
+	 * +c - cconn        - Client connection/quit notices
+	 * +D - deaf         - Don't receive channel messages
+	 * +d - debug        - See debugging notices
+	 * +f - full         - See I: line full notices
+	 * +G - softcallerid - Server Side Ignore for users not on your channels
+	 * +g - callerid     - Server Side Ignore (for privmsgs etc)
+	 * +i - invisible    - Not shown in NAMES or WHO unless you share a
+	 *                     a channel
+	 * +k - skill        - See server generated KILL messages
+	 * +l - locops       - See LOCOPS messages
+	 * +n - nchange      - See client nick changes
+	 * +r - rej          - See rejected client notices
+	 * +s - servnotice   - See general server notices
+	 * +u - unauth       - See unauthorized client notices
+	 * +w - wallop       - See server generated WALLOPS
+	 * +x - external     - See remote server connection and split notices
+	 * +y - spy          - See LINKS, STATS, TRACE notices etc.
+	 * +z - operwall     - See oper generated WALLOPS
+	 */
+#	umodes = locops, servnotice, operwall, wallop;
+
+	/*
+	 * privileges: controls the activities and commands an oper is 
+	 * allowed to do on the server.  All options default to no.
+	 * Available options:
+	 *
+	 * global_kill:  allows remote users to be /KILL'd (OLD 'O' flag)
+	 * remote:       allows remote SQUIT and CONNECT   (OLD 'R' flag)
+	 * remoteban:    allows remote KLINE/UNKLINE
+	 * kline:        allows KILL, KLINE and DLINE      (OLD 'K' flag)
+	 * unkline:      allows UNKLINE and UNDLINE        (OLD 'U' flag)
+	 * gline:        allows GLINE                      (OLD 'G' flag)
+	 * xline:         allows XLINE                     (OLD 'X' flag)
+	 * operwall:     allows OPERWALL
+	 * nick_changes: allows oper to see nickchanges    (OLD 'N' flag)
+	 *               via usermode +n
+	 * rehash:       allows oper to REHASH config      (OLD 'H' flag)
+	 * die:          allows DIE and RESTART            (OLD 'D' flag)
+	 * admin:        gives admin privileges.  admins
+	 *               may (un)load modules and see the
+	 *               real IPs of servers.
+	 * hidden_admin: same as 'admin', but noone can recognize you as
+	 *               being an admin
+	 * hidden_oper:  not shown in /stats p (except for other operators)
+	 */
+	/* You can either use
+	 * die = yes;
+	 * rehash = yes;
+	 *
+	 * or in a flags statement i.e.
+	 * flags = die, rehash;
+	 *
+	 * You can also negate a flag with ~ i.e.
+	 * flags = ~remote;
+	 *
+	 */
+	flags = global_kill, remote, kline, unkline, xline,
+		die, rehash, nick_changes, admin, operwall;
+};
+
+/*
+ * shared {}: users that are allowed to remote kline (OLD U:)
+ *
+ * NOTE: This can be effectively used for remote klines.
+ *       Please note that there is no password authentication
+ *       for users setting remote klines.  You must also be
+ *       /oper'd in order to issue a remote kline.
+ */
+shared {
+	/*
+	 * name: the server the user must be on to set klines.  If this is not
+	 * specified, the user will be allowed to kline from all servers.
+	 */
+	name = "irc2.some.server";
+
+	/*
+	 * user: the user@host mask that is allowed to set klines.  If this is
+	 * not specified, all users on the server above will be allowed to set
+	 * a remote kline.
+	 */
+	user = "oper@my.host.is.spoofed";
+
+	/*
+	 * type: list of what to share, options are as follows:
+	 *	kline	- allow oper/server to kline
+	 *	tkline	- allow temporary klines
+	 *	unkline	- allow oper/server to unkline
+	 *	xline	- allow oper/server to xline
+	 * 	txline	- allow temporary xlines
+	 *	unxline	- allow oper/server to unxline
+	 *	resv	- allow oper/server to resv
+	 * 	tresv	- allow temporary resvs
+	 *	unresv	- allow oper/server to unresv
+	 *      locops  - allow oper/server to locops - only used for servers that cluster
+	 *	all	- allow oper/server to do all of the above (default)
+	 */
+	type = kline, unkline, resv;
+};
+
+/*
+ * kill {}:  users that are not allowed to connect (OLD K:)
+ * Oper issued klines will be added to the specified kline config
+ */
+kill {
+	user = "bad@*.hacked.edu";
+	reason = "Obviously hacked account";
+};
+
+kill {
+	user = "^O[[:alpha:]]?[[:digit:]]+(x\.o|\.xo)$@^[[:alnum:]]{4}\.evilnet.org$";
+	type = regex;
+};
+
+/*
+ * deny {}:  IPs that are not allowed to connect (before DNS/ident lookup)
+ * Oper issued dlines will be added to the specified dline config
+ */
+deny {
+	ip = "10.0.1.0/24";
+	reason = "Reconnecting vhosted bots";
+};
+
+/*
+ * exempt {}: IPs that are exempt from deny {} and Dlines. (OLD d:)
+ */
+exempt {
+	ip = "192.168.0.0/16";
+};
+
+/*
+ * resv {}:  nicks and channels users may not use/join (OLD Q:)
+ */
+resv {
+	/* reason: the reason for the proceeding resv's */
+	reason = "There are no services on this network";
+
+	/* resv: the nicks and channels users may not join/use */
+	nick = "nickserv";
+	nick = "chanserv";
+	channel = "#services";
+
+	/* resv: wildcard masks are also supported in nicks only */
+	reason = "Clone bots";
+	nick = "clone*";
+};
+
+/*
+ * gecos {}:  The X: replacement, used for banning users based on
+ * their "realname".
+ */
+gecos {
+	name = "*sex*";
+	reason = "Possible spambot";
+};
+
+gecos {
+	name = "sub7server";
+	reason = "Trojan drone";
+};
+
+gecos {
+	name = "*http*";
+	reason = "Spambot";
+};
+
+gecos {
+	name = "^\[J[0o]hn Do[3e]\]-[0-9]{2,5}$";
+	type = regex;
+};
+
+/*
+ * channel {}:  The channel block contains options pertaining to channels
+ */
+channel {
+	/*
+	 * disable_fake_channels: this option, if set to 'yes', will
+	 * disallow clients to create or join channels that have one
+	 * of the following ASCII characters in their name:
+	 *
+	 *   2 | bold
+	 *   3 | mirc color
+         *  15 | plain text
+	 *  22 | reverse
+	 *  31 | underline
+	 * 160 | non-breaking space
+	 */
+	disable_fake_channels = yes;
+
+	/*
+	 * restrict_channels: reverse channel RESVs logic, only reserved
+	 * channels are allowed
+	 */
+	restrict_channels = no;
+
+	/*
+	 * disable_local_channels: prevent users from joining &channels.
+	 */
+	disable_local_channels = no;
+
+	/*
+	 * use_invex: Enable/disable channel mode +I, a n!u@h list of masks
+	 * that can join a +i channel without an invite.
+	 */
+	use_invex = yes;
+
+	/*
+	 * use_except: Enable/disable channel mode +e, a n!u@h list of masks
+	 * that can join a channel through a ban (+b).
+	 */
+	use_except = yes;
+
+	/*
+	 * use_knock: Allows users to request an invite to a channel that
+	 * is locked somehow (+ikl).  If the channel is +p or you are banned
+	 * the knock will not be sent.
+	 */
+	use_knock = yes;
+
+	/*
+	 * knock_delay: The amount of time a user must wait between issuing
+	 * the knock command.
+	 */
+	knock_delay = 1 minutes;
+
+	/*
+	 * knock_delay_channel: How often a knock to any specific channel
+	 * is permitted, regardless of the user sending the knock.
+	 */
+	knock_delay_channel = 1 minute;
+
+	/*
+	 * burst_topicwho: enable sending of who set topic on topicburst
+	 * default is yes
+	 */
+	burst_topicwho = yes;
+
+	/*
+	 * max_chans_per_user: The maximum number of channels a user can
+	 * join/be on.
+	 */
+	max_chans_per_user = 25;
+
+	/* quiet_on_ban: stop banned people talking in channels. */
+	quiet_on_ban = yes;
+
+	/* max_bans: maximum number of +b/e/I modes in a channel */
+	max_bans = 1000;
+
+	/*
+	 * how many joins in how many seconds constitute a flood, use 0 to
+	 * disable. +b opers will be notified (changeable via /set)
+	 */
+	join_flood_count = 100;
+	join_flood_time = 10 seconds;
+
+	/*
+	 * splitcode: The ircd will now check splitmode every few seconds.
+	 *
+	 * Either split users or split servers can activate splitmode, but
+	 * both conditions must be met for the ircd to deactivate splitmode.
+	 * 
+	 * You may force splitmode to be permanent by /quote set splitmode on
+	 */
+
+	/*
+	 * default_split_user_count: when the usercount is lower than this level,
+	 * consider ourselves split.  This must be set for automatic splitmode.
+	 */
+	default_split_user_count = 0;
+
+	/*
+	 * default_split_server_count: when the servercount is lower than this,
+	 * consider ourselves split.  This must be set for automatic splitmode.
+	 */
+	default_split_server_count = 0;
+
+	/* split no create: disallow users creating channels on split. */
+	no_create_on_split = yes;
+
+	/* split: no join: disallow users joining channels at all on a split */
+	no_join_on_split = no;
+};
+
+/*
+ * serverhide {}:  The serverhide block contains the options regarding
+ * serverhiding
+ */
+serverhide {
+	/*
+	 * flatten_links: this option will show all servers in /links appear
+	 * that they are linked to this current server
+	 */
+	flatten_links = no;
+
+	/*
+	 * links_delay: how often to update the links file when it is
+	 * flattened.
+	 */
+	links_delay = 5 minutes;
+
+	/*
+	 * hidden: hide this server from a /links output on servers that
+	 * support it.  This allows hub servers to be hidden etc.
+	 */
+	hidden = no;
+
+	/*
+	 * disable_hidden: prevent servers hiding themselves from a
+	 * /links output.
+	 */
+	disable_hidden = no;
+
+	/*
+	 * hide_servers: hide remote servernames everywhere and instead use
+	 * hidden_name and network_desc.
+	 */
+	hide_servers = no;
+
+	/*
+	 * Use this as the servername users see if hide_servers = yes.
+	 */
+	hidden_name = "*.hidden.com";
+
+	/*
+	 * hide_server_ips: If this is disabled, opers will be unable to see servers
+	 * ips and will be shown a masked ip, admins will be shown the real ip.
+	 *
+	 * If this is enabled, nobody can see a servers ip.  *This is a kludge*, it
+	 * has the side effect of hiding the ips everywhere, including logfiles.
+	 *
+	 * We recommend you leave this disabled, and just take care with who you
+	 * give admin=yes; to.
+	 */
+	hide_server_ips = no;
+};
+
+/*
+ * general {}:  The general block contains many of the options that were once
+ * compiled in options in config.h.  The general block is read at start time.
+ */
+general {
+	/*
+	 * gline_min_cidr: the minimum required length of a CIDR bitmask
+	 * for IPv4 based glines
+	 */
+	gline_min_cidr = 16;
+
+	/*
+	 * gline_min_cidr6: the minimum required length of a CIDR bitmask
+	 * for IPv6 based glines
+	 */
+	gline_min_cidr6 = 48;
+
+	/*
+	 * Whether to automatically set mode +i on connecting users.
+	 */
+	invisible_on_connect = yes;
+
+	/*
+	 * If you don't explicitly specify burst_away in your connect blocks, then
+	 * they will default to the burst_away value below.
+	 */
+	burst_away = no;
+
+	/*
+	 * Show "actually using host <ip>" on /whois when possible.
+	 */
+	use_whois_actually = yes;
+
+	/*
+	 * Max time from the nickname change that still causes KILL
+	 * automatically to switch for the current nick of that user. (seconds)
+	 */
+	kill_chase_time_limit = 90;
+
+	/*
+	 * If hide_spoof_ips is disabled, opers will be allowed to see the real IP of spoofed
+	 * users in /trace etc.  If this is defined they will be shown a masked IP.
+	 */
+	hide_spoof_ips = yes;
+
+	/*
+	 * Ignore bogus timestamps from other servers.  Yes, this will desync
+	 * the network, but it will allow chanops to resync with a valid non TS 0
+	 *
+	 * This should be enabled network wide, or not at all.
+	 */
+	ignore_bogus_ts = no;
+
+	/*
+	 * disable_auth: completely disable ident lookups; if you enable this,
+	 * be careful of what you set need_ident to in your auth {} blocks
+	 */
+	disable_auth = no;
+
+	/* disable_remote_commands: disable users doing commands on remote servers */
+	disable_remote_commands = no;
+
+	/*
+	 * tkline_expire_notices: enables or disables temporary kline/xline
+	 * expire notices.
+	 */
+	tkline_expire_notices = no;
+
+	/*
+	 * default_floodcount: the default value of floodcount that is configurable
+	 * via /quote set floodcount.  This is the amount of lines a user
+	 * may send to any other user/channel in one second.
+	 */
+	default_floodcount = 10;
+
+	/*
+	 * failed_oper_notice: send a notice to all opers on the server when 
+	 * someone tries to OPER and uses the wrong password, host or ident.
+	 */
+	failed_oper_notice = yes;
+
+	/*
+	 * dots_in_ident: the amount of '.' characters permitted in an ident
+	 * reply before the user is rejected.
+	 */
+	dots_in_ident = 2;
+
+	/*
+	 * dot_in_ip6_addr: ircd-hybrid-6.0 and earlier will disallow hosts 
+	 * without a '.' in them.  This will add one to the end.  Only needed
+	 * for older servers.
+	 */
+	dot_in_ip6_addr = no;
+
+	/*
+	 * min_nonwildcard: the minimum non wildcard characters in k/d/g lines
+	 * placed via the server.  klines hand placed are exempt from limits.
+	 * wildcard chars: '.' ':' '*' '?' '@' '!' '#'
+	 */
+	min_nonwildcard = 4;
+
+	/*
+	 * min_nonwildcard_simple: the minimum non wildcard characters in 
+	 * gecos bans.  wildcard chars: '*' '?' '#'
+	 */
+	min_nonwildcard_simple = 3;
+
+	/* max_accept: maximum allowed /accept's for +g usermode */
+	max_accept = 20;
+
+	/* anti_nick_flood: enable the nickflood control code */
+	anti_nick_flood = yes;
+
+	/* nick flood: the nick changes allowed in the specified period */
+	max_nick_time = 20 seconds;
+	max_nick_changes = 5;
+
+	/*
+	 * anti_spam_exit_message_time: the minimum time a user must be connected
+	 * before custom quit messages are allowed.
+	 */
+	anti_spam_exit_message_time = 5 minutes;
+
+	/*
+	 * ts delta: the time delta allowed between server clocks before
+	 * a warning is given, or before the link is dropped.  all servers
+	 * should run ntpdate/rdate to keep clocks in sync
+	 */
+	ts_warn_delta = 30 seconds;
+	ts_max_delta = 5 minutes;
+
+	/*
+	 * kline_with_reason: show the user the reason why they are k/d/glined 
+	 * on exit.  May give away who set k/dline when set via tcm.
+	 */
+	kline_with_reason = yes;
+
+	/*
+	 * kline_reason: show this message to users on channel
+	 * instead of the oper reason.
+	 */
+	kline_reason = "Connection closed";
+
+	/*
+	 * reject_hold_time: wait this amount of time before disconnecting
+	 * a rejected client. Use 0 to disable.
+	 */
+	reject_hold_time = 0;
+
+	/*
+	 * warn_no_nline: warn opers about servers that try to connect but
+	 * we don't have a connect {} block for.  Twits with misconfigured 
+	 * servers can get really annoying with this enabled.
+	 */
+	warn_no_nline = yes;
+
+	/*
+	 * stats_e_disabled: set this to 'yes' to disable "STATS e" for both
+	 * operators and administrators.  Doing so is a good idea in case
+	 * there are any exempted (exempt{}) server IPs you don't want to
+	 * see leaked.
+	 */
+	stats_e_disabled = no;
+
+	/* stats_o_oper only: make stats o (opers) oper only */
+	stats_o_oper_only = yes;
+
+	/* stats_P_oper_only: make stats P (ports) oper only */
+	stats_P_oper_only = yes;
+
+	/*
+	 * stats i oper only: make stats i (auth {}) oper only. set to:
+	 *     yes:    show users no auth blocks, made oper only.
+	 *     masked: show users first matching auth block
+	 *     no:     show users all auth blocks.
+	 */
+	stats_i_oper_only = yes;
+
+	/*
+	 * stats_k_oper_only: make stats k/K (klines) oper only.  set to:
+	 *     yes:    show users no auth blocks, made oper only
+	 *     masked: show users first matching auth block
+	 *     no:     show users all auth blocks.
+	 */
+	stats_k_oper_only = yes;
+
+	/*
+	 * caller_id_wait: time between notifying a +g user that somebody
+	 * is messaging them.
+	 */
+	caller_id_wait = 1 minute;
+
+	/*
+	 * opers_bypass_callerid: allows operators to bypass +g and message
+	 * anyone who has it set (useful if you use services).
+	 */
+	opers_bypass_callerid = no;
+
+	/*
+	 * pace_wait_simple: time between use of less intensive commands
+	 * (ADMIN, HELP, (L)USERS, VERSION, remote WHOIS)
+	 */
+	pace_wait_simple = 1 second;
+
+	/*
+	 * pace_wait: time between more intensive commands
+	 * (INFO, LINKS, LIST, MAP, MOTD, STATS, WHO, wildcard WHOIS, WHOWAS)
+	 */
+	pace_wait = 10 seconds;
+
+	/*
+	 * short_motd: send clients a notice telling them to read the motd
+	 * instead of forcing a motd to clients who may simply ignore it.
+	 */
+	short_motd = no;
+
+	/*
+	 * ping_cookie: require clients to respond exactly to a ping command,
+	 * can help block certain types of drones and FTP PASV mode spoofing.
+	 */
+	ping_cookie = no;
+
+	/* no_oper_flood: increase flood limits for opers. */
+	no_oper_flood = yes;
+
+	/*
+	 * true_no_oper_flood: completely eliminate flood limits for opers
+	 * and for clients with can_flood = yes in their auth {} blocks
+	 */
+	true_no_oper_flood = yes;
+
+	/* oper_pass_resv: allow opers to over-ride RESVs on nicks/channels */
+	oper_pass_resv = yes;
+
+	/*
+	 * idletime: the maximum amount of time a user may idle before
+	 * they are disconnected
+	 */
+	idletime = 0;
+
+	/* REMOVE ME.  The following line checks you've been reading. */
+	#havent_read_conf = 1;
+
+	/*
+	 * max_targets: the maximum amount of targets in a single 
+	 * PRIVMSG/NOTICE.  Set to 999 NOT 0 for unlimited.
+	 */
+	max_targets = 4;
+
+	/*
+	 * client_flood: maximum amount of data in a clients queue before
+	 * they are dropped for flooding.
+	 */
+	client_flood = 2560 bytes;
+
+	/*
+	 * message_locale: the default message locale
+	 * Use "standard" for the compiled in defaults.
+	 * To install the translated messages, go into messages/ in the
+	 * source directory and run `make install'.
+	 */
+	message_locale = "standard";
+
+	/*
+	 * usermodes configurable: a list of usermodes for the options below
+	 *
+	 * +b - bots         - See bot and drone flooding notices
+	 * +c - cconn        - Client connection/quit notices
+	 * +D - deaf         - Don't receive channel messages
+	 * +d - debug        - See debugging notices
+	 * +f - full         - See I: line full notices
+	 * +G - softcallerid - Server Side Ignore for users not on your channels
+	 * +g - callerid     - Server Side Ignore (for privmsgs etc)
+	 * +i - invisible    - Not shown in NAMES or WHO unless you share a 
+	 *                     a channel
+	 * +k - skill        - See server generated KILL messages
+	 * +l - locops       - See LOCOPS messages
+	 * +n - nchange      - See client nick changes
+	 * +r - rej          - See rejected client notices
+	 * +s - servnotice   - See general server notices
+	 * +u - unauth       - See unauthorized client notices
+	 * +w - wallop       - See server generated WALLOPS
+	 * +x - external     - See remote server connection and split notices
+	 * +y - spy          - See LINKS, STATS, TRACE notices etc.
+	 * +z - operwall     - See oper generated WALLOPS
+	 */
+
+	/* oper_only_umodes: usermodes only opers may set */
+	oper_only_umodes = bots, cconn, debug, full, skill, nchange, 
+			   rej, spy, external, operwall, locops, unauth;
+
+	/* oper_umodes: default usermodes opers get when they /oper */
+	oper_umodes = bots, locops, servnotice, operwall, wallop;
+
+	/*
+	 * servlink_path: path to 'servlink' program used by ircd to handle
+	 * encrypted/compressed server <-> server links.
+	 *
+	 * only define if servlink is not in same directory as ircd itself.
+	 */
+	#servlink_path = "/usr/local/ircd/bin/servlink";
+
+	/*
+	 * default_cipher_preference: default cipher to use for cryptlink when none is
+	 * specified in connect block.
+	 */
+	#default_cipher_preference = "BF/168";
+
+	/*
+	 * use_egd: if your system does not have *random devices yet you
+	 * want to use OpenSSL and encrypted links, enable this.  Beware -
+	 * EGD is *very* CPU intensive when gathering data for its pool
+	 */
+#	use_egd = yes;
+
+	/*
+	 * egdpool_path: path to EGD pool. Not necessary for OpenSSL >= 0.9.7
+	 * which automatically finds the path.
+	 */
+#	egdpool_path = "/var/run/egd-pool";
+
+
+	/*
+	 * compression_level: level of compression for compressed links between
+	 * servers.  
+	 *
+	 * values are between: 1 (least compression, fastest)
+	 *                and: 9 (most compression, slowest).
+	 */
+#	compression_level = 6;
+
+	/*
+	 * throttle_time: the minimum amount of time between connections from
+	 * the same ip.  exempt {} blocks are excluded from this throttling.
+	 * Offers protection against flooders who reconnect quickly.  
+	 * Set to 0 to disable.
+	 */
+	throttle_time = 10;
+};
+
+glines {
+	/* enable: enable glines, network wide temp klines */
+	enable = yes;
+
+	/*
+	 * duration: the amount of time a gline will remain on your
+	 * server before expiring
+	 */
+	duration = 1 day;
+
+	/*
+	 * logging: which types of rules you want to log when triggered
+	 * (choose reject or block)
+	 */
+	logging = reject, block;
+
+	/*
+	 * NOTE: gline ACLs can cause a desync of glines throughout the
+	 * network, meaning some servers may have a gline triggered, and
+	 * others may not. Also, you only need insert rules for glines
+	 * that you want to block and/or reject. If you want to accept and
+	 * propagate the gline, do NOT put a rule for it.
+	 */
+
+	/* user@host for rule to apply to */
+	user = "god@I.still.hate.packets";
+	/* server for rule to apply to */
+	name = "hades.arpa";
+
+	/*
+	 * action: action to take when a matching gline is found. options are:
+	 *  reject	- do not apply the gline locally
+	 *  block	- do not propagate the gline
+	 */
+	action = reject, block;
+
+	user = "god@*";
+	name = "*";
+	action = block;
+};
+
diff --git a/nixos/modules/services/networking/minidlna.nix b/nixos/modules/services/networking/minidlna.nix
new file mode 100644
index 00000000000..ea5bc8514f1
--- /dev/null
+++ b/nixos/modules/services/networking/minidlna.nix
@@ -0,0 +1,112 @@
+# Module for MiniDLNA, a simple DLNA server.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.minidlna;
+
+  port = 8200;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.minidlna.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description =
+        ''
+          Whether to enable MiniDLNA, a simple DLNA server.  It serves
+          media files such as video and music to DLNA client devices
+          such as televisions and media players.
+        '';
+    };
+
+    services.minidlna.mediaDirs = mkOption {
+      type = types.listOf types.string;
+      default = [];
+      examples = [ "/data/media" "V,/home/alice/video" ];
+      description =
+        ''
+          Directories to be scanned for media files.  The prefixes
+          <literal>A,</literal>, <literal>V,</literal> and
+          <literal>P,</literal> restrict a directory to audio, video
+          or image files.  The directories must be accessible to the
+          <literal>minidlna</literal> user account.
+        '';
+    };
+
+    services.minidlna.config = mkOption {
+      type = types.lines;
+      description = "The contents of MiniDLNA's configuration file.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    # Running minidlna only makes sense for serving files to the
+    # outside, so open up the required ports by default.
+    networking.firewall.allowedTCPPorts = [ port ];
+    networking.firewall.allowedUDPPorts = [ 1900 ]; # SSDP
+
+    services.minidlna.config =
+      ''
+        port=${toString port}
+        friendly_name=NixOS Media Server
+        db_dir=/var/cache/minidlna
+        log_dir=/var/log/minidlna
+        inotify=yes
+        ${concatMapStrings (dir: ''
+          media_dir=${dir}
+        '') cfg.mediaDirs}
+      '';
+
+    users.extraUsers.minidlna = {
+      description = "MiniDLNA daemon user";
+      group = "minidlna";
+      uid = config.ids.uids.minidlna;
+    };
+
+    users.extraGroups.minidlna.gid = config.ids.gids.minidlna;
+
+    systemd.services.minidlna =
+      { description = "MiniDLNA Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        preStart =
+          ''
+            mkdir -p /var/cache/minidlna /var/log/minidlna /run/minidlna
+            chown minidlna /var/cache/minidlna /var/log/minidlna /run/minidlna
+          '';
+
+        # FIXME: log through the journal rather than
+        # /var/log/minidlna.  The -d flag does that, but also raises
+        # the log level to debug...
+        serviceConfig =
+          { User = "minidlna";
+            Group = "nogroup";
+            PermissionsStartOnly = true;
+            Type = "forking";
+            PIDFile = "/run/minidlna/pid";
+            ExecStart =
+              "@${pkgs.minidlna}/sbin/minidlna minidlna -P /run/minidlna/pid" +
+              " -f ${pkgs.writeText "minidlna.conf" cfg.config}";
+          };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
new file mode 100644
index 00000000000..9d62a764f06
--- /dev/null
+++ b/nixos/modules/services/networking/nat.nix
@@ -0,0 +1,104 @@
+# This module enables Network Address Translation (NAT).
+# XXX: todo: support multiple upstream links
+# see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking.nat;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.nat.enable = mkOption {
+      default = false;
+      description =
+        ''
+          Whether to enable Network Address Translation (NAT).
+        '';
+    };
+
+    networking.nat.internalIPs = mkOption {
+      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.
+        '';
+      # Backward compatibility: this used to be a single range instead
+      # of a list.
+      apply = x: if isList x then x else [x];
+    };
+
+    networking.nat.externalInterface = mkOption {
+      example = "eth1";
+      description =
+        ''
+          The name of the external network interface.
+        '';
+    };
+
+    networking.nat.externalIP = mkOption {
+      default = "";
+      example = "203.0.113.123";
+      description =
+        ''
+          The public IP address to which packets from the local
+          network are to be rewritten.  If this is left empty, the
+          IP address associated with the external interface will be
+          used.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.networking.nat.enable {
+
+    environment.systemPackages = [ pkgs.iptables ];
+
+    boot.kernelModules = [ "nf_nat_ftp" ];
+
+    jobs.nat =
+      { description = "Network Address Translation";
+
+        startOn = "started network-interfaces";
+
+        path = [ pkgs.iptables ];
+
+        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 == ""
+                then "-j MASQUERADE"
+                else "-j SNAT --to-source ${cfg.externalIP}"}
+            ''
+          ) cfg.internalIPs) +
+          ''
+            echo 1 > /proc/sys/net/ipv4/ip_forward
+          '';
+
+        postStop =
+          ''
+            iptables -t nat -F POSTROUTING
+          '';
+      };
+  };
+}
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
new file mode 100644
index 00000000000..1d5682f5f3f
--- /dev/null
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -0,0 +1,193 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with pkgs;
+
+let
+  cfg = config.networking.networkmanager;
+
+  stateDirs = "/var/lib/NetworkManager /var/lib/dhclient";
+
+  configFile = writeText "NetworkManager.conf" ''
+    [main]
+    plugins=keyfile
+
+    [keyfile]
+    ${optionalString (config.networking.hostName != "") ''
+      hostname=${config.networking.hostName}
+    ''}
+
+    [logging]
+    level=WARN
+  '';
+
+  polkitConf = ''
+    [network-manager]
+    Identity=unix-group:networkmanager
+    Action=org.freedesktop.NetworkManager.*
+    ResultAny=yes
+    ResultInactive=no
+    ResultActive=yes
+
+    [modem-manager]
+    Identity=unix-group:networkmanager
+    Action=org.freedesktop.ModemManager.*
+    ResultAny=yes
+    ResultInactive=no
+    ResultActive=yes
+  '';
+
+  ipUpScript = writeScript "01nixos-ip-up" ''
+    #!/bin/sh
+    if test "$2" = "up"; then
+      ${config.systemd.package}/bin/systemctl start ip-up.target
+    fi
+  '';
+
+  overrideNameserversScript = writeScript "02overridedns" ''
+    #!/bin/sh
+    ${optionalString cfg.overrideNameservers "${gnused}/bin/sed -i '/nameserver /d' /etc/resolv.conf"}
+    ${concatStrings (map (s: ''
+      ${optionalString cfg.appendNameservers
+        "${gnused}/bin/sed -i '/nameserver ${s}/d' /etc/resolv.conf"
+      }
+      echo 'nameserver ${s}' >> /etc/resolv.conf
+    '') config.networking.nameservers)}
+  '';
+
+in {
+
+  ###### interface
+
+  options = {
+
+    networking.networkmanager = {
+
+      enable = mkOption {
+        default = false;
+        merge = mergeEnableOption;
+        description = ''
+          Whether to use NetworkManager to obtain an IP address and other
+          configuration for all network interfaces that are not manually
+          configured. If enabled, a group <literal>networkmanager</literal>
+          will be created. Add all users that should have permission
+          to change network settings to this group.
+        '';
+      };
+  
+      packages = mkOption {
+        default = [ ];
+        description = ''
+          Extra packages that provide NetworkManager plugins.
+        '';
+        merge = mergeListOption;
+        apply = list: [ networkmanager modemmanager wpa_supplicant ] ++ list;
+      };
+
+      overrideNameservers = mkOption {
+        default = false;
+        description = ''
+          If enabled, any nameservers received by DHCP or configured in
+          NetworkManager will be replaced by the nameservers configured
+          in the <literal>networking.nameservers</literal> option. This
+          option overrides the <literal>appendNameservers</literal> option
+          if both are enabled.
+        '';
+      };
+
+      appendNameservers = mkOption {
+        default = false;
+        description = ''
+          If enabled, the name servers configured in the
+          <literal>networking.nameservers</literal> option will be appended
+          to the ones configured in NetworkManager or received by DHCP.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    assertions = [{
+      assertion = config.networking.wireless.enable == false;
+      message = "You can not use networking.networkmanager with services.networking.wireless";
+    }];
+
+    environment.etc = [
+      { source = ipUpScript;
+        target = "NetworkManager/dispatcher.d/01nixos-ip-up";
+      }
+      { source = configFile;
+        target = "NetworkManager/NetworkManager.conf";
+      }
+      { source = "${networkmanager_openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
+        target = "NetworkManager/VPN/nm-openvpn-service.name";
+      }
+      { source = "${networkmanager_vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
+        target = "NetworkManager/VPN/nm-vpnc-service.name";
+      }
+      { source = "${networkmanager_openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
+        target = "NetworkManager/VPN/nm-openconnect-service.name";
+      }
+    ] ++ pkgs.lib.optional (cfg.overrideNameservers || cfg.appendNameservers)
+           { source = overrideNameserversScript;
+             target = "NetworkManager/dispatcher.d/02overridedns";
+           };
+
+    environment.systemPackages = cfg.packages ++ [
+        networkmanager_openvpn
+        networkmanager_vpnc
+        networkmanager_openconnect
+        ];
+
+    users.extraGroups = singleton {
+      name = "networkmanager";
+      gid = config.ids.gids.networkmanager;
+    };
+
+    systemd.packages = cfg.packages;
+
+    # Create an initialisation service that both starts
+    # NetworkManager when network.target is reached,
+    # and sets up necessary directories for NM.
+    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";
+      };
+    };
+
+    # Turn off NixOS' network management
+    networking = {
+      useDHCP = false;
+      wireless.enable = false;
+    };
+
+    powerManagement.resumeCommands = ''
+      systemctl restart NetworkManager
+    '';
+
+    security.polkit.permissions = polkitConf;
+
+    # openvpn plugin has only dbus interface
+    services.dbus.packages = cfg.packages ++ [
+        networkmanager_openvpn
+        networkmanager_vpnc
+        networkmanager_openconnect
+        ];
+
+    services.udev.packages = cfg.packages;
+  };
+}
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix
new file mode 100644
index 00000000000..e5e164021d3
--- /dev/null
+++ b/nixos/modules/services/networking/ntpd.nix
@@ -0,0 +1,90 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) ntp;
+
+  stateDir = "/var/lib/ntp";
+
+  ntpUser = "ntp";
+
+  configFile = pkgs.writeText "ntp.conf" ''
+    # Keep the drift file in ${stateDir}/ntp.drift.  However, since we
+    # chroot to ${stateDir}, we have to specify it as /ntp.drift.
+    driftfile /ntp.drift
+
+    ${toString (map (server: "server " + server + " iburst\n") config.services.ntp.servers)}
+  '';
+
+  ntpFlags = "-c ${configFile} -u ${ntpUser}:nogroup -i ${stateDir}";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.ntp = {
+
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whether to synchronise your machine's time using the NTP
+          protocol.
+        '';
+      };
+
+      servers = mkOption {
+        default = [
+          "0.pool.ntp.org"
+          "1.pool.ntp.org"
+          "2.pool.ntp.org"
+        ];
+        description = ''
+          The set of NTP servers from which to synchronise.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.ntp.enable {
+
+    # Make tools such as ntpq available in the system path
+    environment.systemPackages = [ pkgs.ntp ];
+
+    users.extraUsers = singleton
+      { name = ntpUser;
+        uid = config.ids.uids.ntp;
+        description = "NTP daemon user";
+        home = stateDir;
+      };
+
+    jobs.ntpd =
+      { description = "NTP Daemon";
+
+        wantedBy = [ "ip-up.target" ];
+        partOf = [ "ip-up.target" ];
+
+        path = [ ntp ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${ntpUser} ${stateDir}
+          '';
+
+        exec = "ntpd -g -n ${ntpFlags}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/oidentd.nix b/nixos/modules/services/networking/oidentd.nix
new file mode 100644
index 00000000000..a2a555a8ad1
--- /dev/null
+++ b/nixos/modules/services/networking/oidentd.nix
@@ -0,0 +1,44 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.oidentd.enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable ‘oidentd’, an implementation of the Ident
+        protocol (RFC 1413).  It allows remote systems to identify the
+        name of the user associated with a TCP connection.
+      '';
+    };
+
+  };
+
+  
+  ###### implementation
+
+  config = mkIf config.services.oidentd.enable {
+
+    jobs.oidentd =
+      { startOn = "started network-interfaces";
+        daemonType = "fork";
+        exec = "${pkgs.oidentd}/sbin/oidentd -u oidentd -g nogroup";
+      };
+
+    users.extraUsers.oidentd = {
+      description = "Ident Protocol daemon user";
+      group = "oidentd";
+      uid = config.ids.uids.oidentd;
+    };
+
+    users.extraGroups.oidentd.gid = config.ids.gids.oidentd;
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/openfire.nix b/nixos/modules/services/networking/openfire.nix
new file mode 100644
index 00000000000..d5c18c0675c
--- /dev/null
+++ b/nixos/modules/services/networking/openfire.nix
@@ -0,0 +1,70 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) jre openfire coreutils which gnugrep gawk gnused;
+
+  extraStartDependency =
+    if config.services.openfire.usePostgreSQL then "and started postgresql" else "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.openfire = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable OpenFire XMPP server.
+        ";
+      };
+
+      usePostgreSQL = mkOption {
+        default = true;
+        description = "
+          Whether you use PostgreSQL service for your storage back-end.
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.openfire.enable (
+  mkAssert (!(config.services.openfire.usePostgreSQL -> config.services.postgresql.enable)) "
+    openfire assertion failed
+  " {
+
+    jobs.openfire =
+      { description = "OpenFire XMPP server";
+
+        startOn = "started networking ${extraStartDependency}";
+
+        script =
+          ''
+            export PATH=${jre}/bin:${openfire}/bin:${coreutils}/bin:${which}/bin:${gnugrep}/bin:${gawk}/bin:${gnused}/bin
+            export HOME=/tmp
+            mkdir /var/log/openfire || true
+            mkdir /etc/openfire || true
+            for i in ${openfire}/conf.inst/*; do
+                if ! test -f /etc/openfire/$(basename $i); then
+                    cp $i /etc/openfire/
+                fi
+            done
+            openfire start
+          ''; # */
+      };
+
+  });
+
+}
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
new file mode 100644
index 00000000000..1e862591406
--- /dev/null
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -0,0 +1,172 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.openvpn;
+
+  inherit (pkgs) openvpn;
+
+  makeOpenVPNJob = cfg: name:
+    let
+
+      path = (getAttr "openvpn-${name}" config.systemd.services).path;
+
+      upScript = ''
+        #! /bin/sh
+        export PATH=${path}
+
+        # For convenience in client scripts, extract the remote domain
+        # name and name server.
+        for var in ''${!foreign_option_*}; do
+          x=(''${!var})
+          if [ "''${x[0]}" = dhcp-option ]; then
+            if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}"
+            elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}"
+            fi
+          fi
+        done
+
+        ${cfg.up}
+      '';
+
+      downScript = ''
+        #! /bin/sh
+        export PATH=${path}
+        ${cfg.down}
+      '';
+
+      configFile = pkgs.writeText "openvpn-config-${name}"
+        ''
+          errors-to-stderr
+          ${optionalString (cfg.up != "" || cfg.down != "") "script-security 2"}
+          ${cfg.config}
+          ${optionalString (cfg.up != "") "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
+          ${optionalString (cfg.down != "") "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+        '';
+
+    in {
+      description = "OpenVPN instance ‘${name}’";
+
+      wantedBy = optional cfg.autoStart "multi-user.target";
+      after = [ "network-interfaces.target" ];
+
+      path = [ pkgs.iptables pkgs.iproute pkgs.nettools ];
+
+      serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}";
+      serviceConfig.Restart = "always";
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    /* !!! Obsolete. */
+    services.openvpn.enable = mkOption {
+      default = true;
+      description = "Whether to enable OpenVPN.";
+    };
+
+    services.openvpn.servers = mkOption {
+      default = {};
+
+      example = {
+
+        server = {
+          config = ''
+            # Simplest server configuration: http://openvpn.net/index.php/documentation/miscellaneous/static-key-mini-howto.html.
+            # server :
+            dev tun
+            ifconfig 10.8.0.1 10.8.0.2
+            secret /root/static.key
+          '';
+          up = "ip route add ...";
+          down = "ip route del ...";
+        };
+
+        client = {
+          config = ''
+            client
+            remote vpn.example.org
+            dev tun
+            proto tcp-client
+            port 8080
+            ca /root/.vpn/ca.crt
+            cert /root/.vpn/alice.crt
+            key /root/.vpn/alice.key
+          '';
+          up = "echo nameserver $nameserver | ${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev";
+          down = "${pkgs.openresolv}/sbin/resolvconf -d $dev";
+        };
+
+      };
+
+      description = ''
+        Each attribute of this option defines an Upstart job to run an
+        OpenVPN instance.  These can be OpenVPN servers or clients.
+        The name of each Upstart job is
+        <literal>openvpn-</literal><replaceable>name</replaceable>,
+        where <replaceable>name</replaceable> is the corresponding
+        attribute name.
+      '';
+
+      type = types.attrsOf types.optionSet;
+
+      options =  {
+
+        config = mkOption {
+          type = types.string;
+            description = ''
+            Configuration of this OpenVPN instance.  See
+            <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            for details.
+          '';
+        };
+
+        up = mkOption {
+          default = "";
+          type = types.string;
+          description = ''
+            Shell commands executed when the instance is starting.
+          '';
+        };
+
+        down = mkOption {
+          default = "";
+          type = types.string;
+          description = ''
+            Shell commands executed when the instance is shutting down.
+          '';
+        };
+
+        autoStart = mkOption {
+          default = true;
+          type = types.bool;
+          description = "Whether this OpenVPN instance should be started automatically.";
+        };
+
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.servers != {}) {
+
+    systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers);
+
+    environment.systemPackages = [ openvpn ];
+
+    boot.kernelModules = [ "tun" ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
new file mode 100644
index 00000000000..fb541bf101a
--- /dev/null
+++ b/nixos/modules/services/networking/prayer.nix
@@ -0,0 +1,103 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) prayer;
+
+  cfg = config.services.prayer;
+
+  stateDir = "/var/lib/prayer";
+
+  prayerUser = "prayer";
+  prayerGroup = "prayer";
+
+  prayerExtraCfg = pkgs.writeText "extraprayer.cf" ''
+    prefix = "${prayer}"
+    var_prefix = "${stateDir}"
+    prayer_user = "${prayerUser}"
+    prayer_group = "${prayerGroup}"
+    sendmail_path = "/var/setuid-wrappers/sendmail"
+
+    use_http_port ${cfg.port}
+
+    ${cfg.extraConfig}
+  '';
+
+  prayerCfg = pkgs.runCommand "prayer.cf" { } ''
+    # We have to remove the http_port 80, or it will start a server there
+    cat ${prayer}/etc/prayer.cf | grep -v http_port > $out
+    cat ${prayerExtraCfg} >> $out
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.prayer = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the prayer webmail http server.
+        '';
+      };
+
+      port = mkOption {
+        default = "2080";
+        description = ''
+          Port the prayer http server is listening to.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "" ;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.prayer.enable {
+    environment.systemPackages = [ prayer ];
+
+    users.extraUsers = singleton
+      { name = prayerUser;
+        uid = config.ids.uids.prayer;
+        description = "Prayer daemon user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = prayerGroup;
+        gid = config.ids.gids.prayer;
+      };
+
+    jobs.prayer =
+      { name = "prayer";
+
+        startOn = "startup";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${prayerUser}.${prayerGroup} ${stateDir}
+          '';
+
+        daemonType = "daemon";
+
+        exec = "${prayer}/sbin/prayer --config-file=${prayerCfg}";
+      };
+  };
+
+}
diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix
new file mode 100644
index 00000000000..89c40c53157
--- /dev/null
+++ b/nixos/modules/services/networking/privoxy.nix
@@ -0,0 +1,95 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) privoxy;
+
+  stateDir = "/var/spool/privoxy";
+
+  privoxyUser = "privoxy";
+
+  privoxyFlags = "--no-daemon --user ${privoxyUser} ${privoxyCfg}";
+
+  privoxyCfg = pkgs.writeText "privoxy.conf" ''
+    listen-address  ${config.services.privoxy.listenAddress}
+    logdir          ${config.services.privoxy.logDir}
+    confdir         ${privoxy}/etc
+    filterfile      default.filter
+
+    ${config.services.privoxy.extraConfig}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.privoxy = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the machine as a HTTP proxy server.
+        '';
+      };
+
+      listenAddress = mkOption {
+        default = "127.0.0.1:8118";
+        description = ''
+          Address the proxy server is listening to.
+        '';
+      };
+
+      logDir = mkOption {
+        default = "/var/log/privoxy" ;
+        description = ''
+          Location for privoxy log files.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "" ;
+        description = ''
+          Extra configuration. Contents will be added verbatim to the configuration file.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.privoxy.enable {
+  
+    environment.systemPackages = [ privoxy ];
+
+    users.extraUsers = singleton
+      { name = privoxyUser;
+        uid = config.ids.uids.privoxy;
+        description = "Privoxy daemon user";
+        home = stateDir;
+      };
+
+    jobs.privoxy =
+      { name = "privoxy";
+
+        startOn = "startup";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${privoxyUser} ${stateDir}
+          '';
+
+        exec = "${privoxy}/sbin/privoxy ${privoxyFlags}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix
new file mode 100644
index 00000000000..f3a4e457ec8
--- /dev/null
+++ b/nixos/modules/services/networking/quassel.nix
@@ -0,0 +1,96 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  quassel = pkgs.kde4.quasselDaemon;
+  cfg = config.services.quassel;
+  user = if cfg.user != null then cfg.user else "quassel";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.quassel = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run the Quassel IRC client daemon.
+        '';
+      };
+
+      interface = mkOption {
+        default = "127.0.0.1";
+        description = ''
+          The interface the Quassel daemon will be listening to.  If `127.0.0.1',
+          only clients on the local host can connect to it; if `0.0.0.0', clients
+          can access it from any network interface.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 4242;
+        description = ''
+          The port number the Quassel daemon will be listening to.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = ''/home/${user}/.config/quassel-irc.org'';
+        description = ''
+          The directory holding configuration files, the SQlite database and the SSL Cert.
+        '';
+      };
+
+      user = mkOption {
+        default = null;
+        description = ''
+          The existing user the Quassel daemon should run as. If left empty, a default "quassel" user will be created.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = mkIf (cfg.user == null) [
+      { name = "quassel";
+        description = "Quassel IRC client daemon";
+        group = "quassel";
+        uid = config.ids.uids.quassel;
+      }];
+
+    users.extraGroups = mkIf (cfg.user == null) [
+      { name = "quassel";
+        gid = config.ids.gids.quassel;
+      }];
+
+    jobs.quassel =
+      { description = "Quassel IRC client daemon";
+
+        startOn = "ip-up";
+
+        preStart = ''
+            mkdir -p ${cfg.dataDir}
+            chown ${user} ${cfg.dataDir}
+        '';
+
+        exec = ''
+            ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \
+                -c '${quassel}/bin/quasselcore --listen=${cfg.interface}\
+                    --port=${toString cfg.portNumber} --configdir=${cfg.dataDir}'
+        '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
new file mode 100644
index 00000000000..8d586ce6e46
--- /dev/null
+++ b/nixos/modules/services/networking/radvd.nix
@@ -0,0 +1,77 @@
+# Module for the IPv6 Router Advertisement Daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.radvd;
+
+  confFile = pkgs.writeText "radvd.conf" cfg.config;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.radvd.enable = mkOption {
+      default = false;
+      description =
+        ''
+          Whether to enable the Router Advertisement Daemon
+          (<command>radvd</command>), which provides link-local
+          advertisements of IPv6 router addresses and prefixes using
+          the Neighbor Discovery Protocol (NDP).  This enables
+          stateless address autoconfiguration in IPv6 clients on the
+          network.
+        '';
+    };
+
+    services.radvd.config = mkOption {
+      example =
+        ''
+          interface eth0 {
+            AdvSendAdvert on;
+            prefix 2001:db8:1234:5678::/64 { };
+          };
+        '';
+      description =
+        ''
+          The contents of the radvd configuration file.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.radvd ];
+
+    jobs.radvd =
+      { description = "IPv6 Router Advertisement Daemon";
+
+        startOn = "started network-interfaces";
+
+        preStart =
+          ''
+            # !!! Radvd only works if IPv6 forwarding is enabled.  But
+            # this should probably be done somewhere else (and not
+            # necessarily for all interfaces).
+            echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
+          '';
+
+        exec = "${pkgs.radvd}/sbin/radvd -m syslog -s -C ${confFile}";
+
+        daemonType = "fork";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix
new file mode 100644
index 00000000000..f797206ad5c
--- /dev/null
+++ b/nixos/modules/services/networking/rdnssd.nix
@@ -0,0 +1,48 @@
+# Module for rdnssd, a daemon that configures DNS servers in
+# /etc/resolv/conf from IPv6 RDNSS advertisements.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.rdnssd.enable = mkOption {
+      default = false;
+      #default = config.networking.enableIPv6;
+      description =
+        ''
+          Whether to enable the RDNSS daemon
+          (<command>rdnssd</command>), which configures DNS servers in
+          <filename>/etc/resolv.conf</filename> from RDNSS
+          advertisements sent by IPv6 routers.
+        '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.rdnssd.enable {
+
+    jobs.rdnssd =
+      { description = "RDNSS daemon";
+
+        # Start before the network interfaces are brought up so that
+        # the daemon receives RDNSS advertisements from the kernel.
+        startOn = "starting network-interfaces";
+
+        # !!! Should write to /var/run/rdnssd/resolv.conf and run the daemon under another uid.
+        exec = "${pkgs.ndisc6}/sbin/rdnssd --resolv-file /etc/resolv.conf -u root";
+
+        daemonType = "fork";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/rpcbind.nix b/nixos/modules/services/networking/rpcbind.nix
new file mode 100644
index 00000000000..00c958c5a4a
--- /dev/null
+++ b/nixos/modules/services/networking/rpcbind.nix
@@ -0,0 +1,81 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  netconfigFile = {
+    target = "netconfig";
+    source = pkgs.writeText "netconfig" ''
+      #
+      # The network configuration file. This file is currently only used in
+      # conjunction with the TI-RPC code in the libtirpc library.
+      #
+      # Entries consist of:
+      #
+      #       <network_id> <semantics> <flags> <protofamily> <protoname> \
+      #               <device> <nametoaddr_libs>
+      #
+      # The <device> and <nametoaddr_libs> fields are always empty in this
+      # implementation.
+      #
+      udp        tpi_clts      v     inet     udp     -       -
+      tcp        tpi_cots_ord  v     inet     tcp     -       -
+      udp6       tpi_clts      v     inet6    udp     -       -
+      tcp6       tpi_cots_ord  v     inet6    tcp     -       -
+      rawip      tpi_raw       -     inet      -      -       -
+      local      tpi_cots_ord  -     loopback  -      -       -
+      unix       tpi_cots_ord  -     loopback  -      -       -
+    '';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.rpcbind = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable `rpcbind', an ONC RPC directory service
+          notably used by NFS and NIS, and which can be queried
+          using the rpcinfo(1) command. `rpcbind` is a replacement for
+          `portmap`.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.rpcbind.enable {
+
+    environment.systemPackages = [ pkgs.rpcbind ];
+
+    environment.etc = [ netconfigFile ];
+
+    systemd.services.rpcbind =
+      { description = "ONC RPC Directory Service";
+
+        wantedBy = [ "multi-user.target" ];
+
+        requires = [ "basic.target" ];
+        after = [ "basic.target" ];
+
+        unitConfig.DefaultDependencies = false; # don't stop during shutdown
+
+        serviceConfig.Type = "forking";
+        serviceConfig.ExecStart = "@${pkgs.rpcbind}/bin/rpcbind rpcbind";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix
new file mode 100644
index 00000000000..8816ac0d2f8
--- /dev/null
+++ b/nixos/modules/services/networking/sabnzbd.nix
@@ -0,0 +1,52 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.sabnzbd;
+  inherit (pkgs) sabnzbd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.sabnzbd = {
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the sabnzbd FTP server.";
+      };
+      configFile = mkOption {
+        default = "/var/sabnzbd/sabnzbd.ini";
+        description = "Path to config file. (You need to create this file yourself!)";
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers =
+      [ { name = "sabnzbd";
+          uid = config.ids.uids.sabnzbd;
+          description = "sabnzbd user";
+          home = "/homeless-shelter";
+        }
+      ];
+
+    jobs.sabnzbd =
+      { description = "sabnzbd server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        exec = "${sabnzbd}/bin/sabnzbd -d -f ${cfg.configFile}";
+      };
+
+  };
+}
diff --git a/nixos/modules/services/networking/ssh/lshd.nix b/nixos/modules/services/networking/ssh/lshd.nix
new file mode 100644
index 00000000000..d32fabbde24
--- /dev/null
+++ b/nixos/modules/services/networking/ssh/lshd.nix
@@ -0,0 +1,175 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) lsh;
+
+  cfg = config.services.lshd;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.lshd = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the GNU lshd SSH2 daemon, which allows
+          secure remote login.
+        '';
+      };
+
+      portNumber = mkOption {
+        default = 22;
+        description = ''
+          The port on which to listen for connections.
+        '';
+      };
+
+      interfaces = mkOption {
+        default = [];
+        description = ''
+          List of network interfaces where listening for connections.
+          When providing the empty list, `[]', lshd listens on all
+          network interfaces.
+        '';
+        example = [ "localhost" "1.2.3.4:443" ];
+      };
+
+      hostKey = mkOption {
+        default = "/etc/lsh/host-key";
+        description = ''
+          Path to the server's private key.  Note that this key must
+          have been created, e.g., using "lsh-keygen --server |
+          lsh-writekey --server", so that you can run lshd.
+        '';
+      };
+
+      syslog = mkOption {
+        default = true;
+        description = ''Whether to enable syslog output.'';
+      };
+
+      passwordAuthentication = mkOption {
+        default = true;
+        description = ''Whether to enable password authentication.'';
+      };
+
+      publicKeyAuthentication = mkOption {
+        default = true;
+        description = ''Whether to enable public key authentication.'';
+      };
+
+      rootLogin = mkOption {
+        default = false;
+        description = ''Whether to enable remote root login.'';
+      };
+
+      loginShell = mkOption {
+        default = null;
+        description = ''
+          If non-null, override the default login shell with the
+          specified value.
+        '';
+        example = "/nix/store/xyz-bash-10.0/bin/bash10";
+      };
+
+      srpKeyExchange = mkOption {
+        default = false;
+        description = ''
+          Whether to enable SRP key exchange and user authentication.
+        '';
+      };
+
+      tcpForwarding = mkOption {
+        default = true;
+        description = ''Whether to enable TCP/IP forwarding.'';
+      };
+
+      x11Forwarding = mkOption {
+        default = true;
+        description = ''Whether to enable X11 forwarding.'';
+      };
+
+      subsystems = mkOption {
+        default = [ ["sftp" "${pkgs.lsh}/sbin/sftp-server"] ];
+        description = ''
+          List of subsystem-path pairs, where the head of the pair
+          denotes the subsystem name, and the tail denotes the path to
+          an executable implementing it.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    jobs.lshd =
+      { description = "GNU lshd SSH2 daemon";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        environment =
+          { LD_LIBRARY_PATH = config.system.nssModules.path; };
+
+        preStart =
+          ''
+            test -d /etc/lsh || mkdir -m 0755 -p /etc/lsh
+            test -d /var/spool/lsh || mkdir -m 0755 -p /var/spool/lsh
+
+            if ! test -f /var/spool/lsh/yarrow-seed-file
+            then
+                # XXX: It would be nice to provide feedback to the
+                # user when this fails, so that they can retry it
+                # manually.
+                ${lsh}/bin/lsh-make-seed --sloppy \
+                   -o /var/spool/lsh/yarrow-seed-file
+            fi
+
+            if ! test -f "${cfg.hostKey}"
+            then
+                ${lsh}/bin/lsh-keygen --server | \
+                ${lsh}/bin/lsh-writekey --server -o "${cfg.hostKey}"
+            fi
+          '';
+
+        exec = with cfg;
+          ''
+            ${lsh}/sbin/lshd --daemonic \
+              --password-helper="${lsh}/sbin/lsh-pam-checkpw" \
+              -p ${toString portNumber} \
+              ${if interfaces == [] then ""
+                else (concatStrings (map (i: "--interface=\"${i}\"")
+                                         interfaces))} \
+              -h "${hostKey}" \
+              ${if !syslog then "--no-syslog" else ""} \
+              ${if passwordAuthentication then "--password" else "--no-password" } \
+              ${if publicKeyAuthentication then "--publickey" else "--no-publickey" } \
+              ${if rootLogin then "--root-login" else "--no-root-login" } \
+              ${if loginShell != null then "--login-shell=\"${loginShell}\"" else "" } \
+              ${if srpKeyExchange then "--srp-keyexchange" else "--no-srp-keyexchange" } \
+              ${if !tcpForwarding then "--no-tcpip-forward" else "--tcpip-forward"} \
+              ${if x11Forwarding then "--x11-forward" else "--no-x11-forward" } \
+              --subsystems=${concatStringsSep ","
+                                              (map (pair: (head pair) + "=" +
+                                                          (head (tail pair)))
+                                                   subsystems)}
+          '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
new file mode 100644
index 00000000000..d57eef860d2
--- /dev/null
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -0,0 +1,338 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg  = config.services.openssh;
+  cfgc = config.programs.ssh;
+
+  nssModulesPath = config.system.nssModules.path;
+
+  permitRootLoginCheck = v:
+    v == "yes" ||
+    v == "without-password" ||
+    v == "forced-commands-only" ||
+    v == "no";
+
+  knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts);
+
+  knownHostsFile = pkgs.writeText "ssh_known_hosts" (
+    flip concatMapStrings knownHosts (h:
+      "${concatStringsSep "," h.hostNames} ${builtins.readFile h.publicKeyFile}"
+    )
+  );
+
+  userOptions = {
+
+    openssh.authorizedKeys = {
+      keys = mkOption {
+        type = types.listOf types.string;
+        default = [];
+        description = ''
+          A list of verbatim OpenSSH public keys that should be added to the
+          user's authorized keys. The keys are added to a file that the SSH
+          daemon reads in addition to the the user's authorized_keys file.
+          You can combine the <literal>keys</literal> and
+          <literal>keyFiles</literal> options.
+        '';
+      };
+
+      keyFiles = mkOption {
+        default = [];
+        description = ''
+          A list of files each containing one OpenSSH public key that should be
+          added to the user's authorized keys. The contents of the files are
+          read at build time and added to a file that the SSH daemon reads in
+          addition to the the user's authorized_keys file. You can combine the
+          <literal>keyFiles</literal> and <literal>keys</literal> options.
+        '';
+      };
+    };
+
+  };
+
+  authKeysFiles = let
+    mkAuthKeyFile = u: {
+      target = "ssh/authorized_keys.d/${u.name}";
+      mode = "0444";
+      source = pkgs.writeText "${u.name}-authorized_keys" ''
+        ${concatStringsSep "\n" u.openssh.authorizedKeys.keys}
+        ${concatMapStrings (f: builtins.readFile f + "\n") u.openssh.authorizedKeys.keyFiles}
+      '';
+    };
+    usersWithKeys = attrValues (flip filterAttrs config.users.extraUsers (n: u:
+      length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0
+    ));
+  in map mkAuthKeyFile usersWithKeys;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.openssh = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the OpenSSH secure shell daemon, which
+          allows secure remote logins.
+        '';
+      };
+
+      forwardX11 = mkOption {
+        default = cfgc.setXAuthLocation;
+        description = ''
+          Whether to allow X11 connections to be forwarded.
+        '';
+      };
+
+      allowSFTP = mkOption {
+        default = true;
+        description = ''
+          Whether to enable the SFTP subsystem in the SSH daemon.  This
+          enables the use of commands such as <command>sftp</command> and
+          <command>sshfs</command>.
+        '';
+      };
+
+      permitRootLogin = mkOption {
+        default = "without-password";
+        check = permitRootLoginCheck;
+        description = ''
+          Whether the root user can login using ssh. Valid values are
+          <literal>yes</literal>, <literal>without-password</literal>,
+          <literal>forced-commands-only</literal> or
+          <literal>no</literal>.
+        '';
+      };
+
+      gatewayPorts = mkOption {
+        default = "no";
+        description = ''
+          Specifies whether remote hosts are allowed to connect to
+          ports forwarded for the client.  See
+          <citerefentry><refentrytitle>sshd_config</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>.
+        '';
+      };
+
+      ports = mkOption {
+        default = [22];
+        description = ''
+          Specifies on which ports the SSH daemon listens.
+        '';
+      };
+
+      usePAM = mkOption {
+        default = true;
+        description = ''
+          Specifies whether the OpenSSH daemon uses PAM to authenticate
+          login attempts.
+        '';
+      };
+
+      passwordAuthentication = mkOption {
+        default = true;
+        description = ''
+          Specifies whether password authentication is allowed. Note
+          that setting this value to <literal>false</literal> is most
+          probably not going to have the desired effect unless
+          <literal>usePAM</literal> is disabled as well.
+        '';
+      };
+
+      challengeResponseAuthentication = mkOption {
+        default = true;
+        description = ''
+          Specifies whether challenge/response authentication is allowed.
+        '';
+      };
+
+      hostKeys = mkOption {
+        default =
+          [ { path = "/etc/ssh/ssh_host_dsa_key";
+              type = "dsa";
+              bits = 1024;
+            }
+            { path = "/etc/ssh/ssh_host_ecdsa_key";
+              type = "ecdsa";
+              bits = 521;
+            }
+          ];
+        description = ''
+          NixOS can automatically generate SSH host keys.  This option
+          specifies the path, type and size of each key.  See
+          <citerefentry><refentrytitle>ssh-keygen</refentrytitle>
+          <manvolnum>1</manvolnum></citerefentry> for supported types
+          and sizes.
+        '';
+      };
+
+      authorizedKeysFiles = mkOption {
+        default = [];
+        description = "Files from with authorized keys are read.";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "Verbatim contents of <filename>sshd_config</filename>.";
+      };
+
+      knownHosts = mkOption {
+        default = {};
+        type = types.loaOf types.optionSet;
+        description = ''
+          The set of system-wide known SSH hosts.
+        '';
+        example = [
+          {
+            hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ];
+            publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub;
+          }
+          {
+            hostNames = [ "myhost2" ];
+            publicKeyFile = ./pubkeys/myhost2_ssh_host_dsa_key.pub;
+          }
+        ];
+        options = {
+          hostNames = mkOption {
+            type = types.listOf types.string;
+            default = [];
+            description = ''
+              A list of host names and/or IP numbers used for accessing
+              the host's ssh service.
+            '';
+          };
+          publicKeyFile = mkOption {
+            description = ''
+              The path to the public key file for the host. The public
+              key file is read at build time and saved in the Nix store.
+              You can fetch a public key file from a running SSH server
+              with the <literal>ssh-keyscan</literal> command.
+            '';
+          };
+        };
+      };
+
+    };
+
+    users.extraUsers = mkOption {
+      options = [ userOptions ];
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = "sshd";
+        uid = config.ids.uids.sshd;
+        description = "SSH privilege separation user";
+        home = "/var/empty";
+      };
+
+    environment.etc = authKeysFiles ++ [
+      { source = "${pkgs.openssh}/etc/ssh/moduli";
+        target = "ssh/moduli";
+      }
+      { source = knownHostsFile;
+        target = "ssh/ssh_known_hosts";
+      }
+    ];
+
+    systemd.services.sshd =
+      { description = "SSH Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+
+        stopIfChanged = false;
+
+        path = [ pkgs.openssh pkgs.gawk ];
+
+        environment.LD_LIBRARY_PATH = nssModulesPath;
+        environment.LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p /etc/ssh
+
+            ${flip concatMapStrings cfg.hostKeys (k: ''
+              if ! [ -f "${k.path}" ]; then
+                  ssh-keygen -t "${k.type}" -b "${toString k.bits}" -f "${k.path}" -N ""
+              fi
+            '')}
+          '';
+
+        serviceConfig =
+          { ExecStart =
+              "${pkgs.openssh}/sbin/sshd " +
+              "-f ${pkgs.writeText "sshd_config" cfg.extraConfig}";
+            Restart = "always";
+            Type = "forking";
+            KillMode = "process";
+            PIDFile = "/run/sshd.pid";
+          };
+      };
+
+    networking.firewall.allowedTCPPorts = cfg.ports;
+
+    security.pam.services = optional cfg.usePAM { name = "sshd"; startSession = true; showMotd = true; };
+
+    services.openssh.authorizedKeysFiles =
+      [ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ];
+
+    services.openssh.extraConfig =
+      ''
+        PidFile /run/sshd.pid
+
+        Protocol 2
+
+        UsePAM ${if cfg.usePAM then "yes" else "no"}
+
+        AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
+        ${concatMapStrings (port: ''
+          Port ${toString port}
+        '') cfg.ports}
+
+        ${optionalString cfgc.setXAuthLocation ''
+            XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
+        ''}
+
+        ${if cfg.forwardX11 then ''
+          X11Forwarding yes
+        '' else ''
+          X11Forwarding no
+        ''}
+
+        ${optionalString cfg.allowSFTP ''
+          Subsystem sftp ${pkgs.openssh}/libexec/sftp-server
+        ''}
+
+        PermitRootLogin ${cfg.permitRootLogin}
+        GatewayPorts ${cfg.gatewayPorts}
+        PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"}
+        ChallengeResponseAuthentication ${if cfg.challengeResponseAuthentication then "yes" else "no"}
+
+        PrintMotd no # handled by pam_motd
+
+        AuthorizedKeysFile ${toString cfg.authorizedKeysFiles}
+
+        ${flip concatMapStrings cfg.hostKeys (k: ''
+          HostKey ${k.path}
+        '')}
+      '';
+
+    assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
+                    message = "cannot enable X11 forwarding without setting xauth location";}];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix
new file mode 100644
index 00000000000..fa8b7556de5
--- /dev/null
+++ b/nixos/modules/services/networking/supybot.nix
@@ -0,0 +1,88 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg  = config.services.supybot;
+
+in
+
+{
+
+  options = {
+
+    services.supybot = {
+
+      enable = mkOption {
+        default = false;
+        description = "Enable Supybot, an IRC bot";
+      };
+
+      stateDir = mkOption {
+        # Setting this to /var/lib/supybot caused useradd to fail
+        default = "/home/supybot";
+        description = "The root directory, logs and plugins are stored here";
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to a supybot config file. This can be generated by
+          running supybot-wizard.
+
+          Note: all paths should include the full path to the stateDir
+          directory (backup conf data logs logs/plugins plugins tmp web).
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.pythonPackages.limnoria ];
+
+    users.extraUsers = singleton {
+      name = "supybot";
+      uid = config.ids.uids.supybot;
+      group = "supybot";
+      description = "Supybot IRC bot user";
+      home = cfg.stateDir;
+      createHome = true;
+    };
+
+    users.extraGroups.supybot = {
+      name = "supybot";
+      gid = config.ids.gids.supybot;
+    };
+
+    systemd.services.supybot = {
+      description = "Supybot, an IRC bot";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.pythonPackages.limnoria ];
+      preStart = ''
+        cd ${cfg.stateDir}
+        mkdir -p backup conf data plugins logs/plugins tmp web
+        ln -sf ${cfg.configFile} supybot.cfg
+        # This needs to be created afresh every time
+        rm -f supybot.cfg.bak
+      '';
+
+      serviceConfig = {
+        ExecStart = "${pkgs.pythonPackages.limnoria}/bin/supybot ${cfg.stateDir}/supybot.cfg";
+        PIDFile = "/run/supybot.pid";
+        User = "supybot";
+        Group = "supybot";
+        UMask = "0007";
+        Restart = "on-abort";
+        StartLimitInterval = "5m";
+        StartLimitBurst = "1";
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix
new file mode 100644
index 00000000000..48cb884f246
--- /dev/null
+++ b/nixos/modules/services/networking/tcpcrypt.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking.tcpcrypt;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.tcpcrypt.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable opportunistic TCP encryption. If the other end
+        speaks Tcpcrypt, then your traffic will be encrypted; otherwise
+        it will be sent in clear text. Thus, Tcpcrypt alone provides no
+        guarantees -- it is best effort. If, however, a Tcpcrypt
+        connection is successful and any attackers that exist are
+        passive, then Tcpcrypt guarantees privacy.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton {
+      name = "tcpcryptd";
+      uid = config.ids.uids.tcpcryptd;
+      description = "tcpcrypt daemon user";
+    };
+
+    jobs.tcpcrypt = {
+      description = "tcpcrypt";
+
+      wantedBy = ["multi-user.target"];
+      after = ["network-interfaces.target"];
+
+      path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ];
+
+      preStart = ''
+        sysctl -n net.ipv4.tcp_ecn >/run/pre-tcpcrypt-ecn-state
+        sysctl -w net.ipv4.tcp_ecn=0
+
+        iptables -t raw -N nixos-tcpcrypt
+        iptables -t raw -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
+        iptables -t raw -I PREROUTING -j nixos-tcpcrypt
+
+        iptables -t mangle -N nixos-tcpcrypt
+        iptables -t mangle -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666
+        iptables -t mangle -I POSTROUTING -j nixos-tcpcrypt
+      '';
+
+      exec = "tcpcryptd -x 0x10";
+
+      postStop = ''
+        if [ -f /run/pre-tcpcrypt-ecn-state ]; then
+          sysctl -w net.ipv4.tcp_ecn=$(cat /run/pre-tcpcrypt-ecn-state)
+        fi
+
+        iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true
+        iptables -t raw -D PREROUTING -j nixos-tcpcrypt || true
+
+        iptables -t raw -F nixos-tcpcrypt || true
+        iptables -t raw -X nixos-tcpcrypt || true
+
+        iptables -t mangle -F nixos-tcpcrypt || true
+        iptables -t mangle -X nixos-tcpcrypt || true
+      '';
+    };
+  };
+
+}
diff --git a/nixos/modules/services/networking/tftpd.nix b/nixos/modules/services/networking/tftpd.nix
new file mode 100644
index 00000000000..37935496c59
--- /dev/null
+++ b/nixos/modules/services/networking/tftpd.nix
@@ -0,0 +1,43 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tftpd.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the anonymous FTP user.
+      '';
+    };
+
+    services.tftpd.path = mkOption {
+      default = "/home/tftp";
+      description = ''
+        Where the tftp server files are stored
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.tftpd.enable {
+
+    services.xinetd.enable = true;
+
+    services.xinetd.services = singleton
+      { name = "tftp";
+        protocol = "udp";
+        server = "${pkgs.netkittftp}/sbin/in.tftpd";
+        serverArgs = "${config.services.tftpd.path}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
new file mode 100644
index 00000000000..fb75b4ed069
--- /dev/null
+++ b/nixos/modules/services/networking/unbound.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.unbound;
+
+  username = "unbound";
+
+  stateDir = "/var/lib/unbound";
+
+  access = concatMapStrings (x: "  access-control: ${x} allow\n") cfg.allowedAccess;
+
+  interfaces = concatMapStrings (x: "  interface: ${x}\n") cfg.interfaces;
+
+  forward = optionalString (length cfg.forwardAddresses != 0)
+    "forward-zone:\n  name: .\n" +
+    concatMapStrings (x: "  forward-addr: ${x}\n") cfg.forwardAddresses;
+
+  confFile = pkgs.writeText "unbound.conf"
+    ''
+      server:
+        directory: "${stateDir}"
+        username: ${username}
+        # make sure unbound can access entropy from inside the chroot.
+        # e.g. on linux the use these commands (on BSD, devfs(8) is used):
+        #      mount --bind -n /dev/random /etc/unbound/dev/random
+        # and  mount --bind -n /dev/log /etc/unbound/dev/log
+        chroot: "${stateDir}"
+        # logfile: "${stateDir}/unbound.log"  #uncomment to use logfile.
+        pidfile: "${stateDir}/unbound.pid"
+        verbosity: 1      # uncomment and increase to get more logging.
+        # listen on all interfaces, answer queries from the local subnet.
+      ${interfaces}
+      ${access}
+      ${forward}
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.unbound = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the Unbound domain name server.
+        ";
+      };
+
+      allowedAccess = mkOption {
+        default = ["127.0.0.0/24"];
+        description = "
+          What networks are allowed to use us as a resolver.
+        ";
+      };
+
+      interfaces = mkOption {
+        default = [ "127.0.0.0" "::1" ];
+        description = "
+          What addresses the server should listen to.
+        ";
+      };
+
+      forwardAddresses = mkOption {
+        default = [ ];
+        description = "
+          What servers to forward the queries to.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          Extra unbound config
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.unbound.enable {
+    environment.systemPackages = [ pkgs.unbound ];
+
+    users.extraUsers = singleton
+      { name = username;
+        uid = config.ids.uids.unbound;
+        description = "unbound daemon user";
+        home = "/tmp";
+      };
+
+    jobs.unbound =
+      { description = "Unbound name server job";
+
+        preStart =
+          ''
+            ${pkgs.coreutils}/bin/mkdir -p ${stateDir}
+          '';
+
+        daemonType = "fork";
+
+        exec = "${pkgs.unbound}/sbin/unbound -c ${confFile}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
new file mode 100644
index 00000000000..1b2432401de
--- /dev/null
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -0,0 +1,137 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.vsftpd;
+
+  inherit (pkgs) vsftpd;
+
+  yesNoOption = p : name :
+    "${name}=${if p then "YES" else "NO"}";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.vsftpd = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the vsftpd FTP server.";
+      };
+
+      anonymousUser = mkOption {
+        default = false;
+        description = "Whether to enable the anonymous FTP user.";
+      };
+
+      anonymousUserHome = mkOption {
+        default = "/home/ftp";
+        description = "Path to anonymous user data.";
+      };
+
+      localUsers = mkOption {
+        default = false;
+        description = "Whether to enable FTP for local users.";
+      };
+
+      writeEnable = mkOption {
+        default = false;
+        description = "Whether any write activity is permitted to users.";
+      };
+
+      anonymousUploadEnable = mkOption {
+        default = false;
+        description = "Whether any uploads are permitted to anonymous users.";
+      };
+
+      anonymousMkdirEnable = mkOption {
+        default = false;
+        description = "Whether mkdir is permitted to anonymous users.";
+      };
+
+      chrootlocalUser = mkOption {
+        default = false;
+        description = "Whether local users are confined to their home directory.";
+      };
+
+      userlistEnable = mkOption {
+        default = false;
+        description = "Whether users are included.";
+      };
+
+      userlistDeny = mkOption {
+        default = false;
+        description = "Whether users are excluded.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers =
+      [ { name = "vsftpd";
+          uid = config.ids.uids.vsftpd;
+          description = "VSFTPD user";
+          home = "/homeless-shelter";
+        }
+      ] ++ pkgs.lib.optional cfg.anonymousUser
+        { name = "ftp";
+          uid = config.ids.uids.ftp;
+          group = "ftp";
+          description = "Anonymous FTP user";
+          home = cfg.anonymousUserHome;
+        };
+
+    users.extraGroups = singleton
+      { name = "ftp";
+        gid = config.ids.gids.ftp;
+      };
+
+    jobs.vsftpd =
+      { description = "vsftpd server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        preStart =
+          ''
+            # !!! Why isn't this generated in the normal way?
+            cat > /etc/vsftpd.conf <<EOF
+            ${yesNoOption cfg.anonymousUser "anonymous_enable"}
+            ${yesNoOption cfg.localUsers "local_enable"}
+            ${yesNoOption cfg.writeEnable "write_enable"}
+            ${yesNoOption cfg.anonymousUploadEnable "anon_upload_enable"}
+            ${yesNoOption cfg.anonymousMkdirEnable "anon_mkdir_write_enable"}
+            ${yesNoOption cfg.chrootlocalUser "chroot_local_user"}
+            ${yesNoOption cfg.userlistEnable "userlist_enable"}
+            ${yesNoOption cfg.userlistDeny "userlist_deny"}
+            background=NO
+            listen=YES
+            nopriv_user=vsftpd
+            secure_chroot_dir=/var/empty
+            EOF
+
+            ${if cfg.anonymousUser then ''
+              mkdir -p -m 555 ${cfg.anonymousUserHome}
+              chown -R ftp:ftp ${cfg.anonymousUserHome}
+            '' else ""}
+          '';
+
+        exec = "${vsftpd}/sbin/vsftpd /etc/vsftpd.conf";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/wakeonlan.nix b/nixos/modules/services/networking/wakeonlan.nix
new file mode 100644
index 00000000000..1fc54986b16
--- /dev/null
+++ b/nixos/modules/services/networking/wakeonlan.nix
@@ -0,0 +1,56 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  interfaces = config.services.wakeonlan.interfaces;
+
+  ethtool = "${pkgs.ethtool}/sbin/ethtool";
+
+  passwordParameter = password : if (password == "") then "" else
+    "sopass ${password}";
+
+  methodParameter = {method, password} :
+    if method == "magicpacket" then "wol g"
+    else if method == "password" then "wol s so ${passwordParameter password}"
+    else throw "Wake-On-Lan method not supported";
+
+  line = { interface, method ? "magicpacket", password ? "" }: ''
+    ${ethtool} -s ${interface} ${methodParameter {inherit method password;}}
+  '';
+
+  concatStrings = fold (x: y: x + y) "";
+  lines = concatStrings (map (l: line l) interfaces);
+
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.wakeonlan.interfaces = mkOption {
+      default = [ ];
+      example = [
+        {
+          interface = "eth0";
+          method = "password";
+          password = "00:11:22:33:44:55";
+        }
+      ];
+      description = ''
+        Interfaces where to enable Wake-On-LAN, and how. Two methods available:
+        "magickey" and "password". The password has the shape of six bytes
+        in hexadecimal separated by a colon each. For more information,
+        check the ethtool manual.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config.powerManagement.powerDownCommands = lines;
+
+}
diff --git a/nixos/modules/services/networking/websockify.nix b/nixos/modules/services/networking/websockify.nix
new file mode 100644
index 00000000000..12042bbad6c
--- /dev/null
+++ b/nixos/modules/services/networking/websockify.nix
@@ -0,0 +1,54 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let cfg = config.services.networking.websockify; in {
+  options = {
+    services.networking.websockify = {
+      enable = mkOption {  
+        description = "Whether to enable websockify to forward websocket connections to TCP connections.";
+
+        default = false;   
+
+        type = types.bool; 
+      };
+
+      sslCert = mkOption {
+        description = "Path to the SSL certificate.";
+        type = types.path;
+      };
+
+      sslKey = mkOption {
+        description = "Path to the SSL key.";
+        default = cfg.sslCert;
+        defaultText = "config.services.networking.websockify.sslCert";
+        type = types.path;
+      };
+
+      portMap = mkOption {
+        description = "Ports to map by default.";
+        default = {};
+        type = types.attrsOf types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services."websockify@" = {
+      description = "Service to forward websocket connections to TCP connections (from port:to port %I)";
+      script = ''
+        IFS=':' read -a array <<< "$1"
+        ${pkgs.pythonPackages.websockify}/bin/websockify --ssl-only \
+          --cert=${cfg.sslCert} --key=${cfg.sslKey} 0.0.0.0:''${array[0]} 0.0.0.0:''${array[1]}
+      '';
+      scriptArgs = "%i";
+    };
+
+    systemd.targets."default-websockify" = {
+      description = "Target to start all default websockify@ services";
+      unitConfig."X-StopOnReconfiguration" = true;
+      wants = mapAttrsToList (name: value: "websockify@${name}:${toString value}.service") cfg.portMap;
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/wicd.nix b/nixos/modules/services/networking/wicd.nix
new file mode 100644
index 00000000000..8e012273216
--- /dev/null
+++ b/nixos/modules/services/networking/wicd.nix
@@ -0,0 +1,41 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.wicd.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to start <command>wicd</command>. Wired and
+        wireless network configurations can then be managed by
+        wicd-client.
+      '';
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.networking.wicd.enable {
+
+    environment.systemPackages = [pkgs.wicd];
+
+    jobs.wicd =
+      { startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        script =
+          "${pkgs.wicd}/sbin/wicd -f";
+      };
+
+    services.dbus.enable = true;
+    services.dbus.packages = [pkgs.wicd];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
new file mode 100644
index 00000000000..dca398dd8be
--- /dev/null
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -0,0 +1,136 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking.wireless;
+  configFile = "/etc/wpa_supplicant.conf";
+
+  ifaces =
+    cfg.interfaces ++
+    optional (config.networking.WLANInterface != "") config.networking.WLANInterface;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.WLANInterface = mkOption {
+      default = "";
+      description = "Obsolete. Use <option>networking.wireless.interfaces</option> instead.";
+    };
+
+    networking.wireless = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to start <command>wpa_supplicant</command> to scan for
+          and associate with wireless networks.  Note: NixOS currently
+          does not generate <command>wpa_supplicant</command>'s
+          configuration file, <filename>${configFile}</filename>.  You
+          should edit this file yourself to define wireless networks,
+          WPA keys and so on (see
+          <citerefentry><refentrytitle>wpa_supplicant.conf</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry>).
+        '';
+      };
+
+      interfaces = mkOption {
+        default = [];
+        example = [ "wlan0" "wlan1" ];
+        description = ''
+          The interfaces <command>wpa_supplicant</command> will use.  If empty, it will
+          automatically use all wireless interfaces. (Note that auto-detection is currently
+          broken on Linux 3.4.x kernels. See http://github.com/NixOS/nixos/issues/10 for
+          further details.)
+        '';
+      };
+
+      driver = mkOption {
+        default = "nl80211,wext";
+        description = "Force a specific wpa_supplicant driver.";
+      };
+
+      userControlled = {
+        enable = mkOption {
+          default = false;
+          description = ''
+            Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
+            This is useful for laptop users that switch networks a lot.
+
+            When you want to use this, make sure ${configFile} doesn't exist.
+            It will be created for you.
+
+            Currently it is also necessary to explicitly specify networking.wireless.interfaces.
+          '';
+        };
+
+        group = mkOption {
+          default = "wheel";
+          example = "network";
+          type = types.string;
+          description = "Members of this group can control wpa_supplicant.";
+        };
+      };
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages =  [ pkgs.wpa_supplicant ];
+
+    services.dbus.packages = [ pkgs.wpa_supplicant ];
+
+    jobs.wpa_supplicant =
+      { description = "WPA Supplicant";
+
+        wantedBy = [ "network.target" ];
+        after = [ "systemd-udev-settle.service" ];
+
+        path = [ pkgs.wpa_supplicant ];
+
+        preStart = ''
+          touch -a ${configFile}
+          chmod 600 ${configFile}
+        '' + optionalString cfg.userControlled.enable ''
+          if [ ! -s ${configFile} ]; then
+            echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=${cfg.userControlled.group}" >> ${configFile}
+            echo "update_config=1" >> ${configFile}
+          fi
+        '';
+
+        script =
+          ''
+            ${if ifaces == [] then ''
+              for i in $(cd /sys/class/net && echo *); do
+                DEVTYPE=
+                source /sys/class/net/$i/uevent
+                if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then
+                  ifaces="$ifaces''${ifaces:+ -N} -i$i"
+                fi
+              done
+            '' else ''
+              ifaces="${concatStringsSep " -N " (map (i: "-i${i}") ifaces)}"
+            ''}
+            exec wpa_supplicant -s -u -D${cfg.driver} -c ${configFile} $ifaces
+          '';
+      };
+
+    powerManagement.resumeCommands =
+      ''
+        ${config.systemd.package}/bin/systemctl try-restart wpa_supplicant
+      '';
+
+    assertions = [{ assertion = !cfg.userControlled.enable || cfg.interfaces != [];
+                    message = "user controlled wpa_supplicant needs explicit networking.wireless.interfaces";}];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/xinetd.nix b/nixos/modules/services/networking/xinetd.nix
new file mode 100644
index 00000000000..626183b810f
--- /dev/null
+++ b/nixos/modules/services/networking/xinetd.nix
@@ -0,0 +1,158 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xinetd;
+
+  inherit (pkgs) xinetd;
+
+  configFile = pkgs.writeText "xinetd.conf"
+    ''
+      defaults
+      {
+        log_type       = SYSLOG daemon info
+        log_on_failure = HOST
+        log_on_success = PID HOST DURATION EXIT
+        ${cfg.extraDefaults}
+      }
+
+      ${concatMapStrings makeService cfg.services}
+    '';
+
+  makeService = srv:
+    ''
+      service ${srv.name}
+      {
+        protocol    = ${srv.protocol}
+        ${optionalString srv.unlisted "type        = UNLISTED"}
+        ${optionalString (srv.flags != "") "flags = ${srv.flags}"}
+        socket_type = ${if srv.protocol == "udp" then "dgram" else "stream"}
+        ${if srv.port != 0 then "port        = ${toString srv.port}" else ""}
+        wait        = ${if srv.protocol == "udp" then "yes" else "no"}
+        user        = ${srv.user}
+        server      = ${srv.server}
+        ${optionalString (srv.serverArgs != "") "server_args = ${srv.serverArgs}"}
+        ${srv.extraConfig}
+      }
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xinetd.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the xinetd super-server daemon.
+      '';
+    };
+
+    services.xinetd.extraDefaults = mkOption {
+      default = "";
+      type = types.string;
+      description = ''
+        Additional configuration lines added to the default section of xinetd's configuration.
+      '';
+    };
+
+    services.xinetd.services = mkOption {
+      default = [];
+      description = ''
+        A list of services provided by xinetd.
+      '';
+
+      type = types.listOf types.optionSet;
+
+      options = {
+
+        name = mkOption {
+          type = types.string;
+          example = "login";
+          description = "Name of the service.";
+        };
+
+        protocol = mkOption {
+          type = types.string;
+          default = "tcp";
+          description =
+            "Protocol of the service.  Usually <literal>tcp</literal> or <literal>udp</literal>.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 0;
+          example = 123;
+          description = "Port number of the service.";
+        };
+
+        user = mkOption {
+          type = types.string;
+          default = "nobody";
+          description = "User account for the service";
+        };
+
+        server = mkOption {
+          type = types.string;
+          example = "/foo/bin/ftpd";
+          description = "Path of the program that implements the service.";
+        };
+
+        serverArgs = mkOption {
+          type = types.string;
+          default = "";
+          description = "Command-line arguments for the server program.";
+        };
+
+        flags = mkOption {
+          type = types.string;
+          default = "";
+          description = "";
+        };
+
+        unlisted = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether this server is listed in
+            <filename>/etc/services</filename>.  If so, the port
+            number can be omitted.
+          '';
+        };
+
+        extraConfig = mkOption {
+          type = types.string;
+          default = "";
+          description = "Extra configuration-lines added to the section of the service.";
+        };
+
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    jobs.xinetd =
+      { description = "xinetd server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        path = [ xinetd ];
+
+        exec = "xinetd -syslog daemon -dontfork -stayalive -f ${configFile}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
new file mode 100644
index 00000000000..c9a4a9087e5
--- /dev/null
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -0,0 +1,223 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) cups;
+
+  cfg = config.services.printing;
+
+  additionalBackends = pkgs.runCommand "additional-cups-backends" { }
+    ''
+      mkdir -p $out
+      if [ ! -e ${pkgs.cups}/lib/cups/backend/smb ]; then
+        mkdir -p $out/lib/cups/backend
+        ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb
+      fi
+
+      # Provide support for printing via HTTPS.
+      if [ ! -e ${pkgs.cups}/lib/cups/backend/https ]; then
+        mkdir -p $out/lib/cups/backend
+        ln -sv ${pkgs.cups}/lib/cups/backend/ipp $out/lib/cups/backend/https
+      fi
+
+      # Import filter configuration from Ghostscript.
+      mkdir -p $out/share/cups/mime/
+      ln -v -s "${pkgs.ghostscript}/etc/cups/"* $out/share/cups/mime/
+    '';
+
+  # Here we can enable additional backends, filters, etc. that are not
+  # part of CUPS itself, e.g. the SMB backend is part of Samba.  Since
+  # we can't update ${cups}/lib/cups itself, we create a symlink tree
+  # here and add the additional programs.  The ServerBin directive in
+  # cupsd.conf tells cupsd to use this tree.
+  bindir = pkgs.buildEnv {
+    name = "cups-progs";
+    paths = cfg.drivers;
+    pathsToLink = [ "/lib/cups" "/share/cups" "/bin" ];
+    postBuild = cfg.bindirCmds;
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+    services.printing = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable printing support through the CUPS daemon.
+        '';
+      };
+
+      bindirCmds = mkOption {
+        default = "";
+        description = ''
+          Additional commands executed while creating the directory
+          containing the CUPS server binaries.
+        '';
+      };
+
+      cupsdConf = mkOption {
+        default = "";
+        example =
+          ''
+            BrowsePoll cups.example.com
+            LogLevel debug
+          '';
+        description = ''
+          The contents of the configuration file of the CUPS daemon
+          (<filename>cupsd.conf</filename>).
+        '';
+      };
+
+      drivers = mkOption {
+        example = [ pkgs.splix ];
+        description = ''
+          CUPS drivers (CUPS, gs and samba are added unconditionally).
+        '';
+      };
+
+      tempDir = mkOption {
+        default = "/tmp";
+        example = "/tmp/cups";
+        description = ''
+          CUPSd temporary directory.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.printing.enable {
+
+    users.extraUsers = singleton
+      { name = "cups";
+        uid = config.ids.uids.cups;
+        group = "lp";
+        description = "CUPS printing services";
+      };
+
+    environment.systemPackages = [ cups ];
+
+    services.dbus.packages = [ cups ];
+
+    # Cups uses libusb to talk to printers, and does not use the
+    # linux kernel driver. If the driver is not in a black list, it
+    # gets loaded, and then cups cannot access the printers.
+    boot.blacklistedKernelModules = [ "usblp" ];
+
+    systemd.services.cupsd =
+      { description = "CUPS Printing Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network-interfaces.target" ];
+
+        path = [ cups ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p /etc/cups
+            mkdir -m 0700 -p /var/cache/cups
+            mkdir -m 0700 -p /var/spool/cups
+            mkdir -m 0755 -p ${cfg.tempDir}
+          '';
+
+        serviceConfig.Type = "forking";
+        serviceConfig.ExecStart = "@${cups}/sbin/cupsd cupsd -c ${pkgs.writeText "cupsd.conf" cfg.cupsdConf}";
+      };
+
+    services.printing.drivers =
+      [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends pkgs.perl pkgs.coreutils pkgs.gnused ];
+
+    services.printing.cupsdConf =
+      ''
+        LogLevel info
+
+        SystemGroup root
+
+        Listen localhost:631
+        Listen /var/run/cups/cups.sock
+
+        # Note: we can't use ${cups}/etc/cups as the ServerRoot, since
+        # CUPS will write in the ServerRoot when e.g. adding new printers
+        # through the web interface.
+        ServerRoot /etc/cups
+
+        ServerBin ${bindir}/lib/cups
+        DataDir ${bindir}/share/cups
+
+        SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin:${bindir}/sbin
+
+        AccessLog syslog
+        ErrorLog syslog
+        PageLog syslog
+
+        TempDir ${cfg.tempDir}
+
+        # User and group used to run external programs, including
+        # those that actually send the job to the printer.  Note that
+        # Udev sets the group of printer devices to `lp', so we want
+        # these programs to run as `lp' as well.
+        User cups
+        Group lp
+
+        Browsing On
+        BrowseOrder allow,deny
+        BrowseAllow @LOCAL
+
+        DefaultAuthType Basic
+
+        <Location />
+          Order allow,deny
+          Allow localhost
+        </Location>
+
+        <Location /admin>
+          Order allow,deny
+          Allow localhost
+        </Location>
+
+        <Location /admin/conf>
+          AuthType Basic
+          Require user @SYSTEM
+          Order allow,deny
+          Allow localhost
+        </Location>
+
+        <Policy default>
+          <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job>
+            Require user @OWNER @SYSTEM
+            Order deny,allow
+          </Limit>
+
+          <Limit Pause-Printer Resume-Printer Set-Printer-Attributes Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Add-Printer CUPS-Delete-Printer CUPS-Add-Class CUPS-Delete-Class CUPS-Accept-Jobs CUPS-Reject-Jobs CUPS-Set-Default>
+            AuthType Basic
+            Require user @SYSTEM
+            Order deny,allow
+          </Limit>
+
+          <Limit Cancel-Job CUPS-Authenticate-Job>
+            Require user @OWNER @SYSTEM
+            Order deny,allow
+          </Limit>
+
+          <Limit All>
+            Order deny,allow
+          </Limit>
+        </Policy>
+      '';
+
+    # Allow CUPS to receive IPP printer announcements via UDP.
+    networking.firewall.allowedUDPPorts = [ 631 ];
+
+  };
+}
diff --git a/nixos/modules/services/scheduling/atd.nix b/nixos/modules/services/scheduling/atd.nix
new file mode 100644
index 00000000000..88bec2cb2f3
--- /dev/null
+++ b/nixos/modules/services/scheduling/atd.nix
@@ -0,0 +1,111 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.atd;
+
+  inherit (pkgs) at;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.atd.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the `at' daemon, a command scheduler.
+      '';
+    };
+
+    services.atd.allowEveryone = mkOption {
+      default = false;
+      description = ''
+        Whether to make /var/spool/at{jobs,spool} writeable
+        by everyone (and sticky).  This is normally not needed since
+        the `at' commands are setuid/setgid `atd'.
+     '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    security.setuidOwners = map (program: {
+      inherit program;
+      owner = "atd";
+      group = "atd";
+      setuid = true;
+      setgid = true;
+    }) [ "at" "atq" "atrm" "batch" ];
+
+    environment.systemPackages = [ at ];
+
+    security.pam.services = [ { name = "atd"; } ];
+
+    users.extraUsers = singleton
+      { name = "atd";
+        uid = config.ids.uids.atd;
+        description = "atd user";
+        home = "/var/empty";
+      };
+
+    users.extraGroups = singleton
+      { name = "atd";
+        gid = config.ids.gids.atd;
+      };
+
+    jobs.atd =
+      { description = "Job Execution Daemon (atd)";
+
+        startOn = "stopped udevtrigger";
+
+        path = [ at ];
+
+        preStart =
+          ''
+            # Snippets taken and adapted from the original `install' rule of
+            # the makefile.
+
+            # We assume these values are those actually used in Nixpkgs for
+            # `at'.
+            spooldir=/var/spool/atspool
+            jobdir=/var/spool/atjobs
+            etcdir=/etc/at
+
+            for dir in "$spooldir" "$jobdir" "$etcdir"; do
+              if [ ! -d "$dir" ]; then
+                  mkdir -p "$dir"
+                  chown atd:atd "$dir"
+              fi
+            done
+            chmod 1770 "$spooldir" "$jobdir"
+            ${if cfg.allowEveryone then ''chmod a+rwxt "$spooldir" "$jobdir" '' else ""}
+            if [ ! -f "$etcdir"/at.deny ]; then
+                touch "$etcdir"/at.deny
+                chown root:atd "$etcdir"/at.deny
+                chmod 640 "$etcdir"/at.deny
+            fi
+            if [ ! -f "$jobdir"/.SEQ ]; then
+                touch "$jobdir"/.SEQ
+                chown atd:atd "$jobdir"/.SEQ
+                chmod 600 "$jobdir"/.SEQ
+            fi
+          '';
+
+        exec = "atd";
+
+        daemonType = "fork";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/scheduling/cron.nix b/nixos/modules/services/scheduling/cron.nix
new file mode 100644
index 00000000000..e14f03fb1e8
--- /dev/null
+++ b/nixos/modules/services/scheduling/cron.nix
@@ -0,0 +1,111 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (config.services) jobsTags;
+
+  # Put all the system cronjobs together.
+  systemCronJobsFile = pkgs.writeText "system-crontab"
+    ''
+      SHELL=${pkgs.bash}/bin/bash
+      PATH=${config.system.path}/bin:${config.system.path}/sbin
+      MAILTO="${config.services.cron.mailto}"
+      NIX_CONF_DIR=/etc/nix
+      ${pkgs.lib.concatStrings (map (job: job + "\n") config.services.cron.systemCronJobs)}
+    '';
+
+  # Vixie cron requires build-time configuration for the sendmail path.
+  cronNixosPkg = pkgs.cron.override {
+    # The mail.nix nixos module, if there is any local mail system enabled,
+    # should have sendmail in this path.
+    sendmailPath = "/var/setuid-wrappers/sendmail";
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.cron = {
+
+      enable = mkOption {
+        default = true;
+        description = "Whether to enable the `vixie cron' daemon.";
+      };
+
+      mailto = mkOption {
+        default = "";
+        description = " The job output will be mailed to this email address. ";
+      };
+
+      systemCronJobs = mkOption {
+        default = [];
+        example = [
+          "* * * * *  test   ls -l / > /tmp/cronout 2>&1"
+          "* * * * *  eelco  echo Hello World > /home/eelco/cronout"
+        ];
+        description = ''
+          A list of Cron jobs to be appended to the system-wide
+          crontab.  See the manual page for crontab for the expected
+          format. If you want to get the results mailed you must setuid
+          sendmail. See <option>security.setuidOwners</option>
+
+          If neither /var/cron/cron.deny nor /var/cron/cron.allow exist only root
+          will is allowed to have its own crontab file. The /var/cron/cron.deny file
+          is created automatically for you. So every user can use a crontab.
+
+          Many nixos modules set systemCronJobs, so if you decide to disable vixie cron
+          and enable another cron daemon, you may want it to get its system crontab
+          based on systemCronJobs.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.cron.enable {
+
+    environment.etc = singleton
+      # The system-wide crontab.
+      { source = systemCronJobsFile;
+        target = "crontab";
+        mode = "0600"; # Cron requires this.
+      };
+
+    security.setuidPrograms = [ "crontab" ];
+
+    environment.systemPackages = [ cronNixosPkg ];
+
+    jobs.cron =
+      { description = "Cron Daemon";
+
+        startOn = "startup";
+
+        path = [ cronNixosPkg ];
+
+        preStart =
+          ''
+            mkdir -m 710 -p /var/cron
+
+            # By default, allow all users to create a crontab.  This
+            # is denoted by the existence of an empty cron.deny file.
+            if ! test -e /var/cron/cron.allow -o -e /var/cron/cron.deny; then
+                touch /var/cron/cron.deny
+            fi
+          '';
+
+        exec = "cron -n";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix
new file mode 100644
index 00000000000..95ff918eb6d
--- /dev/null
+++ b/nixos/modules/services/scheduling/fcron.nix
@@ -0,0 +1,126 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.fcron;
+
+  queuelen = if cfg.queuelen == "" then "" else "-q ${toString cfg.queuelen}";
+
+  systemCronJobs =
+    ''
+      SHELL=${pkgs.bash}/bin/bash
+      PATH=${config.system.path}/bin:${config.system.path}/sbin
+      MAILTO="${config.services.cron.mailto}"
+      NIX_CONF_DIR=/etc/nix
+      ${pkgs.lib.concatStrings (map (job: job + "\n") config.services.cron.systemCronJobs)}
+    '';
+
+  allowdeny = target: users:
+    { source = pkgs.writeText "fcron.${target}" (concatStringsSep "\n" users);
+      target = "fcron.${target}";
+      mode = "600"; # fcron has some security issues.. So I guess this is most safe
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fcron = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the `fcron' daemon.";
+      };
+
+      allow = mkOption {
+        default = [ "all" ];
+        description = ''
+          Users allowed to use fcrontab and fcrondyn (one name per line, "all" for everyone).
+        '';
+      };
+
+      deny = mkOption {
+        default = [];
+        description = "Users forbidden from using fcron.";
+      };
+
+      maxSerialJobs = mkOption {
+        default = 1;
+        description = "Maximum number of serial jobs which can run simultaneously.";
+      };
+
+      queuelen = mkOption {
+        default = "";
+        description = "Number of jobs the serial queue and the lavg queue can contain - empty to net set this number (-q)";
+      };
+
+      systab = mkOption {
+        default = "";
+        description = ''The "system" crontab contents.'';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.fcron.systab = systemCronJobs;
+
+    environment.etc =
+      [ (allowdeny "allow" (cfg.allow))
+        (allowdeny "deny" cfg.deny)
+        # see man 5 fcron.conf
+        { source = pkgs.writeText "fcon.conf" ''
+            fcrontabs   =       /var/spool/fcron
+            pidfile     =       /var/run/fcron.pid
+            fifofile    =       /var/run/fcron.fifo
+            fcronallow  =       /etc/fcron.allow
+            fcrondeny   =       /etc/fcron.deny
+            shell       =       /bin/sh
+            sendmail    =       /var/setuid-wrappers/sendmail
+            editor      =       /run/current-system/sw/bin/vi
+          '';
+          target = "fcron.conf";
+          mode = "0600"; # max allowed is 644
+        }
+      ];
+
+    environment.systemPackages = [ pkgs.fcron ];
+
+    security.setuidPrograms = [ "fcrontab" ];
+
+    jobs.fcron =
+      { description = "fcron daemon";
+
+        startOn = "startup";
+
+        after = [ "local-fs.target" ];
+
+        environment =
+          { PATH = "/run/current-system/sw/bin";
+          };
+
+        preStart =
+          ''
+            ${pkgs.coreutils}/bin/mkdir -m 0700 -p /var/spool/fcron
+            # load system crontab file
+            ${pkgs.fcron}/bin/fcrontab -u systab ${pkgs.writeText "systab" cfg.systab}
+          '';
+
+        daemonType = "fork";
+
+        exec = "${pkgs.fcron}/sbin/fcron -m ${toString cfg.maxSerialJobs} ${queuelen}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
new file mode 100644
index 00000000000..6dfabc7e305
--- /dev/null
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -0,0 +1,115 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.elasticsearch;
+
+  es_home = "/var/lib/elasticsearch";
+
+  configFile = pkgs.writeText "elasticsearch.yml" ''
+    network.host: ${cfg.host}
+    network.port: ${cfg.port}
+    network.tcp.port: ${cfg.tcp_port}
+    cluster.name: ${cfg.cluster_name}
+    ${cfg.extraConf}
+  '';
+
+in {
+
+  ###### interface
+
+  options.services.elasticsearch = {
+    enable = mkOption {
+      description = "Whether to enable elasticsearch";
+      default = false;
+      type = types.uniq types.bool;
+    };
+
+    host = mkOption {
+      description = "Elasticsearch listen address";
+      default = "127.0.0.1";
+      types = type.uniq types.string;
+    };
+
+    port = mkOption {
+      description = "Elasticsearch port to listen for HTTP traffic";
+      default = "9200";
+      types = type.uniq types.string;
+    };
+
+    tcp_port = mkOption {
+      description = "Elasticsearch port for the node to node communication";
+      default = "9300";
+      types = type.uniq types.string;
+    };
+
+    cluster_name = mkOption {
+      description = "Elasticsearch name that identifies your cluster for auto-discovery";
+      default = "elasticsearch";
+      types = type.uniq types.string;
+    };
+
+    extraConf = mkOption {
+      description = "Extra configuration for elasticsearch";
+      default = "";
+      types = type.uniq types.string;
+      example = ''
+        node.name: "elasticsearch"
+        node.master: true
+        node.data: false
+        index.number_of_shards: 5
+        index.number_of_replicas: 1
+      '';
+    };
+
+    logging = mkOption {
+      description = "Elasticsearch logging configuration";
+      default = ''
+        rootLogger: DEBUG, console
+        logger:
+          action: DEBUG
+          com.amazonaws: WARN
+        appender:
+          console:
+            type: console
+            layout:
+              type: consolePattern
+              conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n"
+      '';
+      types = type.uniq types.string;
+    };
+  };
+
+  ###### 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 = mkIf cfg.enable {
+      description = "Elasticsearch daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = { ES_HOME = es_home; };
+      serviceConfig = {
+        ExecStart = "${pkgs.elasticsearch}/bin/elasticsearch -f -Des.path.conf=/etc/elasticsearch";
+        User = "elasticsearch";
+      };
+    };
+
+    environment.systemPackages = [ pkgs.elasticsearch ];
+
+    users.extraUsers = singleton {
+      name = "elasticsearch";
+      uid = config.ids.uids.elasticsearch;
+      description = "Elasticsearch daemon user";
+      home = es_home;
+      createHome = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix
new file mode 100644
index 00000000000..5ccb4927fcb
--- /dev/null
+++ b/nixos/modules/services/security/clamav.nix
@@ -0,0 +1,80 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  clamavUser = "clamav";
+  stateDir = "/var/lib/clamav";
+  clamavGroup = clamavUser;
+  cfg = config.services.clamav;
+in
+{
+  ###### interface
+
+  options = {
+
+    services.clamav = {
+      updater = {
+	enable = mkOption {
+	  default = false;
+	  description = ''
+	    Whether to enable automatic ClamAV virus definitions database updates.
+	  '';
+	};
+
+	frequency = mkOption {
+	  default = 12;
+	  description = ''
+	    Number of database checks per day.
+	  '';
+	};
+
+	config = mkOption {
+	  default = "";
+	  description = ''
+	    Extra configuration for freshclam. Contents will be added verbatim to the
+	    configuration file.
+	  '';
+	};
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.updater.enable {
+    environment.systemPackages = [ pkgs.clamav ];
+    users.extraUsers = singleton
+      { name = clamavUser;
+        uid = config.ids.uids.clamav;
+        description = "ClamAV daemon user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = clamavGroup;
+        gid = config.ids.gids.clamav;
+      };
+
+    services.clamav.updater.config = ''
+      DatabaseDirectory ${stateDir}
+      Foreground yes
+      Checks ${toString cfg.updater.frequency}
+      DatabaseMirror database.clamav.net
+    '';
+
+    jobs = {
+      clamav_updater = {
+	name = "clamav-updater";
+          startOn = "started network-interfaces";
+          stopOn = "stopping network-interfaces";
+
+          preStart = ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${clamavUser}:${clamavGroup} ${stateDir}
+          '';
+          exec = "${pkgs.clamav}/bin/freshclam --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}";
+      }; 
+    };
+
+  };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
new file mode 100644
index 00000000000..2b2a54ef409
--- /dev/null
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -0,0 +1,150 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.fail2ban;
+
+  fail2banConf = pkgs.writeText "fail2ban.conf" cfg.daemonConfig;
+
+  jailConf = pkgs.writeText "jail.conf"
+    (concatStringsSep "\n" (attrValues (flip mapAttrs cfg.jails (name: def:
+      optionalString (def != "") 
+        ''
+          [${name}]
+          ${def}
+        ''))));
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.fail2ban = {
+
+      daemonConfig = mkOption {
+        default =
+          ''
+            [Definition]
+            loglevel  = 3
+            logtarget = SYSLOG
+            socket    = /var/run/fail2ban/fail2ban.sock
+          '';
+        type = types.string;
+        description =
+          ''
+            The contents of Fail2ban's main configuration file.  It's
+            generally not necessary to change it.
+          '';
+      };
+
+      jails = mkOption {
+        default = { };
+        example =
+          { "apache-nohome-iptables" =
+              ''
+                # Block an IP address if it accesses a non-existent
+                # home directory more than 5 times in 10 minutes,
+                # since that indicates that it's scanning.
+                filter   = apache-nohome
+                action   = iptables-multiport[name=HTTP, port="http,https"]
+                logpath  = /var/log/httpd/error_log*
+                findtime = 600
+                bantime  = 600
+                maxretry = 5
+              '';
+          };
+        type = types.attrsOf types.string;
+        description =
+          ''
+            The configuration of each Fail2ban “jail”.  A jail
+            consists of an action (such as blocking a port using
+            <command>iptables</command>) that is triggered when a
+            filter applied to a log file triggers more than a certain
+            number of times in a certain time period.  Actions are
+            defined in <filename>/etc/fail2ban/action.d</filename>,
+            while filters are defined in
+            <filename>/etc/fail2ban/filter.d</filename>.
+          '';
+      };
+      
+    };
+
+  };
+
+  
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ pkgs.fail2ban ];
+
+    environment.etc =
+      [ { source = fail2banConf;
+          target = "fail2ban/fail2ban.conf";
+        }
+        { source = jailConf;
+          target = "fail2ban/jail.conf";
+        }
+        { source = "${pkgs.fail2ban}/etc/fail2ban/action.d/*.conf";
+          target = "fail2ban/action.d";
+        }
+        { source = "${pkgs.fail2ban}/etc/fail2ban/filter.d/*.conf";
+          target = "fail2ban/filter.d";
+        }
+      ];
+
+    system.activationScripts.fail2ban =
+      ''
+        mkdir -p /var/run/fail2ban -m 0755
+      '';
+
+    systemd.services.fail2ban =
+      { description = "Fail2ban intrusion prevention system";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+      
+        restartTriggers = [ fail2banConf jailConf ];
+        path = [ pkgs.fail2ban pkgs.iptables ];
+        
+        serviceConfig =
+          { ExecStart = "${pkgs.fail2ban}/bin/fail2ban-server -f";
+            ReadOnlyDirectories = "/";
+            ReadWriteDirectories = "/var/run/fail2ban /var/tmp";
+            CapabilityBoundingSet="CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_NET_RAW";
+          };
+
+        postStart =
+          ''
+            fail2ban-client reload
+          '';
+      };
+
+    # Add some reasonable default jails.  The special "DEFAULT" jail
+    # sets default values for all other jails.
+    services.fail2ban.jails.DEFAULT =
+      ''
+        ignoreip = 127.0.0.1/8
+        bantime  = 600
+        findtime = 600
+        maxretry = 3
+        backend  = auto
+      '';
+
+    # Block SSH if there are too many failing connection attempts.
+    services.fail2ban.jails."ssh-iptables" =
+      ''
+        filter   = sshd
+        action   = iptables[name=SSH, port=ssh, protocol=tcp]
+        logpath  = /var/log/warn
+        maxretry = 5
+      '';
+    
+  };
+
+}
diff --git a/nixos/modules/services/security/fprot.nix b/nixos/modules/services/security/fprot.nix
new file mode 100644
index 00000000000..9f1fc4ed6d8
--- /dev/null
+++ b/nixos/modules/services/security/fprot.nix
@@ -0,0 +1,88 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  fprotUser = "fprot";
+  stateDir = "/var/lib/fprot";
+  fprotGroup = fprotUser;
+  cfg = config.services.fprot;
+in {
+  options = {
+
+    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.
+	  '';
+	};
+
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.updater.enable {
+    environment.systemPackages = [ pkgs.fprot ];
+    environment.etc = singleton {
+      source = "${pkgs.fprot}/opt/f-prot/f-prot.conf";
+      target = "f-prot.conf";
+    };
+
+    users.extraUsers = singleton
+      { name = fprotUser;
+        uid = config.ids.uids.fprot;
+        description = "F-Prot daemon user";
+        home = stateDir;
+      };
+
+    users.extraGroups = singleton
+      { name = fprotGroup;
+        gid = config.ids.gids.fprot;
+      };
+
+    services.cron.systemCronJobs = [ "*/${toString cfg.updater.frequency} * * * * root start fprot-updater" ];
+
+    jobs = {
+      fprot_updater = {
+	name = "fprot-updater";
+	  task = true;
+
+	  # 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
+          '';
+	  #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/frandom.nix b/nixos/modules/services/security/frandom.nix
new file mode 100644
index 00000000000..9aae7b33a43
--- /dev/null
+++ b/nixos/modules/services/security/frandom.nix
@@ -0,0 +1,31 @@
+{pkgs, config, ...}:
+
+let kernel = config.boot.kernelPackages;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.frandom.enable = pkgs.lib.mkOption {
+      default = false;
+      type = pkgs.lib.types.bool;
+      description = ''
+        enable the /dev/frandom device (a very fast random number generator)
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = pkgs.lib.mkIf config.services.frandom.enable {
+    boot.kernelModules = [ "frandom" ];
+    boot.extraModulePackages = [ kernel.frandom ];
+    services.udev.packages = [ kernel.frandom ];
+  };
+
+}
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
new file mode 100644
index 00000000000..2dafb4595c6
--- /dev/null
+++ b/nixos/modules/services/security/tor.nix
@@ -0,0 +1,321 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) tor privoxy;
+
+  stateDir = "/var/lib/tor";
+  privoxyDir = stateDir+"/privoxy";
+
+  cfg = config.services.tor;
+
+  torUser = "tor";
+
+  opt = name: value: if value != "" then "${name} ${value}" else "";
+  optint = name: value: if value != 0 then "${name} ${toString value}" else "";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tor = {
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to the
+          configuration file.
+        '';
+      };
+
+      client = {
+
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable Tor daemon to route application connections.
+            You might want to disable this if you plan running a dedicated Tor relay.
+          '';
+        };
+
+        socksListenAddress = mkOption {
+          default = "127.0.0.1:9050";
+          example = "192.168.0.1:9100";
+          description = ''
+            Bind to this address to listen for connections from Socks-speaking
+            applications.
+          '';
+        };
+
+	socksListenAddressFaster = mkOption {
+          default = "127.0.0.1:9063";
+	  description = ''
+            Same as socksListenAddress but uses weaker circuit isolation to provide
+            performance suitable for a web browser.
+          '';
+        };
+
+        socksPolicy = mkOption {
+          default = "";
+          example = "accept 192.168.0.0/16, reject *";
+          description = ''
+            Entry policies to allow/deny SOCKS requests based on IP address.
+            First entry that matches wins. If no SocksPolicy is set, we accept
+            all (and only) requests from SocksListenAddress.
+          '';
+        };
+
+        privoxy = {
+
+          enable = mkOption {
+            default = true;
+            description = ''
+              Whether to enable a special instance of privoxy dedicated to Tor.
+              To have anonymity, protocols need to be scrubbed of identifying
+              information.
+              Most people using Tor want to anonymize their web traffic, so by
+              default we enable an special instance of privoxy specifically for
+              Tor.
+              However, if you are only going to use Tor only for other kinds of
+              traffic then you can disable this option.
+            '';
+          };
+
+          listenAddress = mkOption {
+            default = "127.0.0.1:8118";
+            description = ''
+              Address that Tor's instance of privoxy is listening to.
+              *This does not configure the standard NixOS instance of privoxy.*
+              This is for Tor connections only!
+              See services.privoxy.listenAddress to configure the standard NixOS
+              instace of privoxy.
+            '';
+          };
+
+          config = mkOption {
+            default = "";
+            description = ''
+              Extra configuration for Tor's instance of privoxy. Contents will be
+              added verbatim to the configuration file.
+              *This does not configure the standard NixOS instance of privoxy.*
+              This is for Tor connections only!
+              See services.privoxy.extraConfig to configure the standard NixOS
+              instace of privoxy.
+            '';
+          };
+
+        };
+
+      };
+
+      relay = {
+
+        enable = mkOption {
+          default = false;
+          description = ''
+            Whether to enable relaying TOR traffic for others.
+
+            See https://www.torproject.org/docs/tor-doc-relay for details.
+          '';
+        };
+
+        isBridge = mkOption {
+          default = false;
+          description = ''
+            Bridge relays (or "bridges" ) are Tor relays that aren't listed in the
+            main directory. Since there is no complete public list of them, even if an
+            ISP is filtering connections to all the known Tor relays, they probably
+            won't be able to block all the bridges.
+
+            A bridge relay can't be an exit relay.
+
+            You need to set relay.enable to true for this option to take effect.
+
+            The bridge is set up with an obfuscated transport proxy.
+
+            See https://www.torproject.org/bridges.html.en for more info.
+          '';
+        };
+
+        isExit = mkOption {
+          default = false;
+          description = ''
+            An exit relay allows Tor users to access regular Internet services.
+
+            Unlike running a non-exit relay, running an exit relay may expose
+            you to abuse complaints. See https://www.torproject.org/faq.html.en#ExitPolicies for more info.
+
+            You can specify which services Tor users may access via your exit relay using exitPolicy option.
+          '';
+        };
+
+        nickname = mkOption {
+          default = "anonymous";
+          description = ''
+            A unique handle for your TOR relay.
+          '';
+        };
+
+        bandwidthRate = mkOption {
+          default = 0;
+          example = 100;
+          description = ''
+            Specify this to limit the bandwidth usage of relayed (server)
+            traffic. Your own traffic is still unthrottled. Units: bytes/second.
+          '';
+        };
+
+        bandwidthBurst = mkOption {
+          default = cfg.relay.bandwidthRate;
+          example = 200;
+          description = ''
+            Specify this to allow bursts of the bandwidth usage of relayed (server)
+            traffic. The average usage will still be as specified in relayBandwidthRate.
+            Your own traffic is still unthrottled. Units: bytes/second.
+          '';
+        };
+
+        port = mkOption {
+          default = 9001;
+          description = ''
+            What port to advertise for Tor connections.
+          '';
+        };
+
+        listenAddress = mkOption {
+          default = "";
+          example = "0.0.0.0:9090";
+          description = ''
+            Set this if you need to listen on a port other than the one advertised
+            in relayPort (e.g. to advertise 443 but bind to 9090). You'll need to do
+            ipchains or other port forwsarding yourself to make this work.
+          '';
+        };
+
+        exitPolicy = mkOption {
+          default = "";
+          example = "accept *:6660-6667,reject *:*";
+          description = ''
+            A comma-separated list of exit policies. They're considered first
+            to last, and the first match wins. If you want to _replace_
+            the default exit policy, end this with either a reject *:* or an
+            accept *:*. Otherwise, you're _augmenting_ (prepending to) the
+            default exit policy. Leave commented to just use the default, which is
+            available in the man page or at https://www.torproject.org/documentation.html
+
+            Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
+            for issues you might encounter if you use the default exit policy.
+
+            If certain IPs and ports are blocked externally, e.g. by your firewall,
+            you should update your exit policy to reflect this -- otherwise Tor
+            users will be told that those destinations are down.
+          '';
+        };
+
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.client.enable || cfg.relay.enable) (
+  mkAssert (cfg.relay.enable -> !(cfg.relay.isBridge && cfg.relay.isExit)) "
+    Can't be both an exit and a bridge relay at the same time
+  " {
+
+    users.extraUsers = singleton
+      { name = torUser;
+        uid = config.ids.uids.tor;
+        description = "Tor daemon user";
+        home = stateDir;
+      };
+
+    jobs = {
+      tor = { name = "tor";
+
+              startOn = "started network-interfaces";
+              stopOn = "stopping network-interfaces";
+
+              preStart = ''
+                mkdir -m 0755 -p ${stateDir}
+                chown ${torUser} ${stateDir}
+              '';
+              exec = "${tor}/bin/tor -f ${pkgs.writeText "torrc" cfg.config}";
+    }; }
+    // optionalAttrs (cfg.client.privoxy.enable && cfg.client.enable) {
+      torPrivoxy = { name = "tor-privoxy";
+
+                     startOn = "started network-interfaces";
+                     stopOn = "stopping network-interfaces";
+
+                     preStart = ''
+                       mkdir -m 0755 -p ${privoxyDir}
+                       chown ${torUser} ${privoxyDir}
+                     '';
+                     exec = "${privoxy}/sbin/privoxy --no-daemon --user ${torUser} ${pkgs.writeText "torPrivoxy.conf" cfg.client.privoxy.config}";
+    }; };
+
+      services.tor.config = ''
+        DataDirectory ${stateDir}
+        User ${torUser}
+      ''
+      + optionalString cfg.client.enable  ''
+        SOCKSPort ${cfg.client.socksListenAddress} IsolateDestAddr
+	SOCKSPort ${cfg.client.socksListenAddressFaster}
+        ${opt "SocksPolicy" cfg.client.socksPolicy}
+      ''
+      + optionalString cfg.relay.enable ''
+        ORPort ${toString cfg.relay.port}
+        ${opt "ORListenAddress" cfg.relay.listenAddress }
+        ${opt "Nickname" cfg.relay.nickname}
+        ${optint "RelayBandwidthRate" cfg.relay.bandwidthRate}
+        ${optint "RelayBandwidthBurst" cfg.relay.bandwidthBurst}
+        ${if cfg.relay.isExit then opt "ExitPolicy" cfg.relay.exitPolicy else "ExitPolicy reject *:*"}
+        ${if cfg.relay.isBridge then ''
+          BridgeRelay 1
+          ServerTransportPlugin obfs2,obfs3 exec ${pkgs.pythonPackages.obfsproxy}/bin/obfsproxy managed
+        '' else ""}
+      '';
+
+      services.tor.client.privoxy.config = ''
+        # Generally, this file goes in /etc/privoxy/config
+        #
+        # Tor listens as a SOCKS4a proxy here:
+        forward-socks4a / ${cfg.client.socksListenAddressFaster} .
+        confdir ${privoxy}/etc
+        logdir ${privoxyDir}
+        # actionsfile standard  # Internal purpose, recommended
+        actionsfile default.action   # Main actions file
+        actionsfile user.action      # User customizations
+        filterfile default.filter
+
+        # Don't log interesting things, only startup messages, warnings and errors
+        logfile logfile
+        #jarfile jarfile
+        #debug   0    # show each GET/POST/CONNECT request
+        debug   4096 # Startup banner and warnings
+        debug   8192 # Errors - *we highly recommended enabling this*
+
+        user-manual ${privoxy}/doc/privoxy/user-manual
+        listen-address  ${cfg.client.privoxy.listenAddress}
+        toggle  1
+        enable-remote-toggle 0
+        enable-edit-actions 0
+        enable-remote-http-toggle 0
+        buffer-limit 4096
+
+        # Extra config goes here
+      '';
+
+  });
+
+}
diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix
new file mode 100644
index 00000000000..1c158906a91
--- /dev/null
+++ b/nixos/modules/services/security/torify.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+
+  cfg = config.services.tor;
+
+  torify = pkgs.writeTextFile {
+    name = "torify";
+    text = ''
+        #!${pkgs.stdenv.shell}
+        TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.torify.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@"
+    '';
+    executable = true;
+    destination = "/bin/torify";
+  };
+
+in
+
+{
+
+  ###### interface
+  
+  options = {
+  
+    services.tor.torify = {
+
+      enable = mkOption {
+        default = cfg.client.enable;
+        description = ''
+          Whether to build torify scipt to relay application traffic via TOR.
+        '';
+      };
+
+      server = mkOption {
+        default = "localhost:9050";
+        example = "192.168.0.20";
+        description = ''
+          IP address of TOR client to use.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to TSocks
+          configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.torify.enable {
+
+    environment.systemPackages = [ torify ];  # expose it to the users
+
+    services.tor.torify.config = ''
+      server = ${toString(head (splitString ":" cfg.torify.server))}
+      server_port = ${toString(tail (splitString ":" cfg.torify.server))}
+
+      local = 127.0.0.0/255.128.0.0
+      local = 127.128.0.0/255.192.0.0
+    '';
+  };
+
+}
diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix
new file mode 100644
index 00000000000..d6974282a6b
--- /dev/null
+++ b/nixos/modules/services/security/torsocks.nix
@@ -0,0 +1,85 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+
+  cfg = config.services.tor;
+
+  makeConfig = server: ''
+      server = ${toString(head (splitString ":" server))}
+      server_port = ${toString(tail (splitString ":" server))}
+
+      local = 127.0.0.0/255.128.0.0
+      local = 127.128.0.0/255.192.0.0
+      local = 169.254.0.0/255.255.0.0
+      local = 172.16.0.0/255.240.0.0
+      local = 192.168.0.0/255.255.0.0
+
+      ${cfg.torsocks.config}
+    '';
+  makeTorsocks = name: server: pkgs.writeTextFile {
+    name = name;
+    text = ''
+        #!${pkgs.stdenv.shell}
+        TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (makeConfig server)} LD_PRELOAD="${pkgs.torsocks}/lib/torsocks/libtorsocks.so $LD_PRELOAD" "$@"
+    '';
+    executable = true;
+    destination = "/bin/${name}";
+  };
+
+  torsocks = makeTorsocks "torsocks" cfg.torsocks.server;
+  torsocksFaster = makeTorsocks "torsocks-faster" cfg.torsocks.serverFaster;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tor.torsocks = {
+
+      enable = mkOption {
+        default = cfg.client.enable;
+        description = ''
+          Whether to build torsocks scipt to relay application traffic via TOR.
+        '';
+      };
+
+      server = mkOption {
+        default = cfg.client.socksListenAddress;
+        example = "192.168.0.20:9050";
+        description = ''
+          IP address of TOR client to use.
+        '';
+      };
+
+      serverFaster = mkOption {
+        default = cfg.client.socksListenAddressFaster;
+        example = "192.168.0.20:9063";
+        description = ''
+          IP address of TOR client to use for applications like web browsers which
+	  need less circuit isolation to achive satisfactory performance.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = ''
+          Extra configuration. Contents will be added verbatim to torsocks
+          configuration file.
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.torsocks.enable {
+
+    environment.systemPackages = [ torsocks torsocksFaster ];  # expose it to the users
+
+  };
+
+}
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
new file mode 100644
index 00000000000..eab876be76d
--- /dev/null
+++ b/nixos/modules/services/system/dbus.nix
@@ -0,0 +1,194 @@
+# D-Bus configuration and system bus daemon.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.dbus;
+
+  homeDir = "/var/run/dbus";
+
+  configDir = pkgs.stdenv.mkDerivation {
+    name = "dbus-conf";
+    preferLocalBuild = true;
+    buildCommand = ''
+      ensureDir $out
+
+      cp -v ${pkgs.dbus_daemon}/etc/dbus-1/system.conf $out/system.conf
+
+      # !!! Hm, these `sed' calls are rather error-prone...
+
+      # Tell the daemon where the setuid wrapper around
+      # dbus-daemon-launch-helper lives.
+      sed -i $out/system.conf \
+          -e 's|<servicehelper>.*/libexec/dbus-daemon-launch-helper|<servicehelper>${config.security.wrapperDir}/dbus-daemon-launch-helper|'
+
+      # Add the system-services and system.d directories to the system
+      # bus search path.
+      sed -i $out/system.conf \
+          -e 's|<standard_system_servicedirs/>|${systemServiceDirs}|' \
+          -e 's|<includedir>system.d</includedir>|${systemIncludeDirs}|'
+
+      cp ${pkgs.dbus_daemon}/etc/dbus-1/session.conf $out/session.conf
+
+      # Add the services and session.d directories to the session bus
+      # search path.
+      sed -i $out/session.conf \
+          -e 's|<standard_session_servicedirs />|${sessionServiceDirs}&|' \
+          -e 's|<includedir>session.d</includedir>|${sessionIncludeDirs}|'
+    ''; # */
+  };
+
+  systemServiceDirs = concatMapStrings
+    (d: "<servicedir>${d}/share/dbus-1/system-services</servicedir> ")
+    cfg.packages;
+
+  systemIncludeDirs = concatMapStrings
+    (d: "<includedir>${d}/etc/dbus-1/system.d</includedir> ")
+    cfg.packages;
+
+  sessionServiceDirs = concatMapStrings
+    (d: "<servicedir>${d}/share/dbus-1/services</servicedir> ")
+    cfg.packages;
+
+  sessionIncludeDirs = concatMapStrings
+    (d: "<includedir>${d}/etc/dbus-1/session.d</includedir> ")
+    cfg.packages;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.dbus = {
+
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whether to start the D-Bus message bus daemon, which is
+          required by many other system services and applications.
+        '';
+        merge = pkgs.lib.mergeEnableOption;
+      };
+
+      packages = mkOption {
+        default = [];
+        description = ''
+          Packages whose D-Bus configuration files should be included in
+          the configuration of the D-Bus system-wide message bus.
+          Specifically, every file in
+          <filename><replaceable>pkg</replaceable>/etc/dbus-1/system.d</filename>
+          is included.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.dbus_daemon pkgs.dbus_tools ];
+
+    environment.etc = singleton
+      { source = configDir;
+        target = "dbus-1";
+      };
+
+    users.extraUsers.messagebus = {
+      uid = config.ids.uids.messagebus;
+      description = "D-Bus system message bus daemon user";
+      home = homeDir;
+      group = "messagebus";
+    };
+
+    users.extraGroups.messagebus.gid = config.ids.gids.messagebus;
+
+    # FIXME: these are copied verbatim from the dbus source tree.  We
+    # should install and use the originals.
+    systemd.units."dbus.socket".text =
+      ''
+        [Unit]
+        Description=D-Bus System Message Bus Socket
+
+        [Socket]
+        ListenStream=/var/run/dbus/system_bus_socket
+      '';
+      
+    systemd.units."dbus.service".text =
+      ''
+        [Unit]
+        Description=D-Bus System Message Bus
+        Requires=dbus.socket
+
+        [Service]
+        ExecStartPre=${pkgs.dbus_tools}/bin/dbus-uuidgen --ensure
+        ExecStartPre=-${pkgs.coreutils}/bin/rm -f /var/run/dbus/pid
+        ExecStart=${pkgs.dbus_daemon}/bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
+        ExecReload=${pkgs.dbus_tools}/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
+        OOMScoreAdjust=-900
+      '';
+
+    /*
+    jobs.dbus =
+      { startOn = "started udev and started syslogd";
+      
+        restartIfChanged = false;
+        
+        path = [ pkgs.dbus_daemon pkgs.dbus_tools ];
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${homeDir}
+            chown messagebus ${homeDir}
+
+            mkdir -m 0755 -p /var/lib/dbus
+            dbus-uuidgen --ensure
+
+            rm -f ${homeDir}/pid
+          '';
+
+        daemonType = "fork";
+
+        exec = "dbus-daemon --system";
+
+        postStop =
+          ''
+            # !!! Hack: doesn't belong here.
+            pid=$(cat /var/run/ConsoleKit/pid || true)
+            if test -n "$pid"; then
+                kill $pid || true
+                rm -f /var/run/ConsoleKit/pid
+            fi
+          '';
+      };
+    */    
+
+    security.setuidOwners = singleton
+      { program = "dbus-daemon-launch-helper";
+        source = "${pkgs.dbus_daemon}/libexec/dbus-daemon-launch-helper";
+        owner = "root";
+        group = "messagebus";
+        setuid = true;
+        setgid = false;
+        permissions = "u+rx,g+rx,o-rx";
+      };
+
+    services.dbus.packages =
+      [ "/nix/var/nix/profiles/default"
+        config.system.path
+      ];
+
+    environment.pathsToLink = [ "/etc/dbus-1" "/share/dbus-1" ];
+
+  };
+
+}
diff --git a/nixos/modules/services/system/kerberos.nix b/nixos/modules/services/system/kerberos.nix
new file mode 100644
index 00000000000..8fb5debd20e
--- /dev/null
+++ b/nixos/modules/services/system/kerberos.nix
@@ -0,0 +1,71 @@
+{pkgs, config, ...}:
+
+let
+
+  inherit (pkgs.lib) mkOption mkIf singleton;
+
+  inherit (pkgs) heimdal;
+
+  stateDir = "/var/heimdal";
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.kerberos_server = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable the kerberos authentification server.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.kerberos_server.enable {
+
+    environment.systemPackages = [ heimdal ];
+
+    services.xinetd.enable = true;
+    services.xinetd.services = pkgs.lib.singleton
+      { name = "kerberos-adm";
+        flags = "REUSE NAMEINARGS";
+        protocol = "tcp";
+        user = "root";
+        server = "${pkgs.tcp_wrappers}/sbin/tcpd";
+        serverArgs = "${pkgs.heimdal}/sbin/kadmind";
+      };
+
+    jobs.kdc =
+      { description = "Kerberos Domain Controller daemon";
+
+        startOn = "ip-up";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+          '';
+
+        exec = "${heimdal}/sbin/kdc";
+
+      };
+
+    jobs.kpasswdd =
+      { description = "Kerberos Domain Controller daemon";
+
+        startOn = "ip-up";
+
+        exec = "${heimdal}/sbin/kpasswdd";
+      };
+  };
+
+}
diff --git a/nixos/modules/services/system/nscd.conf b/nixos/modules/services/system/nscd.conf
new file mode 100644
index 00000000000..6d0dcacf977
--- /dev/null
+++ b/nixos/modules/services/system/nscd.conf
@@ -0,0 +1,28 @@
+server-user             nscd
+threads                 1
+paranoia                no
+debug-level             0
+
+enable-cache            passwd          yes
+positive-time-to-live   passwd          600
+negative-time-to-live   passwd          20
+suggested-size          passwd          211
+check-files             passwd          yes
+persistent              passwd          no
+shared                  passwd          yes
+
+enable-cache            group           yes
+positive-time-to-live   group           3600
+negative-time-to-live   group           60
+suggested-size          group           211
+check-files             group           yes
+persistent              group           no
+shared                  group           yes
+
+enable-cache            hosts           yes
+positive-time-to-live   hosts           600
+negative-time-to-live   hosts           5
+suggested-size          hosts           211
+check-files             hosts           yes
+persistent              hosts           no
+shared                  hosts           yes
diff --git a/nixos/modules/services/system/nscd.nix b/nixos/modules/services/system/nscd.nix
new file mode 100644
index 00000000000..e8534b12043
--- /dev/null
+++ b/nixos/modules/services/system/nscd.nix
@@ -0,0 +1,71 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  nssModulesPath = config.system.nssModules.path;
+
+  inherit (pkgs.lib) singleton;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.nscd = {
+
+      enable = mkOption {
+        default = true;
+        description = "Whether to enable the Name Service Cache Daemon.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.nscd.enable {
+
+    users.extraUsers = singleton
+      { name = "nscd";
+        uid = config.ids.uids.nscd;
+        description = "Name service cache daemon user";
+      };
+
+    systemd.services.nscd =
+      { description = "Name Service Cache Daemon";
+
+        wantedBy = [ "nss-lookup.target" "nss-user-lookup.target" ];
+
+        environment = { LD_LIBRARY_PATH = nssModulesPath; };
+
+        preStart =
+          ''
+            mkdir -m 0755 -p /run/nscd
+            rm -f /run/nscd/nscd.pid
+            mkdir -m 0755 -p /var/db/nscd
+          '';
+
+        restartTriggers = [ config.environment.etc.hosts.source ];
+
+        serviceConfig =
+          { ExecStart = "@${pkgs.glibc}/sbin/nscd nscd -f ${./nscd.conf}";
+            Type = "forking";
+            PIDFile = "/run/nscd/nscd.pid";
+            Restart = "always";
+            ExecReload =
+              [ "${pkgs.glibc}/sbin/nscd --invalidate passwd"
+                "${pkgs.glibc}/sbin/nscd --invalidate group"
+                "${pkgs.glibc}/sbin/nscd --invalidate hosts"
+              ];
+          };
+      };
+
+  };
+}
diff --git a/nixos/modules/services/system/uptimed.nix b/nixos/modules/services/system/uptimed.nix
new file mode 100644
index 00000000000..61eecd5c9ba
--- /dev/null
+++ b/nixos/modules/services/system/uptimed.nix
@@ -0,0 +1,68 @@
+{pkgs, config, ...}:
+
+let
+
+  inherit (pkgs.lib) mkOption mkIf singleton;
+
+  inherit (pkgs) uptimed;
+
+  stateDir = "/var/spool/uptimed";
+
+  uptimedUser = "uptimed";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.uptimed = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Uptimed allows you to track your highest uptimes.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.uptimed.enable {
+
+    environment.systemPackages = [ uptimed ];
+
+    users.extraUsers = singleton
+      { name = uptimedUser;
+        uid = config.ids.uids.uptimed;
+        description = "Uptimed daemon user";
+        home = stateDir;
+      };
+
+    jobs.uptimed =
+      { description = "Uptimed daemon";
+
+        startOn = "startup";
+
+        preStart =
+          ''
+            mkdir -m 0755 -p ${stateDir}
+            chown ${uptimedUser} ${stateDir}
+
+            if ! test -f ${stateDir}/bootid ; then
+              ${uptimed}/sbin/uptimed -b
+            fi
+          '';
+
+        exec = "${uptimed}/sbin/uptimed";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
new file mode 100644
index 00000000000..e0c212e5661
--- /dev/null
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -0,0 +1,65 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.deluge;
+  cfg_web = config.services.deluge.web;
+in {
+  options = {
+    services.deluge = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Start Deluge daemon.
+        ''; 
+      };  
+    };
+
+    services.deluge.web = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Start Deluge Web daemon.
+        ''; 
+      };  
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.deluged = {
+      after = [ "network.target" ];
+      description = "Deluge BitTorrent Daemon";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.pythonPackages.deluge ];
+      serviceConfig.ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluged -d";
+      serviceConfig.User = "deluge";
+      serviceConfig.Group = "deluge";
+    };
+
+    systemd.services.delugeweb = mkIf cfg_web.enable {
+      after = [ "network.target" ];
+      description = "Deluge BitTorrent WebUI";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.pythonPackages.deluge ];
+      serviceConfig.ExecStart = "${pkgs.pythonPackages.deluge}/bin/deluge --ui web";
+      serviceConfig.User = "deluge";
+      serviceConfig.Group = "deluge";
+    };
+
+    environment.systemPackages = [ pkgs.pythonPackages.deluge ];
+
+    users.extraUsers.deluge = {
+      group = "deluge";
+      uid = config.ids.uids.deluge;
+      home = "/var/lib/deluge/";
+      createHome = true;
+      description = "Deluge Daemon user";
+    };
+
+    users.extraGroups.deluge.gid = config.ids.gids.deluge;
+  };
+}
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
new file mode 100644
index 00000000000..063332d4862
--- /dev/null
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -0,0 +1,173 @@
+# NixOS module for Transmission BitTorrent daemon
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.transmission;
+  homeDir = "/var/lib/transmission";
+  settingsDir = "${homeDir}/.config/transmission-daemon";
+  settingsFile = "${settingsDir}/settings.json";
+
+  # Strings must be quoted, ints and bools must not (for settings.json).
+  toOption = x:
+    if x == true then "true"
+    else if x == false then "false"
+    else if builtins.isInt x then toString x
+    else toString ''\"${x}\"'';
+
+  # All lines in settings.json end with a ',' (comma), except for the last
+  # line. This is standard JSON. But a comma can also appear *inside* some
+  # fields, notably the "rpc-whitelist" field. This is difficult to handle in
+  # sed so we simply ignore it and say that if you want to change the option at
+  # the last line of settings.json, you have to do it manually. At this time of
+  # writing, the last option is "utp-enable":true.
+  attrsToSedArgs = as:
+    concatStrings (concatLists (mapAttrsToList (name: value:
+      #map (x: '' -e 's=\(\"${name}\":\)[^,]*\(.*\)=\1 ${toOption x}\2=' '') # breaks if comma inside value field
+      map (x: '' -e 's=\(\"${name}\":\).*=\1 ${toOption x},=' '') # always append ',' (breaks last line in settings.json)
+        (if isList value then value else [value]))
+        as));
+
+in
+
+{
+
+  ### configuration
+
+  options = {
+
+    services.transmission = {
+
+      enable = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = ''
+          Whether or not to enable the headless Transmission BitTorrent daemon.
+
+          Transmission daemon can be controlled via the RPC interface using
+          transmission-remote or the WebUI (http://localhost:9091/ by default).
+
+          Torrents are downloaded to ${homeDir}/Downloads/ by default and are
+          accessible to users in the "transmission" group.
+        '';
+      };
+
+      settings = mkOption {
+        type = types.attrs;
+        default =
+          {
+            # for users in group "transmission" to have access to torrents
+            umask = 2;
+          }
+        ;
+        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}."
+        '';
+      };
+
+      rpc_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" ];
+      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.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+      serviceConfig.User = "transmission";
+      # NOTE: transmission has an internal umask that also must be set (in settings.json)
+      serviceConfig.UMask = "0002";
+    };
+
+    # It's useful to have transmission in path, e.g. for remote control
+    environment.systemPackages = [ pkgs.transmission ];
+
+    users.extraUsers.transmission = {
+      group = "transmission";
+      uid = config.ids.uids.transmission;
+      description = "Transmission BitTorrent user";
+      home = homeDir;
+      createHome = true;
+    };
+
+    users.extraGroups.transmission.gid = config.ids.gids.transmission;
+
+    # AppArmor profile
+    security.apparmor.profiles = mkIf (config.security.apparmor.enable && cfg.apparmor) [
+      (pkgs.writeText "apparmor-transmission-daemon" ''
+        #include <tunables/global>
+
+        ${pkgs.transmission}/bin/transmission-daemon {
+          #include <abstractions/base>
+          #include <abstractions/nameservice>
+
+          ${pkgs.glibc}/lib/*.so             mr,
+          ${pkgs.libevent}/lib/libevent*.so* mr,
+          ${pkgs.curl}/lib/libcurl*.so*      mr,
+          ${pkgs.openssl}/lib/libssl*.so*    mr,
+          ${pkgs.openssl}/lib/libcrypto*.so* mr,
+          ${pkgs.zlib}/lib/libz*.so*         mr,
+          ${pkgs.libssh2}/lib/libssh2*.so*   mr,
+
+          @{PROC}/sys/kernel/random/uuid   r,
+          @{PROC}/sys/vm/overcommit_memory r,
+
+          ${pkgs.transmission}/share/transmission/** r,
+
+          owner ${settingsDir}/** rw,
+
+          ${cfg.settings.download-dir}/** rw,
+          ${optionalString cfg.settings.incomplete-dir-enabled ''
+            ${cfg.settings.incomplete-dir}/** rw,
+          ''}
+        }
+      '')
+    ];
+  };
+
+}
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
new file mode 100644
index 00000000000..850f558e4cb
--- /dev/null
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -0,0 +1,127 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mingetty = {
+
+      greetingLine = mkOption {
+        default = ''<<< Welcome to NixOS ${config.system.nixosVersion} (\m) - \l >>>'';
+        description = ''
+          Welcome line printed by mingetty.
+        '';
+      };
+
+      helpLine = mkOption {
+        default = "";
+        description = ''
+          Help line printed by mingetty below the welcome line.
+          Used by the installation CD to give some hints on
+          how to proceed.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  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
+      '';
+
+    environment.etc = singleton
+      { # Friendly greeting on the virtual consoles.
+        source = pkgs.writeText "issue" ''
+
+          ${config.services.mingetty.greetingLine}
+          ${config.services.mingetty.helpLine}
+
+        '';
+        target = "issue";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/ttys/gpm.nix b/nixos/modules/services/ttys/gpm.nix
new file mode 100644
index 00000000000..6a425cf327f
--- /dev/null
+++ b/nixos/modules/services/ttys/gpm.nix
@@ -0,0 +1,51 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.gpm;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.gpm = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable GPM, the General Purpose Mouse daemon,
+          which enables mouse support in virtual consoles.
+        '';
+      };
+
+      protocol = mkOption {
+        default = "ps/2";
+        description = "Mouse protocol to use.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    jobs.gpm =
+      { description = "General purpose mouse";
+
+        startOn = "started udev";
+
+        exec = "${pkgs.gpm}/sbin/gpm -m /dev/input/mice -t ${cfg.protocol} -D &>/dev/null";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
new file mode 100644
index 00000000000..e2cfc5cdb26
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -0,0 +1,662 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  mainCfg = config.services.httpd;
+
+  httpd = mainCfg.package;
+
+  version24 = !versionOlder httpd.version "2.4";
+
+  httpdConf = mainCfg.configFile;
+
+  php = pkgs.php.override { apacheHttpd = httpd; };
+
+  getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80;
+
+  extraModules = attrByPath ["extraModules"] [] mainCfg;
+  extraForeignModules = filter builtins.isAttrs extraModules;
+  extraApacheModules = filter (x: !(builtins.isAttrs x)) extraModules; # I'd prefer using builtins.isString here, but doesn't exist yet
+
+
+  makeServerInfo = cfg: {
+    # Canonical name must not include a trailing slash.
+    canonicalName =
+      (if cfg.enableSSL then "https" else "http") + "://" +
+      cfg.hostName +
+      (if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString (getPort cfg)}" else "");
+
+    # Admin address: inherit from the main server if not specified for
+    # a virtual host.
+    adminAddr = if cfg.adminAddr != "" then cfg.adminAddr else mainCfg.adminAddr;
+
+    vhostConfig = cfg;
+    serverConfig = mainCfg;
+    fullConfig = config; # machine config
+  };
+
+
+  vhostOptions = import ./per-server-options.nix {
+    inherit mkOption;
+    forMainServer = false;
+  };
+
+  vhosts = let
+    makeVirtualHost = cfgIn:
+      let
+        # Fill in defaults for missing options.
+        cfg = addDefaultOptionValues vhostOptions cfgIn;
+      in cfg;
+    in map makeVirtualHost mainCfg.virtualHosts;
+
+
+  allHosts = [mainCfg] ++ vhosts;
+
+
+  callSubservices = serverInfo: defs:
+    let f = svc:
+      let
+        svcFunction =
+          if svc ? function then svc.function
+          else import "${./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix";
+        config = addDefaultOptionValues res.options
+          (if svc ? config then svc.config else svc);
+        defaults = {
+          extraConfig = "";
+          extraModules = [];
+          extraModulesPre = [];
+          extraPath = [];
+          extraServerPath = [];
+          globalEnvVars = [];
+          robotsEntries = "";
+          startupScript = "";
+          enablePHP = false;
+          phpOptions = "";
+          options = {};
+        };
+        res = defaults // svcFunction { inherit config pkgs serverInfo php; };
+      in res;
+    in map f defs;
+
+
+  # !!! callSubservices is expensive
+  subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
+
+  mainSubservices = subservicesFor mainCfg;
+
+  allSubservices = mainSubservices ++ concatMap subservicesFor vhosts;
+
+
+  # !!! should be in lib
+  writeTextInDir = name: text:
+    pkgs.runCommand name {inherit text;} "ensureDir $out; echo -n \"$text\" > $out/$name";
+
+
+  enableSSL = any (vhost: vhost.enableSSL) allHosts;
+
+
+  # Names of modules from ${httpd}/modules that we want to load.
+  apacheModules =
+    [ # HTTP authentication mechanisms: basic and digest.
+      "auth_basic" "auth_digest"
+
+      # Authentication: is the user who he claims to be?
+      "authn_file" "authn_dbm" "authn_anon"
+      (if version24 then "authn_core" else "authn_alias")
+
+      # Authorization: is the user allowed access?
+      "authz_user" "authz_groupfile" "authz_host"
+
+      # Other modules.
+      "ext_filter" "include" "log_config" "env" "mime_magic"
+      "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
+      "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
+      "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
+      "userdir" "alias" "rewrite" "proxy" "proxy_http"
+    ]
+    ++ optionals version24 [
+      "mpm_${mainCfg.multiProcessingModule}"
+      "authz_core"
+      "unixd"
+    ]
+    ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
+    ++ optional enableSSL "ssl"
+    ++ extraApacheModules;
+
+
+  allDenied = if version24 then ''
+    Require all denied
+  '' else ''
+    Order deny,allow
+    Deny from all
+  '';
+
+  allGranted = if version24 then ''
+    Require all granted
+  '' else ''
+    Order allow,deny
+    Allow from all
+  '';
+
+
+  loggingConf = ''
+    ErrorLog ${mainCfg.logDir}/error_log
+
+    LogLevel notice
+
+    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+    LogFormat "%h %l %u %t \"%r\" %>s %b" common
+    LogFormat "%{Referer}i -> %U" referer
+    LogFormat "%{User-agent}i" agent
+
+    CustomLog ${mainCfg.logDir}/access_log ${mainCfg.logFormat}
+  '';
+
+
+  browserHacks = ''
+    BrowserMatch "Mozilla/2" nokeepalive
+    BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+    BrowserMatch "RealPlayer 4\.0" force-response-1.0
+    BrowserMatch "Java/1\.0" force-response-1.0
+    BrowserMatch "JDK/1\.0" force-response-1.0
+    BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+    BrowserMatch "^WebDrive" redirect-carefully
+    BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
+    BrowserMatch "^gnome-vfs" redirect-carefully
+  '';
+
+
+  sslConf = ''
+    SSLSessionCache shm:${mainCfg.stateDir}/ssl_scache(512000)
+
+    SSLMutex posixsem
+
+    SSLRandomSeed startup builtin
+    SSLRandomSeed connect builtin
+  '';
+
+
+  mimeConf = ''
+    TypesConfig ${httpd}/conf/mime.types
+
+    AddType application/x-x509-ca-cert .crt
+    AddType application/x-pkcs7-crl    .crl
+    AddType application/x-httpd-php    .php .phtml
+
+    <IfModule mod_mime_magic.c>
+        MIMEMagicFile ${httpd}/conf/magic
+    </IfModule>
+
+    AddEncoding x-compress Z
+    AddEncoding x-gzip gz tgz
+  '';
+
+
+  perServerConf = isMainServer: cfg: let
+
+    serverInfo = makeServerInfo cfg;
+
+    subservices = callSubservices serverInfo cfg.extraSubservices;
+
+    documentRoot = if cfg.documentRoot != null then cfg.documentRoot else
+      pkgs.runCommand "empty" {} "ensureDir $out";
+
+    documentRootConf = ''
+      DocumentRoot "${documentRoot}"
+
+      <Directory "${documentRoot}">
+          Options Indexes FollowSymLinks
+          AllowOverride None
+          ${allGranted}
+      </Directory>
+    '';
+
+    robotsTxt = pkgs.writeText "robots.txt" ''
+      ${# If this is a vhost, the include the entries for the main server as well.
+        if isMainServer then ""
+        else concatMapStrings (svc: svc.robotsEntries) mainSubservices}
+      ${concatMapStrings (svc: svc.robotsEntries) subservices}
+    '';
+
+    robotsConf = ''
+      Alias /robots.txt ${robotsTxt}
+    '';
+
+  in ''
+    ServerName ${serverInfo.canonicalName}
+
+    ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
+
+    ${if cfg.sslServerCert != "" then ''
+      SSLCertificateFile ${cfg.sslServerCert}
+      SSLCertificateKeyFile ${cfg.sslServerKey}
+    '' else ""}
+
+    ${if cfg.enableSSL then ''
+      SSLEngine on
+    '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
+    ''
+      SSLEngine off
+    '' else ""}
+
+    ${if isMainServer || cfg.adminAddr != "" then ''
+      ServerAdmin ${cfg.adminAddr}
+    '' else ""}
+
+    ${if !isMainServer && mainCfg.logPerVirtualHost then ''
+      ErrorLog ${mainCfg.logDir}/error_log-${cfg.hostName}
+      CustomLog ${mainCfg.logDir}/access_log-${cfg.hostName} ${cfg.logFormat}
+    '' else ""}
+
+    ${robotsConf}
+
+    ${if isMainServer || cfg.documentRoot != null then documentRootConf else ""}
+
+    ${if cfg.enableUserDir then ''
+
+      UserDir public_html
+      UserDir disabled root
+
+      <Directory "/home/*/public_html">
+          AllowOverride FileInfo AuthConfig Limit Indexes
+          Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+          <Limit GET POST OPTIONS>
+              ${allGranted}
+          </Limit>
+          <LimitExcept GET POST OPTIONS>
+              ${allDenied}
+          </LimitExcept>
+      </Directory>
+
+    '' else ""}
+
+    ${if cfg.globalRedirect != "" then ''
+      RedirectPermanent / ${cfg.globalRedirect}
+    '' else ""}
+
+    ${
+      let makeFileConf = elem: ''
+            Alias ${elem.urlPath} ${elem.file}
+          '';
+      in concatMapStrings makeFileConf cfg.servedFiles
+    }
+
+    ${
+      let makeDirConf = elem: ''
+            Alias ${elem.urlPath} ${elem.dir}/
+            <Directory ${elem.dir}>
+                Options +Indexes
+                ${allGranted}
+                AllowOverride All
+            </Directory>
+          '';
+      in concatMapStrings makeDirConf cfg.servedDirs
+    }
+
+    ${concatMapStrings (svc: svc.extraConfig) subservices}
+
+    ${cfg.extraConfig}
+  '';
+
+
+  confFile = pkgs.writeText "httpd.conf" ''
+
+    ServerRoot ${httpd}
+
+    ${optionalString version24 ''
+      DefaultRuntimeDir ${mainCfg.stateDir}/runtime
+    ''}
+
+    PidFile ${mainCfg.stateDir}/httpd.pid
+
+    ${optionalString (mainCfg.multiProcessingModule != "prefork") ''
+      # mod_cgid requires this.
+      ScriptSock ${mainCfg.stateDir}/cgisock
+    ''}
+
+    <IfModule prefork.c>
+        MaxClients           ${toString mainCfg.maxClients}
+        MaxRequestsPerChild  ${toString mainCfg.maxRequestsPerChild}
+    </IfModule>
+
+    ${let
+        ports = map getPort allHosts;
+        uniquePorts = uniqList {inputList = ports;};
+      in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts
+    }
+
+    User ${mainCfg.user}
+    Group ${mainCfg.group}
+
+    ${let
+        load = {name, path}: "LoadModule ${name}_module ${path}\n";
+        allModules =
+          concatMap (svc: svc.extraModulesPre) allSubservices
+          ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
+          ++ optional enablePHP { name = "php5"; path = "${php}/modules/libphp5.so"; }
+          ++ concatMap (svc: svc.extraModules) allSubservices
+          ++ extraForeignModules;
+      in concatMapStrings load allModules
+    }
+
+    AddHandler type-map var
+
+    <Files ~ "^\.ht">
+        ${allDenied}
+    </Files>
+
+    ${mimeConf}
+    ${loggingConf}
+    ${browserHacks}
+
+    Include ${httpd}/conf/extra/httpd-default.conf
+    Include ${httpd}/conf/extra/httpd-autoindex.conf
+    Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
+    Include ${httpd}/conf/extra/httpd-languages.conf
+
+    ${if enableSSL then sslConf else ""}
+
+    # Fascist default - deny access to everything.
+    <Directory />
+        Options FollowSymLinks
+        AllowOverride None
+        ${allDenied}
+    </Directory>
+
+    # But do allow access to files in the store so that we don't have
+    # to generate <Directory> clauses for every generated file that we
+    # want to serve.
+    <Directory /nix/store>
+        ${allGranted}
+    </Directory>
+
+    # Generate directives for the main server.
+    ${perServerConf true mainCfg}
+
+    # Always enable virtual hosts; it doesn't seem to hurt.
+    ${let
+        ports = map getPort allHosts;
+        uniquePorts = uniqList {inputList = ports;};
+        directives = concatMapStrings (port: "NameVirtualHost *:${toString port}\n") uniquePorts;
+      in optionalString (!version24) directives
+    }
+
+    ${let
+        makeVirtualHost = vhost: ''
+          <VirtualHost *:${toString (getPort vhost)}>
+              ${perServerConf false vhost}
+          </VirtualHost>
+        '';
+      in concatMapStrings makeVirtualHost vhosts
+    }
+  '';
+
+
+  enablePHP = any (svc: svc.enablePHP) allSubservices;
+
+
+  # Generate the PHP configuration file.  Should probably be factored
+  # out into a separate module.
+  phpIni = pkgs.runCommand "php.ini"
+    { options = concatStringsSep "\n"
+        ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
+    }
+    ''
+      cat ${php}/etc/php-recommended.ini > $out
+      echo "$options" >> $out
+    '';
+
+in
+
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.httpd = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the Apache httpd server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.apacheHttpd.override { mpm = mainCfg.multiProcessingModule; };
+	example = "pkgs.apacheHttpd_2_4";
+        description = "
+          Overridable attribute of the Apache HTTP Server package to use.
+        ";
+      };
+
+      configFile = mkOption {
+        default = confFile;
+	example = ''pkgs.writeText "httpd.conf" "# my custom config file ...";'';
+        description = "
+          Overridable config file to use for Apache. By default, use the
+          file automatically generated by nixos.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          These configuration lines will be appended to the Apache config
+          file. Note that this mechanism may not work when <option>configFile</option>
+          is overridden.
+        ";
+      };
+
+      extraModules = mkOption {
+        default = [];
+        example = [ "proxy_connect" { name = "php5"; path = "${php}/modules/libphp5.so"; } ];
+        description = ''
+          Specifies additional Apache modules.  These can be specified
+          as a string in the case of modules distributed with Apache,
+          or as an attribute set specifying the
+          <varname>name</varname> and <varname>path</varname> of the
+          module.
+        '';
+      };
+
+      logPerVirtualHost = mkOption {
+        default = false;
+        description = "
+          If enabled, each virtual host gets its own
+          <filename>access_log</filename> and
+          <filename>error_log</filename>, namely suffixed by the
+          <option>hostName</option> of the virtual host.
+        ";
+      };
+
+      user = mkOption {
+        default = "wwwrun";
+        description = "
+          User account under which httpd runs.  The account is created
+          automatically if it doesn't exist.
+        ";
+      };
+
+      group = mkOption {
+        default = "wwwrun";
+        description = "
+          Group under which httpd runs.  The account is created
+          automatically if it doesn't exist.
+        ";
+      };
+
+      logDir = mkOption {
+        default = "/var/log/httpd";
+        description = "
+          Directory for Apache's log files.  It is created automatically.
+        ";
+      };
+
+      stateDir = mkOption {
+        default = "/var/run/httpd";
+        description = "
+          Directory for Apache's transient runtime state (such as PID
+          files).  It is created automatically.  Note that the default,
+          <filename>/var/run/httpd</filename>, is deleted at boot time.
+        ";
+      };
+
+      virtualHosts = mkOption {
+        default = [];
+        example = [
+          { hostName = "foo";
+            documentRoot = "/data/webroot-foo";
+          }
+          { hostName = "bar";
+            documentRoot = "/data/webroot-bar";
+          }
+        ];
+        description = ''
+          Specification of the virtual hosts served by Apache.  Each
+          element should be an attribute set specifying the
+          configuration of the virtual host.  The available options
+          are the non-global options permissible for the main host.
+        '';
+      };
+
+      phpOptions = mkOption {
+        default = "";
+        example =
+          ''
+            date.timezone = "CET"
+          '';
+        description =
+          "Options appended to the PHP configuration file <filename>php.ini</filename>.";
+      };
+
+      multiProcessingModule = mkOption {
+        default = "prefork";
+        example = "worker";
+        type = types.uniq types.string;
+        description =
+          ''
+            Multi-processing module to be used by Apache.  Available
+            modules are <literal>prefork</literal> (the default;
+            handles each request in a separate child process),
+            <literal>worker</literal> (hybrid approach that starts a
+            number of child processes each running a number of
+            threads) and <literal>event</literal> (a recent variant of
+            <literal>worker</literal> that handles persistent
+            connections more efficiently).
+          '';
+      };
+
+      maxClients = mkOption {
+        default = 150;
+        example = 8;
+        description = "Maximum number of httpd processes (prefork)";
+      };
+
+      maxRequestsPerChild = mkOption {
+        default = 0;
+        example = 500;
+        description =
+          "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
+      };
+    }
+
+    # Include the options shared between the main server and virtual hosts.
+    // (import ./per-server-options.nix {
+      inherit mkOption;
+      forMainServer = true;
+    });
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.httpd.enable {
+
+    users.extraUsers = optionalAttrs (mainCfg.user == "wwwrun") singleton
+      { name = "wwwrun";
+        group = "wwwrun";
+        description = "Apache httpd user";
+        uid = config.ids.uids.wwwrun;
+      };
+
+    users.extraGroups = optionalAttrs (mainCfg.group == "wwwrun") singleton
+      { name = "wwwrun";
+        gid = config.ids.gids.wwwrun;
+      };
+
+    environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
+
+    services.httpd.phpOptions =
+      ''
+        ; Needed for PHP's mail() function.
+        sendmail_path = sendmail -t -i
+
+        ; Apparently PHP doesn't use $TZ.
+        date.timezone = "${config.time.timeZone}"
+      '';
+
+    systemd.services.httpd =
+      { description = "Apache HTTPD";
+
+        wantedBy = [ "multi-user.target" ];
+        requires = [ "keys.target" ];
+        after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
+
+        path =
+          [ httpd pkgs.coreutils pkgs.gnugrep ]
+          ++ # Needed for PHP's mail() function.  !!! Probably the
+             # ssmtp module should export the path to sendmail in
+             # some way.
+             optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp
+          ++ concatMap (svc: svc.extraServerPath) allSubservices;
+
+        environment =
+          { PHPRC = if enablePHP then phpIni else "";
+          } // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+
+        preStart =
+          ''
+            mkdir -m 0750 -p ${mainCfg.stateDir}
+            chown root.${mainCfg.group} ${mainCfg.stateDir}
+            ${optionalString version24 ''
+              mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
+              chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
+            ''}
+            mkdir -m 0700 -p ${mainCfg.logDir}
+
+            ${optionalString (mainCfg.documentRoot != null)
+            ''
+              # Create the document root directory if does not exists yet
+              mkdir -p ${mainCfg.documentRoot}
+            ''
+            }
+
+            # Get rid of old semaphores.  These tend to accumulate across
+            # server restarts, eventually preventing it from restarting
+            # successfully.
+            for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
+                ${pkgs.utillinux}/bin/ipcrm -s $i
+            done
+
+            # Run the startup hooks for the subservices.
+            for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
+                echo Running Apache startup hook $i...
+                $i
+            done
+          '';
+
+        serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
+        serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
+        serviceConfig.Type = "forking";
+        serviceConfig.Restart = "always";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
new file mode 100644
index 00000000000..dcc05b03891
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -0,0 +1,303 @@
+{ config, pkgs, serverInfo, php, ... }:
+
+with pkgs.lib;
+
+let
+
+  mediawikiConfig = pkgs.writeText "LocalSettings.php"
+    ''
+      <?php
+        # Copied verbatim from the default (generated) LocalSettings.php.
+        if( defined( 'MW_INSTALL_PATH' ) ) {
+                $IP = MW_INSTALL_PATH;
+        } else {
+                $IP = dirname( __FILE__ );
+        }
+
+        $path = array( $IP, "$IP/includes", "$IP/languages" );
+        set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );
+
+        require_once( "$IP/includes/DefaultSettings.php" );
+
+        if ( $wgCommandLineMode ) {
+                if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
+                        die( "This script must be run from the command line\n" );
+                }
+        }
+
+        $wgScriptPath = "${config.urlPrefix}";
+
+        # We probably need to set $wgSecretKey and $wgCacheEpoch.
+
+        # Paths to external programs.
+        $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
+        $wgDiff = "${pkgs.diffutils}/bin/diff";
+        $wgImageMagickConvertCommand = "${pkgs.imagemagick}/bin/convert";
+
+        #$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt";
+
+        # Database configuration.
+        $wgDBtype = "${config.dbType}";
+        $wgDBserver = "${config.dbServer}";
+        $wgDBuser = "${config.dbUser}";
+        $wgDBpassword = "${config.dbPassword}";
+        $wgDBname = "${config.dbName}";
+
+        # E-mail.
+        $wgEmergencyContact = "${config.emergencyContact}";
+        $wgPasswordSender = "${config.passwordSender}";
+
+        $wgSitename = "${config.siteName}";
+
+        ${optionalString (config.logo != "") ''
+          $wgLogo = "${config.logo}";
+        ''}
+
+        ${optionalString (config.articleUrlPrefix != "") ''
+          $wgArticlePath = "${config.articleUrlPrefix}/$1";
+        ''}
+
+        ${optionalString config.enableUploads ''
+          $wgEnableUploads = true;
+          $wgUploadDirectory = "${config.uploadDir}";
+        ''}
+
+        ${optionalString (config.defaultSkin != "") ''
+          $wgDefaultSkin = "${config.defaultSkin}";
+        ''}
+
+        ${config.extraConfig}
+      ?>
+    '';
+
+  # Unpack Mediawiki and put the config file in its root directory.
+  mediawikiRoot = pkgs.stdenv.mkDerivation rec {
+    name= "mediawiki-1.20.5";
+
+    src = pkgs.fetchurl {
+      url = "http://download.wikimedia.org/mediawiki/1.20/${name}.tar.gz";
+      sha256 = "0ix6khrilfdncjqnh41xjs0bd49i1q0rywycjaixjfpwj6vjbqbl";
+    };
+
+    skins = config.skins;
+
+    buildPhase =
+      ''
+        for skin in $skins; do
+          cp -prvd $skin/* skins/
+        done
+      ''; # */
+
+    installPhase =
+      ''
+        ensureDir $out
+        cp -r * $out
+        cp ${mediawikiConfig} $out/LocalSettings.php
+      '';
+  };
+
+  mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts"
+    { buildInputs = [ pkgs.makeWrapper ]; }
+    ''
+      ensureDir $out/bin
+      for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
+        makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \
+          --add-flags ${mediawikiRoot}/maintenance/$i
+      done
+    '';
+
+in
+
+{
+
+  extraConfig =
+    ''
+      ${optionalString config.enableUploads ''
+        Alias ${config.urlPrefix}/images ${config.uploadDir}
+
+        <Directory ${config.uploadDir}>
+            Order allow,deny
+            Allow from all
+            Options -Indexes
+        </Directory>
+      ''}
+
+      Alias ${config.urlPrefix} ${mediawikiRoot}
+
+      <Directory ${mediawikiRoot}>
+          Order allow,deny
+          Allow from all
+          DirectoryIndex index.php
+      </Directory>
+
+      ${optionalString (config.articleUrlPrefix != "") ''
+        Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php
+      ''}
+    '';
+
+  enablePHP = true;
+
+  options = {
+
+    id = mkOption {
+      default = "main";
+      description = ''
+        A unique identifier necessary to keep multiple MediaWiki server
+        instances on the same machine apart.  This is used to
+        disambiguate the administrative scripts, which get names like
+        mediawiki-$id-change-password.
+      '';
+    };
+
+    dbType = mkOption {
+      default = "postgres";
+      example = "mysql";
+      description = "Database type.";
+    };
+
+    dbName = mkOption {
+      default = "mediawiki";
+      description = "Name of the database that holds the MediaWiki data.";
+    };
+
+    dbServer = mkOption {
+      default = ""; # use a Unix domain socket
+      example = "10.0.2.2";
+      description = ''
+        The location of the database server.  Leave empty to use a
+        database server running on the same machine through a Unix
+        domain socket.
+      '';
+    };
+
+    dbUser = mkOption {
+      default = "mediawiki";
+      description = "The user name for accessing the database.";
+    };
+
+    dbPassword = mkOption {
+      default = "";
+      example = "foobar";
+      description = ''
+        The password of the database user.  Warning: this is stored in
+        cleartext in the Nix store!
+      '';
+    };
+
+    emergencyContact = mkOption {
+      default = serverInfo.serverConfig.adminAddr;
+      example = "admin@example.com";
+      description = ''
+        Emergency contact e-mail address.  Defaults to the Apache
+        admin address.
+      '';
+    };
+
+    passwordSender = mkOption {
+      default = serverInfo.serverConfig.adminAddr;
+      example = "password@example.com";
+      description = ''
+        E-mail address from which password confirmations originate.
+        Defaults to the Apache admin address.
+      '';
+    };
+
+    siteName = mkOption {
+      default = "MediaWiki";
+      example = "Foobar Wiki";
+      description = "Name of the wiki";
+    };
+
+    logo = mkOption {
+      default = "";
+      example = "/images/logo.png";
+      description = "The URL of the site's logo (which should be a 135x135px image).";
+    };
+
+    urlPrefix = mkOption {
+      default = "/w";
+      description = ''
+        The URL prefix under which the Mediawiki service appears.
+      '';
+    };
+
+    articleUrlPrefix = mkOption {
+      default = "/wiki";
+      example = "";
+      description = ''
+        The URL prefix under which article pages appear,
+        e.g. http://server/wiki/Page.  Leave empty to use the main URL
+        prefix, e.g. http://server/w/index.php?title=Page.
+      '';
+    };
+
+    enableUploads = mkOption {
+      default = false;
+      description = "Whether to enable file uploads.";
+    };
+
+    uploadDir = mkOption {
+      default = throw "You must specify `uploadDir'.";
+      example = "/data/mediawiki-upload";
+      description = "The directory that stores uploaded files.";
+    };
+
+    defaultSkin = mkOption {
+      default = "";
+      example = "nostalgia";
+      description = "Set this value to change the default skin used by MediaWiki.";
+    };
+
+    skins = mkOption {
+      default = [];
+      type = types.listOf types.path;
+      description =
+        ''
+          List of paths whose content is copied to the ‘skins’
+          subdirectory of the MediaWiki installation.
+        '';
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      example =
+        ''
+          $wgEnableEmail = false;
+        '';
+      description = ''
+        Any additional text to be appended to MediaWiki's
+        configuration file.  This is a PHP script.  For configuration
+        settings, see <link xlink:href='http://www.mediawiki.org/wiki/Manual:Configuration_settings'/>.
+      '';
+    };
+
+  };
+
+  extraPath = [ mediawikiScripts ];
+
+  # !!! Need to specify that Apache has a dependency on PostgreSQL!
+
+  startupScript = pkgs.writeScript "mediawiki_startup.sh"
+    # Initialise the database automagically if we're using a Postgres
+    # server on localhost.
+    (optionalString (config.dbType == "postgres" && config.dbServer == "") ''
+      if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then
+          ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+          ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}"
+          ( echo 'CREATE LANGUAGE plpgsql;'
+            cat ${mediawikiRoot}/maintenance/postgres/tables.sql
+            echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );'
+            echo COMMIT
+          ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
+      fi
+    '');
+
+  robotsEntries = optionalString (config.articleUrlPrefix != "")
+    ''
+      User-agent: *
+      Disallow: ${config.urlPrefix}/
+      Disallow: ${config.articleUrlPrefix}/Special:Search
+      Disallow: ${config.articleUrlPrefix}/Special:Random
+    '';
+
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/mercurial.nix b/nixos/modules/services/web-servers/apache-httpd/mercurial.nix
new file mode 100644
index 00000000000..755b595c783
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/mercurial.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, serverInfo, ... }:
+
+let
+  inherit (pkgs) mercurial;
+  inherit (pkgs.lib) mkOption;
+
+  urlPrefix = config.urlPrefix;
+
+  cgi = pkgs.stdenv.mkDerivation {
+    name = "mercurial-cgi";
+    buildCommand = ''
+      ensureDir $out
+      cp -v ${mercurial}/share/cgi-bin/hgweb.cgi $out
+      sed -i "s|/path/to/repo/or/config|$out/hgweb.config|" $out/hgweb.cgi
+      echo "
+      [collections]
+      ${config.dataDir} = ${config.dataDir}
+      [web]
+      style = gitweb
+      allow_push = *
+      " > $out/hgweb.config
+    '';
+  };
+
+in {
+
+  extraConfig = ''
+    RewriteEngine on
+    RewriteRule /(.*) ${cgi}/hgweb.cgi/$1
+
+    <Location "${urlPrefix}">
+        AuthType Basic
+        AuthName "Mercurial repositories"
+        AuthUserFile ${config.dataDir}/hgusers
+        <LimitExcept GET>
+            Require valid-user
+        </LimitExcept>
+    </Location>
+    <Directory "${cgi}">
+        Order allow,deny
+        Allow from all
+        AllowOverride All
+        Options ExecCGI
+        AddHandler cgi-script .cgi
+        PassEnv PYTHONPATH
+    </Directory>
+  '';
+
+  robotsEntries = ''
+    User-agent: *
+    Disallow: ${urlPrefix}
+  '';
+
+  extraServerPath = [ pkgs.python ];
+
+  globalEnvVars = [ { name = "PYTHONPATH"; value = "${mercurial}/lib/${pkgs.python.libPrefix}/site-packages"; } ];
+
+  options = {
+    urlPrefix = mkOption {
+      default = "/hg";
+      description = "
+        The URL prefix under which the Mercurial service appears.
+        Use the empty string to have it appear in the server root.
+      ";
+    };
+
+    dataDir = mkOption {
+      example = "/data/mercurial";
+      description = "
+        Path to the directory that holds the repositories.
+      ";
+    };
+  };
+
+}
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
new file mode 100644
index 00000000000..a5227bae2d4
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -0,0 +1,146 @@
+# This file defines the options that can be used both for the Apache
+# main server configuration, and for the virtual hosts.  (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{forMainServer, mkOption}:
+
+{
+
+  hostName = mkOption {
+    default = "localhost";
+    description = "
+      Canonical hostname for the server.
+    ";
+  };
+
+  serverAliases = mkOption {
+    default = [];
+    example = ["www.example.org" "www.example.org:8080" "example.org"];
+    description = "
+      Additional names of virtual hosts served by this virtual host configuration.
+    ";
+  };
+
+  port = mkOption {
+    default = 0;
+    description = "
+      Port for the server.  0 means use the default port: 80 for http
+      and 443 for https (i.e. when enableSSL is set).
+    ";
+  };
+
+  enableSSL = mkOption {
+    default = false;
+    description = "
+      Whether to enable SSL (https) support.
+    ";
+  };
+
+  # Note: sslServerCert and sslServerKey can be left empty, but this
+  # only makes sense for virtual hosts (they will inherit from the
+  # main server).
+
+  sslServerCert = mkOption {
+    default = "";
+    example = "/var/host.cert";
+    description = "
+      Path to server SSL certificate.
+    ";
+  };
+
+  sslServerKey = mkOption {
+    default = "";
+    example = "/var/host.key";
+    description = "
+      Path to server SSL certificate key.
+    ";
+  };
+
+  adminAddr = mkOption ({
+    example = "admin@example.org";
+    description = "
+      E-mail address of the server administrator.
+    ";
+  } // (if forMainServer then {} else {default = "";}));
+
+  documentRoot = mkOption {
+    default = null;
+    example = "/data/webserver/docs";
+    description = "
+      The path of Apache's document root directory.  If left undefined,
+      an empty directory in the Nix store will be used as root.
+    ";
+  };
+
+  servedDirs = mkOption {
+    default = [];
+    example = [
+      { urlPath = "/nix";
+        dir = "/home/eelco/Dev/nix-homepage";
+      }
+    ];
+    description = "
+      This option provides a simple way to serve static directories.
+    ";
+  };
+
+  servedFiles = mkOption {
+    default = [];
+    example = [
+      { urlPath = "/foo/bar.png";
+        dir = "/home/eelco/some-file.png";
+      }
+    ];
+    description = "
+      This option provides a simple way to serve individual, static files.
+    ";
+  };
+
+  extraConfig = mkOption {
+    default = "";
+    example = ''
+      <Directory /home>
+        Options FollowSymlinks
+        AllowOverride All
+      </Directory>
+    '';
+    description = "
+      These lines go to httpd.conf verbatim. They will go after
+      directories and directory aliases defined by default.
+    ";
+  };
+
+  extraSubservices = mkOption {
+    default = [];
+    description = "
+      Extra subservices to enable in the webserver.
+    ";
+  };
+
+  enableUserDir = mkOption {
+    default = false;
+    description = "
+      Whether to enable serving <filename>~/public_html</filename> as
+      <literal>/~<replaceable>username</replaceable></literal>.
+    ";
+  };
+
+  globalRedirect = mkOption {
+    default = "";
+    example = http://newserver.example.org/;
+    description = "
+      If set, all requests for this host are redirected permanently to
+      the given URL.
+    ";
+  };
+
+  logFormat = mkOption {
+    default = "common";
+    example = "combined";
+    description = "
+      Log format for Apache's log files. Possible values are: combined, common, referer, agent.
+    ";
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix b/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
new file mode 100644
index 00000000000..f12ae842b58
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
@@ -0,0 +1,95 @@
+{ config, pkgs, serverInfo, ... }:
+
+let
+  extraWorkersProperties = pkgs.lib.optionalString (config ? extraWorkersProperties) config.extraWorkersProperties;
+  
+  workersProperties = pkgs.writeText "workers.properties" ''
+# Define list of workers that will be used
+# for mapping requests
+# The configuration directives are valid
+# for the mod_jk version 1.2.18 and later
+#
+worker.list=loadbalancer,status
+
+# Define Node1
+# modify the host as your host IP or DNS name.
+worker.node1.port=8009
+worker.node1.host=localhost
+worker.node1.type=ajp13
+worker.node1.lbfactor=1
+
+# Load-balancing behaviour
+worker.loadbalancer.type=lb
+worker.loadbalancer.balance_workers=node1
+
+# Status worker for managing load balancer
+worker.status.type=status
+
+${extraWorkersProperties}
+  '';
+in
+{
+  extraModules = [
+    { name = "jk"; path = "${pkgs.tomcat_connectors}/modules/mod_jk.so"; }
+  ];
+
+  extraConfig = ''
+# Where to find workers.properties
+JkWorkersFile ${workersProperties}
+
+# Where to put jk logs
+JkLogFile ${config.logDir}/mod_jk.log
+
+# Set the jk log level [debug/error/info]
+JkLogLevel info
+
+# Select the log format
+JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
+
+# JkOptions indicates to send SSK KEY SIZE
+# Note: Changed from +ForwardURICompat.
+# See http://tomcat.apache.org/security-jk.html
+JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories
+
+# JkRequestLogFormat
+JkRequestLogFormat "%w %V %T"
+
+# Mount your applications
+JkMount /__application__/* loadbalancer
+
+# You can use external file for mount points.
+# It will be checked for updates each 60 seconds.
+# The format of the file is: /url=worker
+# /examples/*=loadbalancer
+#JkMountFile uriworkermap.properties
+
+# Add shared memory.
+# This directive is present with 1.2.10 and
+# later versions of mod_jk, and is needed for
+# for load balancing to work properly
+# Note: Replaced JkShmFile logs/jk.shm due to SELinux issues. Refer to
+# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=225452
+JkShmFile ${config.stateDir}/jk.shm
+
+# Static files in all Tomcat webapp context directories are served by apache
+JkAutoAlias /var/tomcat/webapps
+
+# All requests go to worker by default
+JkMount /* loadbalancer
+# Serve some static files using httpd
+#JkUnMount /*.html loadbalancer
+#JkUnMount /*.jpg  loadbalancer
+#JkUnMount /*.gif  loadbalancer
+#JkUnMount /*.css  loadbalancer
+#JkUnMount /*.png  loadbalancer
+#JkUnMount /*.js  loadbalancer
+
+# Add jkstatus for managing runtime data
+<Location /jkstatus/>
+JkMount status
+Order deny,allow
+Deny from all
+Allow from 127.0.0.1
+</Location>
+  '';
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixos/modules/services/web-servers/apache-httpd/trac.nix
new file mode 100644
index 00000000000..dc82fd34f2f
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/trac.nix
@@ -0,0 +1,121 @@
+{ config, pkgs, serverInfo, ... }:
+
+with pkgs.lib;
+
+let
+
+  # Build a Subversion instance with Apache modules and Swig/Python bindings.
+  subversion = pkgs.subversion.override (origArgs: {
+    bdbSupport = true;
+    httpServer = true;
+    sslSupport = true;
+    compressionSupport = true;
+    pythonBindings = true;
+  });
+
+  pythonLib = p: "${p}/";
+
+in
+
+{
+
+  options = {
+
+    projectsLocation = mkOption {
+      description = "URL path in which Trac projects can be accessed";
+      default = "/projects";
+    };
+
+    projects = mkOption {
+      description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created.";
+      default = [];
+      example =
+        [ { identifier = "myproject";
+            name = "My Project";
+            databaseURL="postgres://root:password@/tracdb";
+            subversionRepository="/data/subversion/myproject";
+          }
+        ];
+    };
+
+    user = mkOption {
+      default = "wwwrun";
+      description = "User account under which Trac runs.";
+    };
+
+    group = mkOption {
+      default = "wwwrun";
+      description = "Group under which Trac runs.";
+    };
+
+    ldapAuthentication = {
+      enable = mkOption {
+        default = false;
+        description = "Enable the ldap authentication in trac";
+      };
+
+      url = mkOption {
+        default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)";
+        description = "URL of the LDAP authentication";
+      };
+
+      name = mkOption {
+        default = "Trac server";
+        description = "AuthName";
+      };
+    };
+
+  };
+
+  extraModules = singleton
+    { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; };
+
+  extraConfig = ''
+    <Location ${config.projectsLocation}>
+      SetHandler mod_python
+      PythonHandler trac.web.modpython_frontend
+      PythonOption TracEnvParentDir /var/trac/projects
+      PythonOption TracUriRoot ${config.projectsLocation}
+      PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache
+    </Location>
+    ${if config.ldapAuthentication.enable then ''
+      <LocationMatch "^${config.projectsLocation}[^/]+/login$">
+        AuthType Basic
+        AuthName "${config.ldapAuthentication.name}"
+        AuthBasicProvider "ldap"
+        AuthLDAPURL "${config.ldapAuthentication.url}"
+        authzldapauthoritative Off
+        require valid-user
+      </LocationMatch>
+    '' else ""}
+  '';
+
+  globalEnvVars = singleton
+    { name = "PYTHONPATH";
+      value =
+        makeSearchPath "lib/${pkgs.python.libPrefix}/site-packages"
+          [ pkgs.mod_python
+            pkgs.pythonPackages.trac
+            pkgs.setuptools
+            pkgs.pythonPackages.genshi
+            pkgs.pythonPackages.psycopg2
+            pkgs.python.modules.sqlite3
+            subversion
+          ];
+    };
+
+  startupScript = pkgs.writeScript "activateTrac" ''
+    mkdir -p /var/trac
+    chown ${config.user}:${config.group} /var/trac
+
+    ${concatMapStrings (project:
+      ''
+        if [ ! -d /var/trac/${project.identifier} ]
+        then
+            export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages
+            ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}"
+        fi
+      '' ) (config.projects)}
+  '';
+
+}
diff --git a/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
new file mode 100644
index 00000000000..a6e6042fdf6
--- /dev/null
+++ b/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
@@ -0,0 +1,82 @@
+{ config, pkgs, serverInfo, ... }:
+
+let
+
+  # The Zabbix PHP frontend needs to be able to write its
+  # configuration settings (the connection info to the database) to
+  # the "conf" subdirectory.  So symlink $out/conf to some directory
+  # outside of the Nix store where we want to keep this stateful info.
+  # Note that different instances of the frontend will therefore end
+  # up with their own copies of the PHP sources.  !!! Alternatively,
+  # we could generate zabbix.conf.php declaratively.
+  zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {}
+    ''
+      cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out"
+      chmod -R u+w $out
+      ln -s "${if config.configFile == null
+               then "${config.stateDir}/zabbix.conf.php"
+               else config.configFile}" "$out/conf/zabbix.conf.php"
+    '';
+
+in
+
+{
+
+  enablePHP = true;
+
+  phpOptions =
+    ''
+      post_max_size = 32M
+      max_execution_time = 300
+      max_input_time = 300
+    '';
+
+  extraConfig = ''
+    Alias ${config.urlPrefix}/ ${zabbixPHP}/
+
+    <Directory ${zabbixPHP}>
+      DirectoryIndex index.php
+      Order deny,allow
+      Allow from *
+    </Directory>
+  '';
+
+  startupScript = pkgs.writeScript "zabbix-startup-hook" ''
+    mkdir -p ${config.stateDir}
+    chown -R ${serverInfo.serverConfig.user} ${config.stateDir}
+  '';
+
+  # The frontend needs "ps" to find out whether zabbix_server is running.
+  extraServerPath = [ pkgs.procps ];
+
+  options = {
+
+    urlPrefix = pkgs.lib.mkOption {
+      default = "/zabbix";
+      description = "
+        The URL prefix under which the Zabbix service appears.
+        Use the empty string to have it appear in the server root.
+      ";
+    };
+
+    configFile = pkgs.lib.mkOption {
+      default = null;
+      type = with pkgs.lib.types; nullOr path;
+      description = ''
+        The configuration file (zabbix.conf.php) which contains the database
+        connection settings. If not set, the configuration settings will created
+        by the web installer.
+      '';
+    };
+
+    stateDir = pkgs.lib.mkOption {
+      default = "/var/lib/zabbix/frontend";
+      description = "
+        Directory where the dynamically generated configuration data
+        of the PHP frontend will be stored.
+      ";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/jboss/builder.sh b/nixos/modules/services/web-servers/jboss/builder.sh
new file mode 100644
index 00000000000..2eb89a90f67
--- /dev/null
+++ b/nixos/modules/services/web-servers/jboss/builder.sh
@@ -0,0 +1,72 @@
+set -e
+
+source $stdenv/setup
+
+mkdir -p $out/bin
+
+cat > $out/bin/control <<EOF
+mkdir -p $logDir
+chown -R $user $logDir
+export PATH=$PATH:$su/bin
+
+start()
+{
+  su $user -s /bin/sh -c "$jboss/bin/run.sh \
+      -Djboss.server.base.dir=$serverDir \
+      -Djboss.server.base.url=file://$serverDir \
+      -Djboss.server.temp.dir=$tempDir \
+      -Djboss.server.log.dir=$logDir \
+      -Djboss.server.lib.url=$libUrl \
+      -c default"
+}
+
+stop()
+{
+  su $user -s /bin/sh -c "$jboss/bin/shutdown.sh -S"
+}
+
+if test "\$1" = start
+then
+  trap stop 15
+  
+  start
+elif test "\$1" = stop
+then
+  stop  
+elif test "\$1" = init
+then
+  echo "Are you sure you want to create a new server instance (old server instance will be lost!)?"
+  read answer
+
+  if ! test \$answer = "yes"
+  then
+    exit 1
+  fi
+  
+  rm -rf $serverDir
+  mkdir -p $serverDir
+  cd $serverDir
+  cp -av $jboss/server/default .
+  sed -i -e "s|deploy/|$deployDir|" default/conf/jboss-service.xml
+  
+  if ! test "$useJK" = ""
+  then
+    sed -i -e 's|<attribute name="UseJK">false</attribute>|<attribute name="UseJK">true</attribute>|' default/deploy/jboss-web.deployer/META-INF/jboss-service.xml
+    sed -i -e 's|<Engine name="jboss.web" defaultHost="localhost">|<Engine name="jboss.web" defaultHost="localhost" jvmRoute="node1">|' default/deploy/jboss-web.deployer/server.xml
+  fi
+  
+  # Make files accessible for the server user
+  
+  chown -R $user $serverDir
+  for i in \`find $serverDir -type d\`
+  do
+    chmod 755 \$i
+  done
+  for i in \`find $serverDir -type f\`
+  do
+    chmod 644 \$i
+  done
+fi
+EOF
+
+chmod +x $out/bin/*
diff --git a/nixos/modules/services/web-servers/jboss/default.nix b/nixos/modules/services/web-servers/jboss/default.nix
new file mode 100644
index 00000000000..e1bcede6563
--- /dev/null
+++ b/nixos/modules/services/web-servers/jboss/default.nix
@@ -0,0 +1,83 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.jboss;
+
+  jbossService = pkgs.stdenv.mkDerivation {
+    name = "jboss-server";
+    builder = ./builder.sh;
+    inherit (pkgs) jboss su;
+    inherit (cfg) tempDir logDir libUrl deployDir serverDir user useJK;
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.jboss = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable jboss";
+      };
+
+      tempDir = mkOption {
+        default = "/tmp";
+        description = "Location where JBoss stores its temp files";
+      };
+
+      logDir = mkOption {
+        default = "/var/log/jboss";
+        description = "Location of the logfile directory of JBoss";
+      };
+
+      serverDir = mkOption {
+        description = "Location of the server instance files";
+        default = "/var/jboss/server";
+      };
+
+      deployDir = mkOption {
+        description = "Location of the deployment files";
+        default = "/nix/var/nix/profiles/default/server/default/deploy/";
+      };
+
+      libUrl = mkOption {
+        default = "file:///nix/var/nix/profiles/default/server/default/lib";
+        description = "Location where the shared library JARs are stored";
+      };
+
+      user = mkOption {
+        default = "nobody";
+        description = "User account under which jboss runs.";
+      };
+
+      useJK = mkOption {
+        default = false;
+        description = "Whether to use to connector to the Apache HTTP server";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.jboss.enable {
+
+    jobs.jboss =
+      { description = "JBoss server";
+
+        exec = "${jbossService}/bin/control start";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/lighttpd/cgit.nix b/nixos/modules/services/web-servers/lighttpd/cgit.nix
new file mode 100644
index 00000000000..62264f1db45
--- /dev/null
+++ b/nixos/modules/services/web-servers/lighttpd/cgit.nix
@@ -0,0 +1,65 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.lighttpd.cgit;
+  configFile = pkgs.writeText "cgitrc"
+    ''
+      ${cfg.configText}
+    '';
+in
+{
+
+  options.services.lighttpd.cgit = {
+
+    enable = mkOption {
+      default = false;
+      type = types.uniq types.bool;
+      description = ''
+        If true, enable cgit (fast web interface for git repositories) as a
+        sub-service in lighttpd. cgit will be accessible at
+        http://yourserver/cgit
+      '';
+    };
+
+    configText = mkOption {
+      default = "";
+      example = ''
+        cache-size=1000
+        scan-path=/srv/git
+      '';
+      type = types.string;
+      description = ''
+        Verbatim contents of the cgit runtime configuration file. Documentation
+        (with cgitrc example file) is available in "man cgitrc". Or online:
+        http://git.zx2c4.com/cgit/tree/cgitrc.5.txt
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # make the cgitrc manpage available
+    environment.systemPackages = [ pkgs.cgit ];
+
+    services.lighttpd.extraConfig = ''
+      $HTTP["url"] =~ "^/cgit" {
+          cgi.assign = (
+              "cgit.cgi" => "${pkgs.cgit}/cgit/cgit.cgi"
+          )
+          alias.url = (
+              "/cgit.css" => "${pkgs.cgit}/cgit/cgit.css",
+              "/cgit.png" => "${pkgs.cgit}/cgit/cgit.png",
+              "/cgit"     => "${pkgs.cgit}/cgit/cgit.cgi"
+          )
+          setenv.add-environment = (
+              "CGIT_CONFIG" => "${configFile}"
+          )
+      }
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
new file mode 100644
index 00000000000..f9e40fc4b54
--- /dev/null
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -0,0 +1,178 @@
+# NixOS module for lighttpd web server
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.lighttpd;
+
+  needModRedirect = cfg.gitweb.enable;
+  needModAlias = cfg.cgit.enable or cfg.gitweb.enable;
+  needModSetenv = cfg.cgit.enable or cfg.gitweb.enable;
+  needModCgi = cfg.cgit.enable or cfg.gitweb.enable;
+  needModStatus = cfg.mod_status;
+  needModUserdir = cfg.mod_userdir;
+
+  configFile = if cfg.configText != "" then
+    pkgs.writeText "lighttpd.conf" ''
+      ${cfg.configText}
+    ''
+    else
+    pkgs.writeText "lighttpd.conf" ''
+      server.document-root = "${cfg.document-root}"
+      server.port = ${toString cfg.port}
+      server.username = "lighttpd"
+      server.groupname = "lighttpd"
+
+      # As for why all modules are loaded here, instead of having small
+      # server.modules += () entries in each sub-service extraConfig snippet,
+      # read this:
+      #
+      #   http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
+      #   http://redmine.lighttpd.net/issues/2337
+      #
+      # Basically, lighttpd doesn't want to load (or even silently ignore) a
+      # module for a second time, and there is no way to check if a module has
+      # been loaded already. So if two services were to put the same module in
+      # server.modules += (), that would break the lighttpd configuration.
+      server.modules = (
+          ${optionalString needModRedirect ''"mod_redirect",''}
+          ${optionalString needModAlias ''"mod_alias",''}
+          ${optionalString needModSetenv ''"mod_setenv",''}
+          ${optionalString needModCgi ''"mod_cgi",''}
+          ${optionalString needModStatus ''"mod_status",''}
+          ${optionalString needModUserdir ''"mod_userdir",''}
+          "mod_accesslog"
+      )
+
+      # Logging (logs end up in systemd journal)
+      accesslog.use-syslog = "enable"
+      server.errorlog-use-syslog = "enable"
+
+      mimetype.assign = (
+          ".html" => "text/html",
+          ".htm" => "text/html",
+          ".txt" => "text/plain",
+          ".jpg" => "image/jpeg",
+          ".png" => "image/png",
+          ".css" => "text/css"
+          )
+
+      static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
+      index-file.names = ( "index.html" )
+
+      ${if cfg.mod_userdir then ''
+        userdir.path = "public_html"
+      '' else ""}
+
+      ${if cfg.mod_status then ''
+        status.status-url = "/server-status"
+        status.statistics-url = "/server-statistics"
+        status.config-url = "/server-config"
+      '' else ""}
+
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  options = {
+
+    services.lighttpd = {
+
+      enable = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          Enable the lighttpd web server.
+        '';
+      };
+
+      port = mkOption {
+        default = 80;
+        type = types.uniq types.int;
+        description = ''
+          TCP port number for lighttpd to bind to.
+        '';
+      };
+
+      document-root = mkOption {
+        default = "/srv/www";
+        type = types.uniq types.string;
+        description = ''
+          Document-root of the web server. Must be readable by the "lighttpd" user.
+        '';
+      };
+
+      mod_userdir = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          If true, requests in the form /~user/page.html are rewritten to take
+          the file public_html/page.html from the home directory of the user.
+        '';
+      };
+
+      mod_status = mkOption {
+        default = false;
+        type = types.uniq types.bool;
+        description = ''
+          Show server status overview at /server-status, statistics at
+          /server-statistics and list of loaded modules at /server-config.
+        '';
+      };
+
+      configText = mkOption {
+        default = "";
+        type = types.string;
+	example = ''...verbatim config file contents...'';
+        description = ''
+          Overridable config file contents to use for lighttpd. By default, use
+          the contents automatically generated by NixOS.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          These configuration lines will be appended to the generated lighttpd
+          config file. Note that this mechanism does not work when the manual
+          <option>configText</option> option is used.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.lighttpd = {
+      description = "Lighttpd Web Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        ${if cfg.cgit.enable then ''
+          mkdir -p /var/cache/cgit
+          chown lighttpd:lighttpd /var/cache/cgit
+        '' else ""}
+      '';
+      serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
+      # SIGINT => graceful shutdown
+      serviceConfig.KillSignal = "SIGINT";
+    };
+
+    users.extraUsers.lighttpd = {
+      group = "lighttpd";
+      description = "lighttpd web server privilege separation user";
+      uid = config.ids.uids.lighttpd;
+    };
+
+    users.extraGroups.lighttpd.gid = config.ids.gids.lighttpd;
+  };
+}
diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
new file mode 100644
index 00000000000..d759d8144b6
--- /dev/null
+++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.lighttpd.gitweb;
+  gitwebConfigFile = pkgs.writeText "gitweb.conf" ''
+    # path to git projects (<project>.git)
+    $projectroot = "${cfg.projectroot}";
+    ${cfg.extraConfig}
+  '';
+
+in
+{
+
+  options.services.lighttpd.gitweb = {
+
+    enable = mkOption {
+      default = false;
+      type = types.uniq types.bool;
+      description = ''
+        If true, enable gitweb in lighttpd. Access it at http://yourserver/gitweb
+      '';
+    };
+
+    projectroot = mkOption {
+      default = "/srv/git";
+      type = types.uniq types.string;
+      description = ''
+        Path to git projects (bare repositories) that should be served by
+        gitweb. Must not end with a slash.
+      '';
+    };
+
+    extraConfig = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      description = ''
+        Verbatim configuration text appended to the generated gitweb.conf file.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.lighttpd.extraConfig = ''
+      $HTTP["url"] =~ "^/gitweb" {
+          cgi.assign = (
+              ".cgi" => "${pkgs.perl}/bin/perl"
+          )
+          url.redirect = (
+              "^/gitweb$" => "/gitweb/"
+          )
+          alias.url = (
+              "/gitweb/static/" => "${pkgs.git}/share/gitweb/static/",
+              "/gitweb/"        => "${pkgs.git}/share/gitweb/gitweb.cgi"
+          )
+          setenv.add-environment = (
+              "GITWEB_CONFIG" => "${gitwebConfigFile}"
+          )
+      }
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
new file mode 100644
index 00000000000..b26af1aa744
--- /dev/null
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -0,0 +1,88 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.nginx;
+  nginx = pkgs.nginx.override { fullWebDAV = cfg.fullWebDAV; };
+  configFile = pkgs.writeText "nginx.conf" ''
+    user ${cfg.user} ${cfg.group};
+    daemon off;
+    ${cfg.config}
+  '';
+in
+
+{
+  options = {
+    services.nginx = {
+      enable = mkOption {
+        default = false;
+        description = "
+          Enable the nginx Web Server.
+        ";
+      };
+
+      config = mkOption {
+        default = "events {}";
+        description = "
+          Verbatim nginx.conf configuration.
+        ";
+      };
+
+      stateDir = mkOption {
+        default = "/var/spool/nginx";
+        description = "
+          Directory holding all state for nginx to run.
+        ";
+      };
+
+      user = mkOption {
+        default = "nginx";
+        description = "User account under which nginx runs.";
+      };
+
+      group = mkOption {
+        default = "nginx";
+        description = "Group account under which nginx runs.";
+      };
+
+      fullWebDAV = mkOption {
+        default = false;
+        description = "Compile in a third party module providing full WebDAV support";
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ nginx ];
+
+    # TODO: test user supplied config file pases syntax test
+
+    systemd.services.nginx = {
+      description = "Nginx Web Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ nginx ];
+      preStart =
+        ''
+        mkdir -p ${cfg.stateDir}/logs
+        chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
+        '';
+      serviceConfig = {
+        ExecStart = "${nginx}/bin/nginx -c ${configFile} -p ${cfg.stateDir}";
+      };
+    };
+
+    users.extraUsers = optionalAttrs (cfg.user == "nginx") (singleton
+      { name = "nginx";
+        group = "nginx";
+        uid = config.ids.uids.nginx;
+      });
+
+    users.extraGroups = optionalAttrs (cfg.group == "nginx") (singleton
+      { name = "nginx";
+        gid = config.ids.gids.nginx;
+      });
+  };
+}
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
new file mode 100644
index 00000000000..a68828de5d8
--- /dev/null
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -0,0 +1,344 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.tomcat;
+  tomcat = pkgs.tomcat6;
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.tomcat = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Apache Tomcat";
+      };
+
+      baseDir = mkOption {
+        default = "/var/tomcat";
+        description = "Location where Tomcat stores configuration files, webapplications and logfiles";
+      };
+
+      extraGroups = mkOption {
+        default = [];
+        example = [ "users" ];
+        description = "Defines extra groups to which the tomcat user belongs.";
+      };
+
+      user = mkOption {
+        default = "tomcat";
+        description = "User account under which Apache Tomcat runs.";
+      };
+
+      group = mkOption {
+        default = "tomcat";
+        description = "Group account under which Apache Tomcat runs.";
+      };
+
+      javaOpts = mkOption {
+        default = "";
+        description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
+      };
+
+      catalinaOpts = mkOption {
+        default = "";
+        description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
+      };
+
+      sharedLibs = mkOption {
+        default = [];
+        description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
+      };
+
+      commonLibs = mkOption {
+        default = [];
+        description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
+      };
+
+      webapps = mkOption {
+        default = [ tomcat ];
+        description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
+      };
+
+      virtualHosts = mkOption {
+        default = [];
+        description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
+      };
+
+      logPerVirtualHost = mkOption {
+        default = false;
+        description = "Whether to enable logging per virtual host.";
+      };
+
+      axis2 = {
+
+        enable = mkOption {
+          default = false;
+          description = "Whether to enable an Apache Axis2 container";
+        };
+
+        services = mkOption {
+          default = [];
+          description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
+        };
+
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.tomcat.enable {
+
+    users.extraGroups = singleton
+      { name = "tomcat";
+        gid = config.ids.gids.tomcat;
+      };
+
+    users.extraUsers = singleton
+      { name = "tomcat";
+        uid = config.ids.uids.tomcat;
+        description = "Tomcat user";
+        home = "/homeless-shelter";
+        extraGroups = cfg.extraGroups;
+      };
+
+    jobs.tomcat =
+      { description = "Apache Tomcat server";
+
+        startOn = "started network-interfaces";
+        stopOn = "stopping network-interfaces";
+
+        preStart =
+          ''
+            # Create the base directory
+            mkdir -p ${cfg.baseDir}
+
+            # Create a symlink to the bin directory of the tomcat component
+            ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
+
+            # Create a conf/ directory
+            mkdir -p ${cfg.baseDir}/conf
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf
+
+            # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
+            for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml)
+            do
+                ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
+            done
+
+            # Create subdirectory for virtual hosts
+            mkdir -p ${cfg.baseDir}/virtualhosts
+
+            # Create a modified catalina.properties file
+            # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
+            sed -e 's|''${catalina.home}|''${catalina.base}|g' \
+                -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
+                ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
+
+            # Create a modified server.xml which also includes all virtual hosts
+            sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\  ${
+                         toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\"  prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
+                ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
+
+            # Create a logs/ directory
+            mkdir -p ${cfg.baseDir}/logs
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs
+            ${if cfg.logPerVirtualHost then
+               toString (map (h: ''
+                                    mkdir -p ${cfg.baseDir}/logs/${h.name}
+                                    chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
+                                 '') cfg.virtualHosts) else ''''}
+
+            # Create a temp/ directory
+            mkdir -p ${cfg.baseDir}/temp
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp
+
+            # Create a lib/ directory
+            mkdir -p ${cfg.baseDir}/lib
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib
+
+            # Create a shared/lib directory
+            mkdir -p ${cfg.baseDir}/shared/lib
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib
+
+            # Create a webapps/ directory
+            mkdir -p ${cfg.baseDir}/webapps
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps
+
+            # Symlink all the given common libs files or paths into the lib/ directory
+            for i in ${tomcat} ${toString cfg.commonLibs}
+            do
+                if [ -f $i ]
+                then
+                    # If the given web application is a file, symlink it into the common/lib/ directory
+                    ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
+                elif [ -d $i ]
+                then
+                    # If the given web application is a directory, then iterate over the files
+                    # in the special purpose directories and symlink them into the tomcat tree
+
+                    for j in $i/lib/*
+                    do
+                        ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
+                    done
+                fi
+            done
+
+            # Symlink all the given shared libs files or paths into the shared/lib/ directory
+            for i in ${toString cfg.sharedLibs}
+            do
+                if [ -f $i ]
+                then
+                    # If the given web application is a file, symlink it into the common/lib/ directory
+                    ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
+                elif [ -d $i ]
+                then
+                    # If the given web application is a directory, then iterate over the files
+                    # in the special purpose directories and symlink them into the tomcat tree
+
+                    for j in $i/shared/lib/*
+                    do
+                        ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
+                    done
+                fi
+            done
+
+            # Symlink all the given web applications files or paths into the webapps/ directory
+            for i in ${toString cfg.webapps}
+            do
+                if [ -f $i ]
+                then
+                    # If the given web application is a file, symlink it into the webapps/ directory
+                    ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
+                elif [ -d $i ]
+                then
+                    # If the given web application is a directory, then iterate over the files
+                    # in the special purpose directories and symlink them into the tomcat tree
+
+                    for j in $i/webapps/*
+                    do
+                        ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
+                    done
+
+                    # Also symlink the configuration files if they are included
+                    if [ -d $i/conf/Catalina ]
+                    then
+                        for j in $i/conf/Catalina/*
+                        do
+                            mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
+                            ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+                        done
+                    fi
+                fi
+            done
+
+            ${toString (map (virtualHost: ''
+              # Create webapps directory for the virtual host
+              mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
+
+              # Modify ownership
+              chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
+
+              # Symlink all the given web applications files or paths into the webapps/ directory
+              # of this virtual host
+              for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"
+              do
+                  if [ -f $i ]
+                  then
+                      # If the given web application is a file, symlink it into the webapps/ directory
+                      ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
+                  elif [ -d $i ]
+                  then
+                      # If the given web application is a directory, then iterate over the files
+                      # in the special purpose directories and symlink them into the tomcat tree
+
+                      for j in $i/webapps/*
+                      do
+                          ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
+                      done
+
+                      # Also symlink the configuration files if they are included
+                      if [ -d $i/conf/Catalina ]
+                      then
+                          for j in $i/conf/Catalina/*
+                          do
+                              mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
+                              ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
+                          done
+                      fi
+                  fi
+              done
+
+              ''
+            ) cfg.virtualHosts) }
+
+            # Create a work/ directory
+            mkdir -p ${cfg.baseDir}/work
+            chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work
+
+            ${if cfg.axis2.enable then
+                ''
+                # Copy the Axis2 web application
+                cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
+
+                # Turn off addressing, which causes many errors
+                sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
+
+                # Modify permissions on the Axis2 application
+                chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
+
+                # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
+                for i in ${toString cfg.axis2.services}
+                do
+                    if [ -f $i ]
+                    then
+                        # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
+                        ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
+                    elif [ -d $i ]
+                    then
+                        # If the given web application is a directory, then iterate over the files
+                        # in the special purpose directories and symlink them into the tomcat tree
+
+                        for j in $i/webapps/axis2/WEB-INF/services/*
+                        do
+                            ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
+                        done
+
+                        # Also symlink the configuration files if they are included
+                        if [ -d $i/conf/Catalina ]
+                        then
+                            for j in $i/conf/Catalina/*
+                            do
+                                ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
+                            done
+                        fi
+                    fi
+                done
+                ''
+            else ""}
+
+            ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
+          '';
+
+        postStop =
+          ''
+            echo "Stopping tomcat..."
+            CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
+          '';
+
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
new file mode 100644
index 00000000000..7e327120c3d
--- /dev/null
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, ...}:
+let
+  cfg = config.services.varnish;
+
+in
+with pkgs.lib;
+{
+  options = {
+    services.varnish = {
+      enable = mkOption {
+        default = false;
+        description = "
+          Enable the Varnish Server.
+        ";
+      };
+
+      http_address = mkOption {
+        default = "*:6081";
+        description = "
+          HTTP listen address and port.
+        ";
+      };
+
+      config = mkOption {
+        description = "
+          Verbatim default.vcl configuration.
+        ";
+      };
+
+      stateDir = mkOption {
+        default = "/var/spool/varnish";
+        description = "
+          Directory holding all state for Varnish to run.
+        ";
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.varnish = {
+      description = "Varnish";
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p ${cfg.stateDir}
+        chown -R varnish:varnish ${cfg.stateDir}
+      '';
+      path = [ pkgs.gcc ];
+      serviceConfig.ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -f ${pkgs.writeText "default.vcl" cfg.config} -n ${cfg.stateDir} -u varnish";
+      serviceConfig.Type = "forking";
+    };
+
+    environment.systemPackages = [ pkgs.varnish ];
+
+    users.extraUsers.varnish = {
+      group = "varnish";
+      uid = config.ids.uids.varnish;
+    };
+
+    users.extraGroups.varnish.gid = config.ids.uids.varnish;
+  };
+}
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
new file mode 100644
index 00000000000..19afa55d7fe
--- /dev/null
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -0,0 +1,249 @@
+{ pkgs, config, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.zope2;
+
+  zope2Opts = { name, config, ... }: {
+    options = {
+
+      name = mkOption {
+        default = "${name}";
+        type = types.string;
+        description = "The name of the zope2 instance. If undefined, the name of the attribute set will be used.";
+      };
+
+      threads = mkOption {
+        default = 2;
+        type = types.int;
+        description = "Specify the number of threads that Zope's ZServer web server will use to service requests. ";
+      };
+
+      http_address = mkOption {
+        default = "localhost:8080";
+        type = types.string;
+        description = "Give a port and adress for the HTTP server.";
+      };
+
+      user = mkOption {
+        default = "zope2";
+        type = types.string;
+        description = "The name of the effective user for the Zope process.";
+      };
+
+      extra = mkOption {
+        default =
+          ''
+          <zodb_db main>
+          mount-point /
+          cache-size 30000
+          <blobstorage>
+              blob-dir /var/lib/zope2/${name}/blobstorage
+              <filestorage>
+              path /var/lib/zope2/${name}/filestorage/Data.fs
+              </filestorage>
+          </blobstorage>
+          </zodb_db>
+          '';
+        type = types.string;
+        description = "Extra zope.conf";
+      };
+
+      packages = mkOption {
+        type = types.listOf types.package;
+        description = "The list of packages you want to make available to the zope2 instance.";
+      };
+
+    };
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.zope2.instances = mkOption {
+      default = {};
+      type = types.loaOf types.optionSet;
+      example = {
+        plone01 = {
+          http_address = "127.0.0.1:8080";
+          extra =
+            ''
+            <zodb_db main>
+            mount-point /
+            cache-size 30000
+            <blobstorage>
+                blob-dir /var/lib/zope2/plone01/blobstorage
+                <filestorage>
+                path /var/lib/zope2/plone01/filestorage/Data.fs
+                </filestorage>
+            </blobstorage>
+            </zodb_db>
+            '';
+
+        };
+      };
+      description = "zope2 instances to be created automaticaly by the system.";
+      options = [ zope2Opts ];
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (cfg.instances != {}) {
+
+    users.extraUsers.zope2.uid = config.ids.uids.zope2;
+
+    systemd.services =
+      let
+
+        createZope2Instance = opts: name:
+          let
+            interpreter = pkgs.writeScript "interpreter"
+              ''
+import sys
+
+_interactive = True
+if len(sys.argv) > 1:
+    _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
+    _interactive = False
+    for (_opt, _val) in _options:
+        if _opt == '-i':
+            _interactive = True
+        elif _opt == '-c':
+            exec _val
+        elif _opt == '-m':
+            sys.argv[1:] = _args
+            _args = []
+            __import__("runpy").run_module(
+                 _val, {}, "__main__", alter_sys=True)
+
+    if _args:
+        sys.argv[:] = _args
+        __file__ = _args[0]
+        del _options, _args
+        execfile(__file__)
+
+if _interactive:
+    del _interactive
+    __import__("code").interact(banner="", local=globals())
+              '';
+            env = pkgs.buildEnv {
+              name = "zope2-${name}-env";
+              paths = [
+                pkgs.python27
+                pkgs.python27Packages.recursivePthLoader
+                pkgs.python27Packages."plone.recipe.zope2instance"
+              ] ++ attrValues pkgs.python27.modules
+                ++ opts.packages;
+              postBuild =
+                ''
+                echo "#!$out/bin/python" > $out/bin/interpreter
+                cat ${interpreter} >> $out/bin/interpreter
+                '';
+            };
+            conf = pkgs.writeText "zope2-${name}-conf"
+              ''%define INSTANCEHOME ${env}
+instancehome $INSTANCEHOME
+%define CLIENTHOME /var/lib/zope2/${name}
+clienthome $CLIENTHOME
+
+debug-mode off
+security-policy-implementation C
+verbose-security off
+default-zpublisher-encoding utf-8
+zserver-threads ${toString opts.threads}
+effective-user ${opts.user}
+
+pid-filename /var/lib/zope2/${name}/pid
+lock-filename /var/lib/zope2/${name}/lock
+python-check-interval 1000
+enable-product-installation off
+
+<environment>
+  zope_i18n_compile_mo_files false
+</environment>
+
+<eventlog>
+level INFO
+<logfile>
+    path /var/log/zope2/${name}.log
+    level INFO
+</logfile>
+</eventlog>
+
+<logger access>
+level WARN
+<logfile>
+    path /var/log/zope2/${name}-Z2.log
+    format %(message)s
+</logfile>
+</logger>
+
+<http-server>
+address ${opts.http_address}
+</http-server>
+
+<zodb_db temporary>
+<temporarystorage>
+    name temporary storage for sessioning
+</temporarystorage>
+mount-point /temp_folder
+container-class Products.TemporaryFolder.TemporaryContainer
+</zodb_db>
+
+${opts.extra}
+              '';
+            ctlScript = pkgs.writeScript "zope2-${name}-ctl-script"
+              ''#!${env}/bin/python
+
+import sys
+import plone.recipe.zope2instance.ctl
+
+if __name__ == '__main__':
+    sys.exit(plone.recipe.zope2instance.ctl.main(
+        ["-C", "${conf}"]
+        + sys.argv[1:]))
+              '';
+
+            ctl = pkgs.writeScript "zope2-${name}-ctl"
+              ''#!${pkgs.bash}/bin/bash -e
+export PYTHONHOME=${env}
+exec ${ctlScript} "$@"
+              '';
+          in {
+            description = "zope2 ${name} instance";
+            after = [ "network.target" ];  # with RelStorage also add "postgresql.service"
+            wantedBy = [ "multi-user.target" ];
+            path = opts.packages;
+            preStart =
+              ''
+              mkdir -p /var/log/zope2/
+              touch /var/log/zope2/${name}.log
+              touch /var/log/zope2/${name}-Z2.log
+              chown ${opts.user} /var/log/zope2/${name}.log
+              chown ${opts.user} /var/log/zope2/${name}-Z2.log
+
+              mkdir -p /var/lib/zope2/${name}/filestorage /var/lib/zope2/${name}/blobstorage
+              chown ${opts.user} /var/lib/zope2/${name} -R
+
+              ${ctl} adduser admin admin
+              '';
+
+            serviceConfig.Type = "forking";
+            serviceConfig.ExecStart = "${ctl} start";
+            serviceConfig.ExecStop = "${ctl} stop";
+            serviceConfig.ExecReload = "${ctl} restart";
+          };
+
+      in listToAttrs (map (name: { name = "zope2-${name}"; value = createZope2Instance (builtins.getAttr name cfg.instances) name; }) (builtins.attrNames cfg.instances));
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
new file mode 100644
index 00000000000..0fea74d5ba7
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, ... }:
+
+with pkgs.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);
+
+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 ./gnome.nix ./kde4.nix ./e17.nix ];
+
+  options = {
+
+    services.xserver.desktopManager = {
+
+      session = mkOption {
+        default = [];
+        example = singleton
+          { name = "kde";
+            bgSupport = true;
+            start = "...";
+          };
+        description = "
+          Internal option used to add some common line to desktop manager
+          scripts before forwarding the value to the
+          <varname>displayManager</varname>.
+        ";
+        apply = list: {
+          list = map (d: d // {
+            manage = "desktop";
+            start = d.start
+            + optionalString (needBGCond d) ''
+              if test -e $HOME/.background-image; then
+                ${pkgs.feh}/bin/feh --bg-scale $HOME/.background-image
+              fi
+            '';
+          }) list;
+          needBGPackages = [] != filter needBGCond list;
+        };
+      };
+
+      default = mkOption {
+        default = "";
+        example = "none";
+        description = "Default desktop manager loaded if none have been chosen.";
+        merge = mergeOneOption;
+        apply = defaultDM:
+          if defaultDM == "" && cfg.session.list != [] then
+            (head cfg.session.list).name
+          else if any (w: w.name == defaultDM) cfg.session.list then
+            defaultDM
+          else
+            throw "Default desktop manager ($(defaultDM)) not found.";
+      };
+
+    };
+
+  };
+
+  config = {
+    services.xserver.displayManager.session = cfg.session.list;
+    environment.x11Packages =
+      mkIf cfg.session.needBGPackages [ pkgs.feh ];
+  };
+}
diff --git a/nixos/modules/services/x11/desktop-managers/e17.nix b/nixos/modules/services/x11/desktop-managers/e17.nix
new file mode 100644
index 00000000000..3d91617c62a
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/e17.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.e17;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.e17.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable support for the E17 desktop environment.";
+    };
+
+  };
+
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    services.dbus.packages = [ pkgs.e17.ethumb ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix
new file mode 100644
index 00000000000..b0212446ad3
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.desktopManager.gnome;
+  gnome = pkgs.gnome;
+
+in
+
+{
+
+  options = {
+
+    services.xserver.desktopManager.gnome.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable a gnome terminal as a desktop manager.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.desktopManager.session = singleton
+      { name = "gnome";
+        start = ''
+          ${gnome.gnometerminal}/bin/gnome-terminal -ls &
+          waitPID=$!
+        '';
+      };
+
+    environment.systemPackages =
+      [ gnome.gnometerminal
+        gnome.GConf
+        gnome.gconfeditor
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix
new file mode 100644
index 00000000000..c76acfbcd4e
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/kde4.nix
@@ -0,0 +1,169 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.kde4;
+  xorg = pkgs.xorg;
+
+  # Disable Nepomuk and Strigi by default.  As of KDE 4.7, they don't
+  # really work very well (e.g. searching files often fails to find
+  # files), segfault sometimes and consume significant resources.
+  # They can be re-enabled in the KDE System Settings under "Desktop
+  # Search".
+  nepomukConfig = pkgs.writeTextFile
+    { name = "nepomuk-config";
+      destination = "/share/config/nepomukserverrc";
+      text =
+        ''
+          [Basic Settings]
+          Start Nepomuk=false
+
+          [Service-nepomukstrigiservice]
+          autostart=false
+        '';
+    };
+
+  phononBackends = {
+    gstreamer = [
+      pkgs.phonon_backend_gstreamer
+      pkgs.gst_all.gstPluginsBase
+      pkgs.gst_all.gstPluginsGood
+      pkgs.gst_all.gstPluginsUgly
+      pkgs.gst_all.gstPluginsBad
+      pkgs.gst_all.gstFfmpeg # for mp3 playback
+      pkgs.gst_all.gstreamer # needed?
+    ];
+
+    vlc = [pkgs.phonon_backend_vlc];
+  };
+
+  phononBackendPackages = flip concatMap cfg.phononBackends
+    (name: attrByPath [name] (throw "unknown phonon backend `${name}'") phononBackends);
+
+  wantsUdisks2 = pkgs.kde4.kdelibs.wantsUdisks2 or false;
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.kde4 = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the KDE 4 desktop environment.";
+      };
+
+      phononBackends = mkOption {
+        type = types.listOf types.string;
+        default = ["gstreamer"];
+        example = ["gstreamer" "vlc"];
+        description = "Which phonon multimedia backend kde should use";
+      };
+    };
+
+    environment.kdePackages = mkOption {
+      default = [];
+      example = "[ pkgs.kde4.kdesdk ]";
+      type = types.listOf types.package;
+      description = "This option is obsolete.  Please use <option>environment.systemPackages</option> instead.";
+    };
+
+  };
+
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    # If KDE 4 is enabled, make it the default desktop manager (unless
+    # overridden by the user's configuration).
+    # !!! doesn't work yet ("Multiple definitions. Only one is allowed
+    # for this option.")
+    # services.xserver.desktopManager.default = mkOverrideTemplate 900 "kde4";
+
+    services.xserver.desktopManager.session = singleton
+      { name = "kde4";
+        bgSupport = true;
+        start =
+          ''
+            # The KDE icon cache is supposed to update itself
+            # automatically, but it uses the timestamp on the icon
+            # theme directory as a trigger.  Since in Nix the
+            # timestamp is always the same, this doesn't work.  So as
+            # a workaround, nuke the icon cache on login.  This isn't
+            # perfect, since it may require logging out after
+            # installing new applications to update the cache.
+            # See http://lists-archives.org/kde-devel/26175-what-when-will-icon-cache-refresh.html
+            rm -fv $HOME/.kde/cache-*/icon-cache.kcache
+
+            # Qt writes a weird ‘libraryPath’ line to
+            # ~/.config/Trolltech.conf that causes the KDE plugin
+            # paths of previous KDE invocations to be searched.
+            # Obviously using mismatching KDE libraries is potentially
+            # disastrous, so here we nuke references to the Nix store
+            # in Trolltech.conf.  A better solution would be to stop
+            # Qt from doing this wackiness in the first place.
+            if [ -e $HOME/.config/Trolltech.conf ]; then
+                sed -e '/nix\\store\|nix\/store/ d' -i $HOME/.config/Trolltech.conf
+            fi
+
+            # Start KDE.
+            exec ${pkgs.kde4.kdebase_workspace}/bin/startkde
+          '';
+      };
+
+    security.setuidOwners = singleton
+      { program = "kcheckpass";
+        source = "${pkgs.kde4.kdebase_workspace}/lib/kde4/libexec/kcheckpass";
+        owner = "root";
+        group = "root";
+        setuid = true;
+      };
+
+    environment.systemPackages =
+        [ pkgs.kde4.kdelibs
+
+          pkgs.kde4.kde_baseapps # Splitted kdebase
+          pkgs.kde4.kde_workspace
+          pkgs.kde4.kde_runtime
+          pkgs.kde4.konsole
+          pkgs.kde4.kate
+
+          pkgs.kde4.kde_wallpapers # contains kdm's default background
+          pkgs.kde4.oxygen_icons
+          pkgs.virtuoso # to enable Nepomuk to find Virtuoso
+
+          # Starts KDE's Polkit authentication agent.
+          pkgs.kde4.polkit_kde_agent
+
+          # Miscellaneous runtime dependencies.
+          pkgs.kde4.qt4 # needed for qdbus
+          pkgs.shared_mime_info
+          xorg.xmessage # so that startkde can show error messages
+          xorg.xset # used by startkde, non-essential
+          xorg.xauth # used by kdesu
+          pkgs.shared_desktop_ontologies # used by nepomuk
+          pkgs.strigi # used by nepomuk
+          pkgs.mysql # used by akonadi
+        ]
+      ++ [ nepomukConfig ] ++ phononBackendPackages
+      ++ config.environment.kdePackages;
+
+    environment.pathsToLink = [ "/share" ];
+
+    environment.etc = singleton
+      { source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
+        target = "X11/xkb";
+      };
+
+    # Enable helpful DBus services.
+    services.udisks.enable = ! wantsUdisks2;
+    services.udisks2.enable = wantsUdisks2;
+    services.upower.enable = config.powerManagement.enable;
+
+    security.pam.services = [ { name = "kde"; allowNullPassword = true; startSession = true; } ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/none.nix b/nixos/modules/services/x11/desktop-managers/none.nix
new file mode 100644
index 00000000000..af7a376ae02
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/none.nix
@@ -0,0 +1,7 @@
+{
+  services.xserver.desktopManager.session =
+    [ { name = "none";
+        start = "";
+      }
+    ];
+}
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
new file mode 100644
index 00000000000..f5d544ad046
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -0,0 +1,90 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.xfce;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.xfce.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable the Xfce desktop environment.";
+    };
+
+  };
+
+
+  config = mkIf (xcfg.enable && cfg.enable) {
+
+    services.xserver.desktopManager.session = singleton
+      { name = "xfce";
+        bgSupport = true;
+        start =
+          ''
+            # Set GTK_PATH so that GTK+ can find the theme engines.
+            export GTK_PATH=${config.system.path}/lib/gtk-2.0
+
+            # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
+            export GTK_DATA_PREFIX=${config.system.path}
+
+            # Necessary to get xfce4-mixer to find GST's ALSA plugin.
+            # Ugly.
+            export GST_PLUGIN_PATH=${config.system.path}/lib
+
+            exec ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc}
+          '';
+      };
+
+    environment.systemPackages =
+      [ pkgs.gtk # To get GTK+'s themes.
+        pkgs.hicolor_icon_theme
+        pkgs.tango-icon-theme
+        pkgs.shared_mime_info
+        pkgs.which # Needed by the xfce's xinitrc script.
+        pkgs.xfce.exo
+        pkgs.xfce.gtk_xfce_engine
+        pkgs.xfce.libxfcegui4 # For the icons.
+        pkgs.xfce.mousepad
+        pkgs.xfce.ristretto
+        pkgs.xfce.terminal
+        pkgs.xfce.thunar
+        pkgs.xfce.xfce4icontheme
+        pkgs.xfce.xfce4panel
+        pkgs.xfce.xfce4session
+        pkgs.xfce.xfce4settings
+        pkgs.xfce.xfce4mixer
+        pkgs.xfce.xfceutils
+        pkgs.xfce.xfconf
+        pkgs.xfce.xfdesktop
+        pkgs.xfce.xfwm4
+        # This supplies some "abstract" icons such as
+        # "utilities-terminal" and "accessories-text-editor".
+        pkgs.gnome.gnomeicontheme
+        pkgs.desktop_file_utils
+        pkgs.xfce.libxfce4ui
+        pkgs.xfce.garcon
+        pkgs.xfce.thunar_volman
+        pkgs.xfce.gvfs
+        pkgs.xfce.xfce4_appfinder
+      ]
+      ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager;
+
+    environment.pathsToLink =
+      [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
+
+    environment.variables.GIO_EXTRA_MODULES = "${pkgs.xfce.gvfs}/lib/gio/modules";
+
+    # Enable helpful DBus services.
+    services.udisks2.enable = true;
+    services.upower.enable = config.powerManagement.enable;
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/xterm.nix b/nixos/modules/services/x11/desktop-managers/xterm.nix
new file mode 100644
index 00000000000..edc61c103ea
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/xterm.nix
@@ -0,0 +1,36 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.desktopManager.xterm;
+
+in
+
+{
+  options = {
+
+    services.xserver.desktopManager.xterm.enable = mkOption {
+      default = true;
+      example = false;
+      description = "Enable a xterm terminal as a desktop manager.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.desktopManager.session = singleton
+      { name = "xterm";
+        start = ''
+          ${pkgs.xterm}/bin/xterm -ls &
+          waitPID=$!
+        '';
+      };
+
+    environment.systemPackages = [ pkgs.xterm ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/display-managers/auto.nix b/nixos/modules/services/x11/display-managers/auto.nix
new file mode 100644
index 00000000000..33d97e0e07a
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/auto.nix
@@ -0,0 +1,52 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  dmcfg = config.services.xserver.displayManager;
+  cfg = dmcfg.auto;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.displayManager.auto = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the fake "auto" display manager, which
+          automatically logs in the user specified in the
+          <option>user</option> option.  This is mostly useful for
+          automated tests.
+        '';
+      };
+
+      user = mkOption {
+        default = "root";
+        description = "The user account to login automatically.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.displayManager.slim = {
+      enable = true;
+      autoLogin = true;
+      defaultUser = cfg.user;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
new file mode 100644
index 00000000000..c7599e245b0
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -0,0 +1,283 @@
+# This module declares the options to define a *display manager*, the
+# program responsible for handling X logins (such as xdm, kdm, gdb, or
+# SLiM).  The display manager allows the user to select a *session
+# type*.  When the user logs in, the display manager starts the
+# *session script* ("xsession" below) to launch the selected session
+# type.  The session type defines two things: the *desktop manager*
+# (e.g., KDE, Gnome or a plain xterm), and optionally the *window
+# manager* (e.g. kwin or twm).
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver;
+  xorg = pkgs.xorg;
+
+  vaapiDrivers = pkgs.buildEnv {
+    name = "vaapi-drivers";
+    paths = cfg.vaapiDrivers;
+    # We only want /lib/dri, but with a single input path, we need "/" for it to work
+    pathsToLink = [ "/" ];
+  };
+
+  # file provided by services.xserver.displayManager.session.script
+  xsession = wm: dm: pkgs.writeScript "xsession"
+    ''
+      #! /bin/sh
+
+      . /etc/profile
+      cd "$HOME"
+
+      # The first argument of this script is the session type.
+      sessionType="$1"
+      if [ "$sessionType" = default ]; then sessionType=""; fi
+
+      ${optionalString (!cfg.displayManager.job.logsXsession) ''
+        exec > ~/.xsession-errors 2>&1
+      ''}
+
+      ${optionalString cfg.displayManager.desktopManagerHandlesLidAndPower ''
+        # Stop systemd from handling the power button and lid switch,
+        # since presumably the desktop environment will handle these.
+        if [ -z "$_INHIBITION_LOCK_TAKEN" ]; then
+          export _INHIBITION_LOCK_TAKEN=1
+          exec ${config.systemd.package}/bin/systemd-inhibit --what=handle-lid-switch:handle-power-key "$0" "$sessionType"
+        fi
+
+      ''}
+
+      ${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.
+            exec "${pkgs.gnupg}/bin/gpg-agent"                         \
+              --enable-ssh-support --daemon                             \
+              --pinentry-program "${pkgs.pinentry}/bin/pinentry-gtk-2"  \
+              --write-env-file "$HOME/.gpg-agent-info"                  \
+              "$0" "$sessionType"
+        fi
+      ''}
+
+      # Handle being called by kdm.
+      if test "''${1:0:1}" = /; then eval exec "$1"; fi
+
+      # Start PulseAudio if enabled.
+      ${optionalString (config.hardware.pulseaudio.enable) ''
+        ${optionalString (!config.hardware.pulseaudio.systemWide)
+          "${pkgs.pulseaudio}/bin/pulseaudio --start"
+        }
+
+        # Publish access credentials in the root window.
+        ${pkgs.pulseaudio}/bin/pactl load-module module-x11-publish "display=$DISPLAY"
+
+        # Keep track of devices.  Mostly useful for Phonon/KDE.
+        ${pkgs.pulseaudio}/bin/pactl load-module module-device-manager "do_routing=1"
+      ''}
+
+      # Load X defaults.
+      if test -e ~/.Xdefaults; then
+          ${xorg.xrdb}/bin/xrdb -merge ~/.Xdefaults
+      fi
+
+      export LIBVA_DRIVERS_PATH=${vaapiDrivers}/lib/dri
+
+      # Speed up application start by 50-150ms according to
+      # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html
+      rm -rf $HOME/.compose-cache
+      mkdir $HOME/.compose-cache
+
+      ${cfg.displayManager.sessionCommands}
+
+      # Allow the user to setup a custom session type.
+      if test -x ~/.xsession; then
+          exec ~/.xsession
+      else
+          if test "$sessionType" = "custom"; then
+              sessionType="" # fall-thru if there is no ~/.xsession
+          fi
+      fi
+
+      # The session type is "<desktop-manager> + <window-manager>", so
+      # extract those.
+      windowManager="''${sessionType##* + }"
+      : ''${windowManager:=${cfg.windowManager.default}}
+      desktopManager="''${sessionType% + *}"
+      : ''${desktopManager:=${cfg.desktopManager.default}}
+
+      # Start the window manager.
+      case $windowManager in
+        ${concatMapStrings (s: ''
+          (${s.name})
+            ${s.start}
+            ;;
+        '') wm}
+        (*) echo "$0: Window manager '$windowManager' not found.";;
+      esac
+
+      # Start the desktop manager.
+      case $desktopManager in
+        ${concatMapStrings (s: ''
+          (${s.name})
+            ${s.start}
+            ;;
+        '') dm}
+        (*) echo "$0: Desktop manager '$desktopManager' not found.";;
+      esac
+
+      test -n "$waitPID" && wait "$waitPID"
+      exit 0
+    '';
+
+  mkDesktops = names: pkgs.runCommand "desktops" {}
+    ''
+      mkdir -p $out
+      ${concatMapStrings (n: ''
+        cat - > "$out/${n}.desktop" << EODESKTOP
+        [Desktop Entry]
+        Version=1.0
+        Type=XSession
+        TryExec=${cfg.displayManager.session.script}
+        Exec=${cfg.displayManager.session.script} '${n}'
+        Name=${n}
+        Comment=
+        EODESKTOP
+      '') names}
+    '';
+
+in
+
+{
+
+  options = {
+
+    services.xserver.displayManager = {
+
+      xauthBin = mkOption {
+        default = "${xorg.xauth}/bin/xauth";
+        description = "Path to the <command>xauth</command> program used by display managers.";
+      };
+
+      xserverBin = mkOption {
+        default = "${xorg.xorgserver}/bin/X";
+        description = "Path to the X server used by display managers.";
+      };
+
+      xserverArgs = mkOption {
+        default = [];
+        example = [ "-ac" "-logverbose" "-nolisten tcp" ];
+        description = "List of arguments for the X server.";
+        apply = toString;
+      };
+
+      sessionCommands = mkOption {
+        default = "";
+        example =
+          ''
+            xmessage "Hello World!" &
+          '';
+        type = types.string;
+        description = "Shell commands executed just before the window or desktop manager is started.";
+      };
+
+      desktopManagerHandlesLidAndPower = mkOption {
+        default = true;
+        description = ''
+          Whether the display manager should prevent systemd from handling
+          lid and power events. This is normally handled by the desktop
+          environment's power manager. Turn this off when using a minimal
+          X11 setup without a full power manager.
+        '';
+      };
+
+      session = mkOption {
+        default = [];
+        example = [
+          {
+            manage = "desktop";
+            name = "xterm";
+            start = "
+              ${pkgs.xterm}/bin/xterm -ls &
+              waitPID=$!
+            ";
+          }
+        ];
+        description = ''
+          List of sessions supported with the command used to start each
+          session.  Each session script can set the
+          <varname>waitPID</varname> shell variable to make this script
+          wait until the end of the user session.  Each script is used
+          to define either a windows manager or a desktop manager.  These
+          can be differentiated by setting the attribute
+          <varname>manage</varname> either to <literal>"window"</literal>
+          or <literal>"desktop"</literal>.
+
+          The list of desktop manager and window manager should appear
+          inside the display manager with the desktop manager name
+          followed by the window manager name.
+        '';
+        apply = list: rec {
+          wm = filter (s: s.manage == "window") list;
+          dm = filter (s: s.manage == "desktop") list;
+          names = flip concatMap dm
+            (d: map (w: d.name + optionalString (w.name != "none") (" + " + w.name))
+              (filter (w: d.name != "none" || w.name != "none") wm));
+          desktops = mkDesktops names;
+          script = xsession wm dm;
+        };
+      };
+
+      job = mkOption {
+        default = {};
+        type = types.uniq types.optionSet;
+        description = "This option defines how to start the display manager.";
+
+        options = {
+
+          preStart = mkOption {
+            default = "";
+            example = "rm -f /var/log/my-display-manager.log";
+            description = "Script executed before the display manager is started.";
+          };
+
+          execCmd = mkOption {
+            example = "${pkgs.slim}/bin/slim";
+            description = "Command to start the display manager.";
+          };
+
+          environment = mkOption {
+            default = {};
+            example = { SLIM_CFGFILE = /etc/slim.conf; };
+            description = "Additional environment variables needed by the display manager.";
+          };
+
+          logsXsession = mkOption {
+            default = false;
+            description = ''
+              Whether the display manager redirects the
+              output of the session script to
+              <filename>~/.xsession-errors</filename>.
+            '';
+          };
+
+        };
+
+      };
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/display-managers/kdm.nix b/nixos/modules/services/x11/display-managers/kdm.nix
new file mode 100644
index 00000000000..229ab12c6e1
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/kdm.nix
@@ -0,0 +1,151 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  dmcfg = config.services.xserver.displayManager;
+  cfg = dmcfg.kdm;
+
+  inherit (pkgs.kde4) kdebase_workspace;
+
+  defaultConfig =
+    ''
+      [Shutdown]
+      HaltCmd=${config.systemd.package}/sbin/shutdown -h now
+      RebootCmd=${config.systemd.package}/sbin/shutdown -r now
+      ${optionalString (config.system.boot.loader.id == "grub") ''
+        BootManager=${if config.boot.loader.grub.version == 2 then "Grub2" else "Grub"}
+      ''}
+
+      [X-*-Core]
+      Xrdb=${pkgs.xlibs.xrdb}/bin/xrdb
+      SessionsDirs=${dmcfg.session.desktops}
+      Session=${dmcfg.session.script}
+      FailsafeClient=${pkgs.xterm}/bin/xterm
+
+      [X-:*-Core]
+      ServerCmd=${dmcfg.xserverBin} ${dmcfg.xserverArgs}
+      # KDM calls `rm' somewhere to clean up some temporary directory.
+      SystemPath=${pkgs.coreutils}/bin
+      # The default timeout (15) is too short in a heavily loaded boot process.
+      ServerTimeout=60
+      # Needed to prevent the X server from dying on logout and not coming back:
+      TerminateServer=true
+      ${optionalString (cfg.setupScript != "")
+      ''
+        Setup=${cfg.setupScript}
+      ''} 
+
+      [X-*-Greeter]
+      HiddenUsers=root,nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
+      PluginsLogin=${kdebase_workspace}/lib/kde4/kgreet_classic.so
+      ${optionalString (cfg.themeDirectory != "")
+      ''
+        UseTheme=true
+        Theme=${cfg.themeDirectory}
+      ''
+      }
+
+      ${optionalString (cfg.enableXDMCP)
+      ''
+        [Xdmcp]
+        Enable=true
+      ''}
+    '';
+
+  kdmrc = pkgs.stdenv.mkDerivation {
+    name = "kdmrc";
+    config = defaultConfig + cfg.extraConfig;
+    buildCommand =
+      ''
+        echo "$config" > $out
+
+        # The default kdmrc would add "-nolisten tcp", and we already
+        # have that managed by nixos. Hence the grep.
+        cat ${kdebase_workspace}/share/config/kdm/kdmrc | grep -v nolisten >> $out
+      '';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.displayManager.kdm = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the KDE display manager.
+        '';
+      };
+
+      enableXDMCP = mkOption {
+        default = false;
+        description = ''
+          Whether to enable XDMCP, which allows remote logins.
+        '';
+      };
+
+      themeDirectory = mkOption {
+        default = "";
+        description = ''
+          The path to a KDM theme directory. This theme
+          will be used by the KDM greeter.
+        '';
+      };
+
+      setupScript = mkOption {
+        default = "";
+        description = ''
+          The path to a KDM setup script. This script is run as root just
+          before KDM starts. Can be used for setting up
+          monitors with xrandr, for example.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Options appended to <filename>kdmrc</filename>, the
+          configuration file of KDM.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.displayManager.slim.enable = false;
+
+    services.xserver.displayManager.job =
+      { execCmd =
+          ''
+            mkdir -m 0755 -p /var/lib/kdm
+            chown kdm /var/lib/kdm
+            ${(optionalString (config.system.boot.loader.id == "grub" && config.system.build.grub != null) "PATH=${config.system.build.grub}/sbin:$PATH ") +
+              "KDEDIRS=/run/current-system/sw exec ${kdebase_workspace}/bin/kdm -config ${kdmrc} -nodaemon"}
+          '';
+        logsXsession = true;
+      };
+
+    security.pam.services = [ { name = "kde"; allowNullPassword = true; startSession = true; } ];
+
+    users.extraUsers = singleton
+      { name = "kdm";
+        uid = config.ids.uids.kdm;
+        description = "KDM user";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
new file mode 100644
index 00000000000..c2b90d239ea
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -0,0 +1,119 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  dmcfg = config.services.xserver.displayManager;
+  xEnv = config.systemd.services."display-manager".environment;
+  cfg = dmcfg.lightdm;
+
+  inherit (pkgs) stdenv lightdm writeScript writeText;
+
+  # lightdm runs with clearenv(), but we need a few things in the enviornment for X to startup
+  xserverWrapper = writeScript "xserver-wrapper"
+    ''
+      #! /bin/sh
+      ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
+      exec ${dmcfg.xserverBin} ${dmcfg.xserverArgs}
+    '';
+
+  # The default greeter provided with this expression is the GTK greeter.
+  # Again, we need a few things in the environment for the greeter to run with
+  # fonts/icons.
+  wrappedGtkGreeter = stdenv.mkDerivation {
+    name = "lightdm-gtk-greeter";
+    buildInputs = [ pkgs.makeWrapper ];
+
+    buildCommand = ''
+      ensureDir $out/gtk-3.0/
+
+      # This wrapper ensures that we actually get fonts
+      makeWrapper ${pkgs.lightdm_gtk_greeter}/sbin/lightdm-gtk-greeter \
+        $out/greeter \
+        --set XDG_DATA_DIRS ${pkgs.gnome2.gnome_icon_theme}/share \
+        --set FONTCONFIG_FILE /etc/fonts/fonts.conf \
+        --set XDG_CONFIG_HOME $out/
+
+      # We need this to ensure that it actually tries to find icons from gnome-icon-theme
+      cat - > $out/gtk-3.0/settings.ini << EOF
+      [Settings]
+      gtk-icon-theme-name=gnome
+      EOF
+
+      cat - > $out/lightdm-gtk-greeter.desktop << EOF
+      [Desktop Entry]
+      Name=LightDM Greeter
+      Comment=This runs the LightDM Greeter
+      Exec=$out/greeter
+      Type=Application
+      EOF
+    '';
+  };
+
+  lightdmConf = writeText "lightdm.conf"
+    ''
+      [LightDM]
+      greeter-user = ${config.users.extraUsers.lightdm.name}
+      xgreeters-directory = ${cfg.greeter.package}
+      xsessions-directory = ${dmcfg.session.desktops}
+
+      [SeatDefaults]
+      xserver-command = ${xserverWrapper}
+      session-wrapper = ${dmcfg.session.script}
+      greeter-session = ${cfg.greeter.name}
+    '';
+
+in
+{
+  options = {
+    services.xserver.displayManager.lightdm = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable lightdm as the display manager.
+        '';
+      };
+
+      greeter = mkOption {
+        description = ''
+          The LightDM greeter to login via. The package should be a directory
+          containing a .desktop file matching the name in the 'name' option.
+        '';
+        default = {
+          name = "lightdm-gtk-greeter";
+          package = wrappedGtkGreeter;
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.displayManager.job = {
+      logsXsession = true;
+
+      # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
+      execCmd = ''
+        export PATH=${lightdm}/sbin:$PATH
+        ${lightdm}/sbin/lightdm --log-dir=/var/log --run-dir=/run --config=${lightdmConf}
+      '';
+    };
+
+    services.dbus.enable = true;
+    services.dbus.packages = [ lightdm ];
+
+    security.pam.services = [
+      { name = "lightdm"; allowNullPassword = true; startSession = true; }
+      { name = "lightdm-greeter"; allowNullPassword = true; startSession = true; }
+    ];
+
+    users.extraUsers.lightdm = {
+      createHome = true;
+      home = "/var/lib/lightdm";
+      group = "lightdm";
+      uid = config.ids.uids.lightdm;
+    };
+
+    users.extraGroups.lightdm.gid = config.ids.gids.lightdm;
+  };
+}
diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix
new file mode 100644
index 00000000000..9e8b9391f45
--- /dev/null
+++ b/nixos/modules/services/x11/display-managers/slim.nix
@@ -0,0 +1,113 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  dmcfg = config.services.xserver.displayManager;
+  cfg = dmcfg.slim;
+
+  slimConfig = pkgs.writeText "slim.cfg"
+    ''
+      xauth_path ${dmcfg.xauthBin}
+      default_xserver ${dmcfg.xserverBin}
+      xserver_arguments ${dmcfg.xserverArgs}
+      sessions ${pkgs.lib.concatStringsSep "," (dmcfg.session.names ++ ["custom"])}
+      login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session"
+      halt_cmd ${config.systemd.package}/sbin/shutdown -h now
+      reboot_cmd ${config.systemd.package}/sbin/shutdown -r now
+      ${optionalString (cfg.defaultUser != "") ("default_user " + cfg.defaultUser)}
+      ${optionalString cfg.autoLogin "auto_login yes"}
+    '';
+
+  # Unpack the SLiM theme, or use the default.
+  slimThemesDir =
+    let
+      unpackedTheme = pkgs.stdenv.mkDerivation {
+        name = "slim-theme";
+        buildCommand = ''
+          ensureDir $out
+          cd $out
+          unpackFile ${cfg.theme}
+          ln -s * default
+        '';
+      };
+    in if cfg.theme == null then "${pkgs.slim}/share/slim/themes" else unpackedTheme;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.displayManager.slim = {
+
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whether to enable SLiM as the display manager.
+        '';
+      };
+
+      theme = mkOption {
+        default = null;
+        example = pkgs.fetchurl {
+          url = http://download.berlios.de/slim/slim-wave.tar.gz;
+          sha256 = "0ndr419i5myzcylvxb89m9grl2xyq6fbnyc3lkd711mzlmnnfxdy";
+        };
+        description = ''
+          The theme for the SLiM login manager.  If not specified, SLiM's
+          default theme is used.  See <link
+          xlink:href='http://slim.berlios.de/themes01.php'/> for a
+          collection of themes.
+        '';
+      };
+
+      defaultUser = mkOption {
+        default = "";
+        example = "login";
+        description = ''
+          The default user to load. If you put a username here you
+          get it automatically loaded into the username field, and
+          the focus is placed on the password.
+        '';
+      };
+
+      autoLogin = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Automatically log in as the default user.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.displayManager.job =
+      { preStart =
+          ''
+            rm -f /var/log/slim.log
+          '';
+        environment =
+          { SLIM_CFGFILE = slimConfig;
+            SLIM_THEMESDIR = slimThemesDir;
+          };
+        execCmd = "exec ${pkgs.slim}/bin/slim";
+      };
+
+    # Allow null passwords so that the user can login as root on the
+    # installation CD.
+    security.pam.services = [ { name = "slim"; allowNullPassword = true; startSession = true; } ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/hardware/multitouch.nix b/nixos/modules/services/x11/hardware/multitouch.nix
new file mode 100644
index 00000000000..4f9048bfd91
--- /dev/null
+++ b/nixos/modules/services/x11/hardware/multitouch.nix
@@ -0,0 +1,60 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let cfg = config.services.xserver.multitouch; in
+
+{
+
+  options = {
+
+    services.xserver.multitouch = {
+
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Whether to enable multitouch touchpad support.";
+      };
+
+      invertScroll = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = "Whether to invert scrolling direction à la OSX Lion";
+      };
+
+      ignorePalm = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = "Whether to ignore touches detected as being the palm (i.e when typing)";
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.modules = [ pkgs.xf86_input_mtrack ];
+
+    services.xserver.config =
+      ''
+        # Automatically enable the multitouch driver
+        Section "InputClass"
+          MatchIsTouchpad "on"
+          Identifier "Touchpads"
+          Driver "mtrack"
+          Option "IgnorePalm" "${if cfg.ignorePalm then "true" else "false"}"
+          ${optionalString cfg.invertScroll ''
+            Option "ScrollUpButton" "5"
+            Option "ScrollDownButton" "4"
+            Option "ScrollLeftButton" "7"
+            Option "ScrollRightButton" "6"
+          ''}
+        EndSection
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
new file mode 100644
index 00000000000..d16142a5fdf
--- /dev/null
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -0,0 +1,122 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let cfg = config.services.xserver.synaptics; in
+
+{
+
+  options = {
+
+    services.xserver.synaptics = {
+
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Whether to enable touchpad support.";
+      };
+
+      dev = mkOption {
+        default = null;
+        example = "/dev/input/event0";
+        description =
+          ''
+            Path for touchpad device.  Set to null to apply to any
+            auto-detected touchpad.
+          '';
+      };
+
+      accelFactor = mkOption {
+        default = "0.001";
+        description = "Cursor acceleration (how fast speed increases from minSpeed to maxSpeed).";
+      };
+
+      minSpeed = mkOption {
+        default = "0.6";
+        description = "Cursor speed factor for precision finger motion.";
+      };
+
+      maxSpeed = mkOption {
+        default = "1.0";
+        description = "Cursor speed factor for highest-speed finger motion.";
+      };
+
+      twoFingerScroll = mkOption {
+        default = false;
+        description = "Whether to enable two-finger drag-scrolling.";
+      };
+
+      vertEdgeScroll = mkOption {
+        default = ! cfg.twoFingerScroll;
+        description = "Whether to enable vertical edge drag-scrolling.";
+      };
+
+      tapButtons = mkOption {
+        default = true;
+        example = false;
+        description = "Whether to enable tap buttons.";
+      };
+
+      palmDetect = mkOption {
+        default = false;
+        example = true;
+        description = "Whether to enable palm detection (hardware support required)";
+      };
+
+      horizontalScroll = mkOption {
+        default = true;
+        example = false;
+        description = "Whether to enable horizontal scrolling (on touchpad)";
+      };
+
+      additionalOptions = mkOption {
+        default = "";
+        example = ''
+          Option "RTCornerButton" "2"
+          Option "RBCornerButton" "3"
+		'';
+        description = ''
+          Additional options for synaptics touchpad driver.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    services.xserver.modules = [ pkgs.xorg.xf86inputsynaptics ];
+
+    environment.systemPackages = [ pkgs.xorg.xf86inputsynaptics ];
+
+    services.xserver.config =
+      ''
+        # Automatically enable the synaptics driver for all touchpads.
+        Section "InputClass"
+          Identifier "synaptics touchpad catchall"
+          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}"
+          Option "TapButton1" "${if cfg.tapButtons then "1" else "0"}"
+          Option "TapButton2" "${if cfg.tapButtons then "2" else "0"}"
+          Option "TapButton3" "${if cfg.tapButtons then "3" else "0"}"
+          ${if cfg.tapButtons then "" else ''Option "MaxTapTime" "0"''}
+          Option "VertTwoFingerScroll" "${if cfg.twoFingerScroll then "1" else "0"}"
+          Option "HorizTwoFingerScroll" "${if cfg.twoFingerScroll then "1" else "0"}"
+          Option "VertEdgeScroll" "${if cfg.vertEdgeScroll then "1" else "0"}"
+          ${if cfg.palmDetect then ''Option "PalmDetect" "1"'' else ""}
+          ${if cfg.horizontalScroll then "" else ''Option "HorizScrollDelta" "0"''}
+          ${cfg.additionalOptions}
+        EndSection
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/hardware/wacom.nix b/nixos/modules/services/x11/hardware/wacom.nix
new file mode 100644
index 00000000000..dfc588cd213
--- /dev/null
+++ b/nixos/modules/services/x11/hardware/wacom.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.wacom;
+
+in
+
+{
+
+  options = {
+
+    services.xserver.wacom = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Wacom touchscreen/digitizer/tablet.
+          If you ever have any issues such as, try switching to terminal (ctrl-alt-F1) and back
+          which will make Xorg reconfigure the device ?
+
+          If you're not satisfied by the default behaviour you can override
+          <option>environment.etc."X11/xorg.conf.d/50-wacom.conf"</option> in
+          configuration.nix easily.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.xf86_input_wacom ]; # provides xsetwacom
+
+    services.xserver.modules = [ pkgs.xf86_input_wacom ];
+
+    services.udev.packages = [ pkgs.xf86_input_wacom ];
+
+    environment.etc."X11/xorg.conf.d/50-wacom.conf".source = "${pkgs.xf86_input_wacom}/share/X11/xorg.conf.d/50-wacom.conf";
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/terminal-server.nix b/nixos/modules/services/x11/terminal-server.nix
new file mode 100644
index 00000000000..ab05639aeca
--- /dev/null
+++ b/nixos/modules/services/x11/terminal-server.nix
@@ -0,0 +1,66 @@
+# This module implements a terminal service based on ‘x11vnc’.  It
+# listens on port 5900 for VNC connections.  It then presents a login
+# screen to the user.  If the user successfully authenticates, x11vnc
+# checks to see if a X server is already running for that user.  If
+# not, a X server (Xvfb) is started for that user.  The Xvfb instances
+# persist across VNC sessions.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  # Wrap Xvfb to set some flags/variables.
+  xvfbWrapper = pkgs.writeScriptBin "Xvfb"
+    ''
+      #! ${pkgs.stdenv.shell}
+      export XKB_BINDIR=${pkgs.xorg.xkbcomp}/bin
+      export XORG_DRI_DRIVER_PATH=${pkgs.mesa}/lib/dri
+      exec ${pkgs.xorg.xorgserver}/bin/Xvfb "$@" -xkbdir "${pkgs.xkeyboard_config}/etc/X11/xkb"
+    '';
+
+  # ‘xinetd’ is insanely braindamaged in that it sends stderr to
+  # stdout.  Thus requires just about any xinetd program to be
+  # wrapped to redirect its stderr.  Sigh.
+  x11vncWrapper = pkgs.writeScriptBin "x11vnc-wrapper"
+    ''
+      #! ${pkgs.stdenv.shell}
+      export PATH=${makeSearchPath "bin" [ xvfbWrapper pkgs.gawk pkgs.which pkgs.openssl pkgs.xorg.xauth pkgs.nettools pkgs.shadow pkgs.procps pkgs.utillinux pkgs.bash ]}:$PATH
+      export FD_GEOM=1024x786x24
+      exec ${pkgs.x11vnc}/bin/x11vnc -inetd -display WAIT:1024x786:cmd=FINDCREATEDISPLAY-Xvfb.xdmcp -unixpw -ssl SAVE 2> /var/log/x11vnc.log
+    '';
+
+in 
+
+{
+
+  config = {
+  
+    services.xserver.enable = true;
+
+    # Enable KDM.  Any display manager will do as long as it supports XDMCP.
+    services.xserver.displayManager.kdm.enable = true;
+    services.xserver.displayManager.kdm.enableXDMCP = true;
+    services.xserver.displayManager.kdm.extraConfig =
+      ''
+        [General]
+        # We're headless, so don't bother starting an X server.
+        StaticServers=
+
+        [Xdmcp]
+        Xaccess=${pkgs.writeText "Xaccess" "localhost"}
+      '';
+
+    services.xinetd.enable = true;
+    services.xinetd.services = singleton
+      { name = "x11vnc";
+        port = 5900;
+        unlisted = true;
+        user = "root";
+        server = "${x11vncWrapper}/bin/x11vnc-wrapper";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/awesome.nix b/nixos/modules/services/x11/window-managers/awesome.nix
new file mode 100644
index 00000000000..880ebf1eca6
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/awesome.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.awesome;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.windowManager.awesome.enable = mkOption {
+      default = false;
+      description = "Enable the Awesome window manager.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "awesome";
+        start =
+          ''
+            ${pkgs.awesome}/bin/awesome &
+            waitPID=$!
+          '';
+      };
+
+    environment.x11Packages = [ pkgs.awesome ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/compiz.nix b/nixos/modules/services/x11/window-managers/compiz.nix
new file mode 100644
index 00000000000..209401f2646
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/compiz.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.compiz;
+  xorg = config.services.xserver.package;
+
+in
+
+{
+
+  options = {
+
+    services.xserver.windowManager.compiz = {
+
+      enable = mkOption {
+        default = false;
+        description = "Enable the Compiz window manager.";
+      };
+
+      renderingFlag = mkOption {
+        default = "";
+        example = "--indirect-rendering";
+        description = "Pass the <option>--indirect-rendering</option> flag to Compiz.";
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "compiz";
+        start =
+          ''
+            # Start Compiz using the flat-file configuration backend
+            # (ccp).
+            export COMPIZ_PLUGINDIR=${config.system.path}/lib/compiz
+            export COMPIZ_METADATADIR=${config.system.path}/share/compiz
+            ${pkgs.compiz}/bin/compiz ccp ${cfg.renderingFlag} &
+
+            # Start GTK-style window decorator.
+            ${pkgs.compiz}/bin/gtk-window-decorator &
+          '';
+      };
+
+    environment.systemPackages =
+      [ pkgs.compiz
+        pkgs.compiz_ccsm
+        pkgs.compiz_plugins_main
+        pkgs.compiz_plugins_extra
+        pkgs.libcompizconfig # for the "ccp" plugin
+      ];
+
+    environment.pathsToLink = [ "/lib/compiz" "/share/compiz" ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
new file mode 100644
index 00000000000..c201b789ae4
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -0,0 +1,61 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.xserver.windowManager;
+in
+
+{
+  imports =
+    [ ./compiz.nix
+      ./openbox.nix
+      ./metacity.nix
+      ./none.nix
+      ./twm.nix
+      ./wmii.nix
+      ./xmonad.nix
+      ./i3.nix
+      ./xbmc.nix
+    ];
+
+  options = {
+
+    services.xserver.windowManager = {
+
+      session = mkOption {
+        default = [];
+        example = [{
+          name = "wmii";
+          start = "...";
+        }];
+        description = ''
+          Internal option used to add some common line to window manager
+          scripts before forwarding the value to the
+          <varname>displayManager</varname>.
+        '';
+        apply = map (d: d // {
+          manage = "window";
+        });
+      };
+
+      default = mkOption {
+        default = "none";
+        example = "wmii";
+        description = "Default window manager loaded if none have been chosen.";
+        merge = mergeOneOption;
+        apply = defaultWM:
+          if any (w: w.name == defaultWM) cfg.session then
+            defaultWM
+          else
+            throw "Default window manager (${defaultWM}) not found.";
+      };
+
+    };
+
+  };
+
+  config = {
+    services.xserver.displayManager.session = cfg.session;
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix
new file mode 100644
index 00000000000..6777a95ddc8
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/i3.nix
@@ -0,0 +1,43 @@
+{ pkgs, config, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.xserver.windowManager.i3;
+in
+
+{
+  options = {
+    services.xserver.windowManager.i3 = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the i3 tiling window manager.";
+      };
+
+      configFile = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          Path to the i3 configuration file.
+          If left at the default value, $HOME/.i3/config will be used.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.windowManager = {
+      session = [{
+        name = "i3";
+        start = "
+          ${pkgs.i3}/bin/i3 ${optionalString (cfg.configFile != null)
+            "-c \"${cfg.configFile}\""
+          } &
+          waitPID=$!
+        ";
+      }];
+    };
+    environment.x11Packages = [ pkgs.i3 ];
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/icewm.nix b/nixos/modules/services/x11/window-managers/icewm.nix
new file mode 100644
index 00000000000..9da4a415fad
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/icewm.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.icewm;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.windowManager.icewm.enable = mkOption {
+      default = false;
+      description = "Enable the IceWM window manager.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "icewm";
+        start =
+          ''
+            ${pkgs.icewm}/bin/icewm &
+            waitPID=$!
+          '';
+      };
+
+    environment.x11Packages = [ pkgs.icewm ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/metacity.nix b/nixos/modules/services/x11/window-managers/metacity.nix
new file mode 100644
index 00000000000..712e2038594
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/metacity.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.metacity;
+  xorg = config.services.xserver.package;
+  gnome = pkgs.gnome;
+
+in
+
+{
+  options = {
+
+    services.xserver.windowManager.metacity.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable the metacity window manager.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "metacity";
+        start = ''
+          env LD_LIBRARY_PATH=${xorg.libX11}/lib:${xorg.libXext}/lib:/usr/lib/
+          # !!! Hack: load the schemas for Metacity.
+          GCONF_CONFIG_SOURCE=xml::~/.gconf ${gnome.GConf}/bin/gconftool-2 \
+            --makefile-install-rule ${gnome.metacity}/etc/gconf/schemas/*.schemas # */
+          ${gnome.metacity}/bin/metacity &
+          waitPID=$!
+        '';
+      };
+
+    environment.systemPackages = [ gnome.metacity ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/none.nix b/nixos/modules/services/x11/window-managers/none.nix
new file mode 100644
index 00000000000..84cf1d77077
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/none.nix
@@ -0,0 +1,12 @@
+{
+  services = {
+    xserver = {
+      windowManager = {
+        session = [{
+          name = "none";
+          start = "";
+        }];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/openbox.nix b/nixos/modules/services/x11/window-managers/openbox.nix
new file mode 100644
index 00000000000..ae34a938c4a
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/openbox.nix
@@ -0,0 +1,30 @@
+{pkgs, config, ...}:
+
+let
+  inherit (pkgs.lib) mkOption mkIf;
+  cfg = config.services.xserver.windowManager.openbox;
+in
+
+{
+  options = {
+    services.xserver.windowManager.openbox = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the Openbox window manager.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.windowManager = {
+      session = [{
+        name = "openbox";
+        start = "
+          ${pkgs.openbox}/bin/openbox-session
+        ";
+      }];
+    };
+    environment.x11Packages = [ pkgs.openbox ];
+  };
+}
diff --git a/nixos/modules/services/x11/window-managers/twm.nix b/nixos/modules/services/x11/window-managers/twm.nix
new file mode 100644
index 00000000000..c1a99b97566
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/twm.nix
@@ -0,0 +1,42 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.twm;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xserver.windowManager.twm.enable = mkOption {
+      default = false;
+      description = "Enable the twm window manager.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      { name = "twm";
+        start =
+          ''
+            ${pkgs.xorg.twm}/bin/twm &
+            waitPID=$!
+          '';
+      };
+
+    environment.x11Packages = [ pkgs.xorg.twm ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/wmii.nix b/nixos/modules/services/x11/window-managers/wmii.nix
new file mode 100644
index 00000000000..b61521274fb
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/wmii.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.xserver.windowManager.wmii;
+
+in
+
+{
+  options = {
+
+    services.xserver.windowManager.wmii.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable the wmii window manager.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = singleton
+      # stop wmii by
+      #   $wmiir xwrite /ctl quit
+      # this will cause wmii exiting with exit code 0
+      #
+      # why this loop?
+      # wmii crashes once a month here. That doesn't matter that much
+      # wmii can recover very well. However without loop the x session terminates and then your workspace setup is
+      # lost and all applications running on X will terminate.
+      # Another use case is kill -9 wmii; after rotating screen.
+      # Note: we don't like kill for that purpose. But it works (-> subject "wmii and xrandr" on mailinglist)
+      { name = "wmii";
+        start = ''
+          while :; do
+            ${pkgs.wmiiSnap}/bin/wmii && break
+          done
+        '';
+      };
+
+    environment.systemPackages = [ pkgs.wmiiSnap ];
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/window-managers/xbmc.nix b/nixos/modules/services/x11/window-managers/xbmc.nix
new file mode 100644
index 00000000000..46494202b40
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/xbmc.nix
@@ -0,0 +1,31 @@
+{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/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
new file mode 100644
index 00000000000..2cbb5002d6c
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/xmonad.nix
@@ -0,0 +1,30 @@
+{pkgs, config, ...}:
+
+let
+  inherit (pkgs.lib) mkOption mkIf;
+  cfg = config.services.xserver.windowManager.xmonad;
+in
+
+{
+  options = {
+    services.xserver.windowManager.xmonad = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the xmonad window manager.";
+      };
+    };
+  };
+
+  config = {
+    services.xserver.windowManager = {
+      session = mkIf cfg.enable [{
+        name = "xmonad";
+        start = "
+          ${pkgs.haskellPackages.xmonad}/bin/xmonad &
+          waitPID=$!
+        ";
+      }];
+    };
+  };
+}
diff --git a/nixos/modules/services/x11/xfs.conf b/nixos/modules/services/x11/xfs.conf
new file mode 100644
index 00000000000..13dcf803db2
--- /dev/null
+++ b/nixos/modules/services/x11/xfs.conf
@@ -0,0 +1,15 @@
+# font server configuration file
+# $Xorg: config.cpp,v 1.3 2000/08/17 19:54:19 cpqbld Exp $
+
+clone-self = on
+use-syslog = off
+error-file = /var/log/xfs.log
+# in decipoints
+default-point-size = 120
+default-resolutions = 75,75,100,100
+
+# font cache control, specified in KB
+cache-hi-mark = 2048
+cache-low-mark = 1433
+cache-balance = 70
+catalogue = /run/current-system/sw/share/X11-fonts/
diff --git a/nixos/modules/services/x11/xfs.nix b/nixos/modules/services/x11/xfs.nix
new file mode 100644
index 00000000000..f4a40dbb08f
--- /dev/null
+++ b/nixos/modules/services/x11/xfs.nix
@@ -0,0 +1,46 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  configFile = ./xfs.conf;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.xfs = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the X Font Server.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.xfs.enable (
+  mkAssert config.fonts.enableFontDir "
+    Please enable fontDir (fonts.enableFontDir) to use xfs.
+  " {
+
+    jobs.xfs =
+      { description = "X Font Server";
+
+        startOn = "started networking";
+
+        exec = "${pkgs.xorg.xfs}/bin/xfs -config ${configFile}";
+      };
+
+  });
+
+}
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
new file mode 100644
index 00000000000..d42d7caaa06
--- /dev/null
+++ b/nixos/modules/services/x11/xserver.nix
@@ -0,0 +1,641 @@
+{ config, pkgs, pkgs_i686, ... }:
+
+with pkgs.lib;
+
+let
+
+  kernelPackages = config.boot.kernelPackages;
+
+  # Abbreviations.
+  cfg = config.services.xserver;
+  xorg = pkgs.xorg;
+
+
+  # Map video driver names to driver packages.
+  knownVideoDrivers = {
+    ati_unfree   = { modules = [ kernelPackages.ati_drivers_x11 ]; driverName = "fglrx"; };
+    nouveau       = { modules = [ pkgs.xf86_video_nouveau ]; };
+    nvidia       = { modules = [ kernelPackages.nvidia_x11 ]; };
+    nvidiaLegacy96 = { modules = [ kernelPackages.nvidia_x11_legacy96 ]; driverName = "nvidia"; };
+    nvidiaLegacy173 = { modules = [ kernelPackages.nvidia_x11_legacy173 ]; driverName = "nvidia"; };
+    nvidiaLegacy304 = { modules = [ kernelPackages.nvidia_x11_legacy304 ]; driverName = "nvidia"; };
+    unichrome    = { modules = [ pkgs.xorgVideoUnichrome ]; };
+    virtualbox   = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
+  };
+
+  driverNames =
+    optional (cfg.videoDriver != null) cfg.videoDriver ++ cfg.videoDrivers;
+
+  drivers = flip map driverNames
+    (name: { inherit name; driverName = name; } //
+      attrByPath [name] (if (hasAttr ("xf86video" + name) xorg) then { modules = [(getAttr ("xf86video" + name) xorg) ]; } else throw "unknown video driver `${name}'") knownVideoDrivers);
+
+  fontsForXServer =
+    config.fonts.fonts ++
+    # We don't want these fonts in fonts.conf, because then modern,
+    # fontconfig-based applications will get horrible bitmapped
+    # Helvetica fonts.  It's better to get a substitution (like Nimbus
+    # Sans) than that horror.  But we do need the Adobe fonts for some
+    # old non-fontconfig applications.  (Possibly this could be done
+    # better using a fontconfig rule.)
+    [ pkgs.xorg.fontadobe100dpi
+      pkgs.xorg.fontadobe75dpi
+    ];
+
+
+  # Just enumerate all heads without discarding XRandR output information.
+  xrandrHeads = let
+    mkHead = num: output: {
+      name = "multihead${toString num}";
+      inherit output;
+    };
+  in imap mkHead cfg.xrandrHeads;
+
+  xrandrDeviceSection = flip concatMapStrings xrandrHeads (h: ''
+    Option "monitor-${h.output}" "${h.name}"
+  '');
+
+  # Here we chain every monitor from the left to right, so we have:
+  # m4 right of m3 right of m2 right of m1   .----.----.----.----.
+  # Which will end up in reverse ----------> | m1 | m2 | m3 | m4 |
+  #                                          `----^----^----^----'
+  xrandrMonitorSections = let
+    mkMonitor = previous: current: previous ++ singleton {
+      inherit (current) name;
+      value = ''
+        Section "Monitor"
+          Identifier "${current.name}"
+          ${optionalString (previous != []) ''
+          Option "RightOf" "${(head previous).name}"
+          ''}
+        EndSection
+      '';
+    };
+    monitors = foldl mkMonitor [] xrandrHeads;
+  in concatMapStrings (getAttr "value") monitors;
+
+
+  configFile = pkgs.stdenv.mkDerivation {
+    name = "xserver.conf";
+
+    xfs = optionalString (cfg.useXFS != false)
+      ''FontPath "${toString cfg.useXFS}"'';
+
+    inherit (cfg) config;
+
+    buildCommand =
+      ''
+        echo 'Section "Files"' >> $out
+        echo $xfs >> $out
+
+        for i in ${toString fontsForXServer}; do
+          if test "''${i:0:''${#NIX_STORE}}" == "$NIX_STORE"; then
+            for j in $(find $i -name fonts.dir); do
+              echo "  FontPath \"$(dirname $j)\"" >> $out
+            done
+          fi
+        done
+
+        for i in $(find ${toString cfg.modules} -type d); do
+          if test $(echo $i/*.so* | wc -w) -ne 0; then
+            echo "  ModulePath \"$i\"" >> $out
+          fi
+        done
+
+        echo 'EndSection' >> $out
+
+        echo "$config" >> $out
+      ''; # */
+  };
+
+
+  checkAgent = mkAssert (!(cfg.startOpenSSHAgent && cfg.startGnuPGAgent))
+    ''
+      The OpenSSH agent and GnuPG agent cannot be started both.
+      Choose between `startOpenSSHAgent' and `startGnuPGAgent'.
+    '';
+
+  checkPolkit = mkAssert config.security.polkit.enable
+    "X11 requires Polkit to be enabled (‘security.polkit.enable = true’).";
+
+
+in
+
+{
+
+  imports =
+    [ ./display-managers/default.nix
+      ./window-managers/default.nix
+      ./desktop-managers/default.nix
+    ];
+
+
+  ###### interface
+
+  options = {
+
+    services.xserver = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the X server.
+        '';
+      };
+
+      autorun = mkOption {
+        default = true;
+        description = ''
+          Whether to start the X server automatically.
+        '';
+      };
+
+      exportConfiguration = mkOption {
+        default = false;
+        description = ''
+          Whether to symlink the X server configuration under
+          <filename>/etc/X11/xorg.conf</filename>.
+        '';
+      };
+
+      enableTCP = mkOption {
+        default = false;
+        description = ''
+          Whether to allow the X server to accept TCP connections.
+        '';
+      };
+
+      modules = mkOption {
+        default = [];
+        example = [ pkgs.xf86_input_wacom ];
+        description = "Packages to be added to the module search path of the X server.";
+      };
+
+      resolutions = mkOption {
+        default = [];
+        example = [ { x = 1600; y = 1200; } { x = 1024; y = 786; } ];
+        description = ''
+          The screen resolutions for the X server.  The first element
+          is the default resolution.  If this list is empty, the X
+          server will automatically configure the resolution.
+        '';
+      };
+
+      videoDriver = mkOption {
+        default = null;
+        example = "i810";
+        description = ''
+          The name of the video driver for your graphics card.  This
+          option is obsolete; please set the
+          <option>videoDrivers</option> instead.
+        '';
+      };
+
+      videoDrivers = mkOption {
+        # !!! 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 X server should
+          support.  The X server will try all of the drivers listed
+          here until it finds one that supports your video card.
+        '';
+      };
+
+      vaapiDrivers = mkOption {
+        default = [ ];
+        defaultText = "[ pkgs.vaapiIntel pkgs.vaapiVdpau ]";
+        example = "[ pkgs.vaapiIntel pkgs.vaapiVdpau ]";
+        description = ''
+          Packages providing libva acceleration drivers.
+        '';
+      };
+
+      driSupport = mkOption {
+        default = true;
+        description = ''
+          Whether to enable accelerated OpenGL rendering through the
+          Direct Rendering Interface (DRI).
+        '';
+      };
+
+      driSupport32Bit = mkOption {
+        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>.
+        '';
+      };
+
+      startOpenSSHAgent = mkOption {
+        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 {
+        default = false;
+        description = ''
+          Whether to start the GnuPG agent when you log in.  The GnuPG agent
+          remembers private keys for you so that you don't have to type in
+          passphrases every time you make an SSH connection or sign/encrypt
+          data.  Use <command>ssh-add</command> to add a key to the agent.
+        '';
+      };
+
+      layout = mkOption {
+        default = "us";
+        description = ''
+          Keyboard layout.
+        '';
+      };
+
+      xkbModel = mkOption {
+        default = "pc104";
+        example = "presario";
+        description = ''
+          Keyboard model.
+        '';
+      };
+
+      xkbOptions = mkOption {
+        default = "terminate:ctrl_alt_bksp";
+        example = "grp:caps_toggle, grp_led:scroll";
+        description = ''
+          X keyboard options; layout switching goes here.
+        '';
+      };
+
+      xkbVariant = mkOption {
+        default = "";
+        example = "colemak";
+        description = ''
+          X keyboard variant.
+        '';
+      };
+
+      config = mkOption {
+        description = ''
+          The contents of the configuration file of the X server
+          (<filename>xorg.conf</filename>).
+        '';
+      };
+
+      deviceSection = mkOption {
+        default = "";
+        example = "VideoRAM 131072";
+        description = "Contents of the first Device section of the X server configuration file.";
+      };
+
+      screenSection = mkOption {
+        default = "";
+        example = ''
+          Option "RandRRotation" "on"
+        '';
+        description = "Contents of the first Screen section of the X server configuration file.";
+      };
+
+      monitorSection = mkOption {
+        default = "";
+        example = "HorizSync 28-49";
+        description = "Contents of the first Monitor section of the X server configuration file.";
+      };
+
+      xrandrHeads = mkOption {
+        default = [];
+        example = [ "HDMI-0" "DVI-0" ];
+        type = with types; listOf string;
+        description = ''
+          Simple multiple monitor configuration, just specify a list of XRandR
+          outputs which will be mapped from left to right in the order of the
+          list.
+
+          Be careful using this option with multiple graphic adapters or with
+          drivers that have poor support for XRandR, unexpected things might
+          happen with those.
+        '';
+      };
+
+      moduleSection = mkOption {
+        default = "";
+        example =
+          ''
+            SubSection "extmod"
+            EndSubsection
+          '';
+        description = "Contents of the Module section of the X server configuration file.";
+      };
+
+      serverLayoutSection = mkOption {
+        default = "";
+        example =
+          ''
+            Option "AIGLX" "true"
+          '';
+        description = "Contents of the ServerLayout section of the X server configuration file.";
+      };
+
+      extraDisplaySettings = mkOption {
+        default = "";
+        example = "Virtual 2048 2048";
+        description = "Lines to be added to every Display subsection of the Screen section.";
+      };
+
+      defaultDepth = mkOption {
+        default = 0;
+        example = 8;
+        description = "Default colour depth.";
+      };
+
+      useXFS = mkOption {
+        default = false;
+        example = "unix/:7100";
+        description = "Determines how to connect to the X Font Server.";
+      };
+
+      tty = mkOption {
+        default = 7;
+        example = 9;
+        description = "Virtual console for the X server.";
+      };
+
+      display = mkOption {
+        default = 0;
+        example = 1;
+        description = "Display number for the X server.";
+      };
+
+      virtualScreen = mkOption {
+        default = null;
+        example = { x = 2048; y = 2048; };
+        description = ''
+          Virtual screen size for Xrandr.
+        '';
+      };
+
+    };
+
+    environment.x11Packages = mkOption {
+      default = [];
+      type = types.listOf types.package;
+      description = ''
+        List of packages added to the system when the X server is
+        activated (<option>services.xserver.enable</option>).
+      '';
+    };
+
+  };
+
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (checkAgent (checkPolkit {
+
+    boot.extraModulePackages =
+      optional (elem "nvidia" driverNames) kernelPackages.nvidia_x11 ++
+      optional (elem "nvidiaLegacy96" driverNames) kernelPackages.nvidia_x11_legacy96 ++
+      optional (elem "nvidiaLegacy173" driverNames) kernelPackages.nvidia_x11_legacy173 ++
+      optional (elem "nvidiaLegacy304" driverNames) kernelPackages.nvidia_x11_legacy304 ++
+      optional (elem "virtualbox" driverNames) kernelPackages.virtualboxGuestAdditions ++
+      optional (elem "ati_unfree" driverNames) kernelPackages.ati_drivers_x11;
+
+    boot.blacklistedKernelModules =
+      optionals (elem "nvidia" driverNames) [ "nouveau" "nvidiafb" ];
+
+    environment.variables.LD_LIBRARY_PATH =
+      [ "/run/opengl-driver/lib" "/run/opengl-driver-32/lib" ];
+
+    environment.etc =
+      (optionals cfg.exportConfiguration
+        [ { source = "${configFile}";
+            target = "X11/xorg.conf";
+          }
+          # -xkbdir command line option does not seems to be passed to xkbcomp.
+          { source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
+            target = "X11/xkb";
+          }
+        ])
+      ++ (optionals (elem "ati_unfree" driverNames) [
+
+          # according toiive on #ati you don't need the pcs, it is like registry... keeps old stuff to make your
+          # life harder ;) Still it seems to be required
+          { source = "${kernelPackages.ati_drivers_x11}/etc/ati";
+            target = "ati";
+          }
+      ])
+      ++ (optionals (elem "nvidia" driverNames) [
+
+          { source = "${kernelPackages.nvidia_x11}/lib/vendors/nvidia.icd";
+            target = "OpenCL/vendors/nvidia.icd";
+          }
+      ]);
+
+    environment.x11Packages =
+      [ xorg.xorgserver
+        xorg.xrandr
+        xorg.xrdb
+        xorg.setxkbmap
+        xorg.iceauth # required for KDE applications (it's called by dcopserver)
+        xorg.xlsclients
+        xorg.xset
+        xorg.xsetroot
+        xorg.xinput
+        xorg.xprop
+        pkgs.xterm
+        pkgs.xdg_utils
+      ]
+      ++ optional (elem "nvidia" driverNames) kernelPackages.nvidia_x11
+      ++ optional (elem "nvidiaLegacy96" driverNames) kernelPackages.nvidia_x11_legacy96
+      ++ optional (elem "nvidiaLegacy173" driverNames) kernelPackages.nvidia_x11_legacy173
+      ++ optional (elem "nvidiaLegacy304" driverNames) kernelPackages.nvidia_x11_legacy304
+      ++ optional (elem "virtualbox" driverNames) xorg.xrefresh
+      ++ optional (elem "ati_unfree" driverNames) kernelPackages.ati_drivers_x11;
+
+    environment.systemPackages = config.environment.x11Packages;
+
+    environment.pathsToLink =
+      [ "/etc/xdg" "/share/xdg" "/share/applications" "/share/icons" "/share/pixmaps" ];
+
+    systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
+
+    systemd.services."display-manager" =
+      { description = "X11 Server";
+
+        after = [ "systemd-udev-settle.service" "local-fs.target" ];
+
+        restartIfChanged = false;
+
+        environment =
+          { FONTCONFIG_FILE = "/etc/fonts/fonts.conf"; # !!! cleanup
+            XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension.
+            XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
+          } // optionalAttrs (elem "nvidia" driverNames) {
+            LD_LIBRARY_PATH = "${xorg.libX11}/lib:${xorg.libXext}/lib:${kernelPackages.nvidia_x11}/lib";
+          } // optionalAttrs (elem "nvidiaLegacy96" driverNames) {
+            LD_LIBRARY_PATH = "${xorg.libX11}/lib:${xorg.libXext}/lib:${kernelPackages.nvidia_x11_legacy96}/lib";
+          } // optionalAttrs (elem "nvidiaLegacy173" driverNames) {
+            LD_LIBRARY_PATH = "${xorg.libX11}/lib:${xorg.libXext}/lib:${kernelPackages.nvidia_x11_legacy173}/lib";
+          } // optionalAttrs (elem "nvidiaLegacy304" driverNames) {
+            LD_LIBRARY_PATH = "${xorg.libX11}/lib:${xorg.libXext}/lib:${kernelPackages.nvidia_x11_legacy304}/lib";
+          } // optionalAttrs (elem "ati_unfree" driverNames) {
+            LD_LIBRARY_PATH = "${xorg.libX11}/lib:${xorg.libXext}/lib:${kernelPackages.ati_drivers_x11}/lib:${kernelPackages.ati_drivers_x11}/X11R6/lib64/modules/linux";
+            #XORG_DRI_DRIVER_PATH = "${kernelPackages.ati_drivers_x11}/lib/dri"; # is ignored because ati drivers ship their own unpatched libglx.so !
+          } // cfg.displayManager.job.environment;
+
+        preStart =
+          ''
+            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" driverNames then
+                ''
+                  ln -sf ${kernelPackages.nvidia_x11} /run/opengl-driver
+                  ${optionalString cfg.driSupport32Bit
+                    "ln -sf ${pkgs_i686.linuxPackages.nvidia_x11.override { libsOnly = true; kernelDev = null; } } /run/opengl-driver-32"}
+                ''
+              else if elem "nvidiaLegacy96" driverNames then
+                "ln -sf ${kernelPackages.nvidia_x11_legacy96} /run/opengl-driver"
+              else if elem "nvidiaLegacy173" driverNames then
+                "ln -sf ${kernelPackages.nvidia_x11_legacy173} /run/opengl-driver"
+              else if elem "nvidiaLegacy304" driverNames then
+                ''
+                  ln -sf ${kernelPackages.nvidia_x11_legacy304} /run/opengl-driver
+                  ${optionalString cfg.driSupport32Bit
+                    "ln -sf ${pkgs_i686.linuxPackages.nvidia_x11_legacy304.override { libsOnly = true; kernelDev = null; } } /run/opengl-driver-32"}
+                ''
+              else if elem "ati_unfree" driverNames then
+                "ln -sf ${kernelPackages.ati_drivers_x11} /run/opengl-driver"
+              else
+                ''
+                  ${optionalString cfg.driSupport "ln -sf ${pkgs.mesa_drivers} /run/opengl-driver"}
+                  ${optionalString cfg.driSupport32Bit
+                    "ln -sf ${pkgs_i686.mesa_drivers} /run/opengl-driver-32"}
+                ''
+            }
+
+            ${cfg.displayManager.job.preStart}
+
+            rm -f /tmp/.X0-lock
+          '';
+
+        script = "${cfg.displayManager.job.execCmd}";
+      };
+
+    services.xserver.displayManager.xserverArgs =
+      [ "-ac"
+        "-logverbose"
+        "-verbose"
+        "-terminate"
+        "-logfile" "/var/log/X.${toString cfg.display}.log"
+        "-config ${configFile}"
+        ":${toString cfg.display}" "vt${toString cfg.tty}"
+        "-xkbdir" "${pkgs.xkeyboard_config}/etc/X11/xkb"
+      ] ++ optional (!cfg.enableTCP) "-nolisten tcp";
+
+    services.xserver.modules =
+      concatLists (catAttrs "modules" drivers) ++
+      [ xorg.xorgserver
+        xorg.xf86inputevdev
+      ];
+
+    services.xserver.config =
+      ''
+        Section "ServerFlags"
+          Option "AllowMouseOpenFail" "on"
+        EndSection
+
+        Section "Module"
+          ${cfg.moduleSection}
+        EndSection
+
+        Section "Monitor"
+          Identifier "Monitor[0]"
+          ${cfg.monitorSection}
+        EndSection
+
+        Section "InputClass"
+          Identifier "Keyboard catchall"
+          MatchIsKeyboard "on"
+          Option "XkbRules" "base"
+          Option "XkbModel" "${cfg.xkbModel}"
+          Option "XkbLayout" "${cfg.layout}"
+          Option "XkbOptions" "${cfg.xkbOptions}"
+          Option "XkbVariant" "${cfg.xkbVariant}"
+        EndSection
+
+        Section "ServerLayout"
+          Identifier "Layout[all]"
+          ${cfg.serverLayoutSection}
+          # Reference the Screen sections for each driver.  This will
+          # cause the X server to try each in turn.
+          ${flip concatMapStrings drivers (d: ''
+            Screen "Screen-${d.name}[0]"
+          '')}
+        EndSection
+
+        # For each supported driver, add a "Device" and "Screen"
+        # section.
+        ${flip concatMapStrings drivers (driver: ''
+
+          Section "Device"
+            Identifier "Device-${driver.name}[0]"
+            Driver "${driver.driverName}"
+            ${cfg.deviceSection}
+            ${xrandrDeviceSection}
+          EndSection
+
+          Section "Screen"
+            Identifier "Screen-${driver.name}[0]"
+            Device "Device-${driver.name}[0]"
+            ${optionalString (cfg.monitorSection != "") ''
+              Monitor "Monitor[0]"
+            ''}
+
+            ${cfg.screenSection}
+
+            ${optionalString (cfg.defaultDepth != 0) ''
+              DefaultDepth ${toString cfg.defaultDepth}
+            ''}
+
+            ${optionalString (driver.name == "nvidia") ''
+              Option "RandRRotation" "on"
+            ''}
+
+            ${optionalString
+                (driver.name != "virtualbox" &&
+                 (cfg.resolutions != [] ||
+                  cfg.extraDisplaySettings != "" ||
+                  cfg.virtualScreen != null))
+              (let
+                f = depth:
+                  ''
+                    SubSection "Display"
+                      Depth ${toString depth}
+                      ${optionalString (cfg.resolutions != [])
+                        "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
+                      ${cfg.extraDisplaySettings}
+                      ${optionalString (cfg.virtualScreen != null)
+                        "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
+                    EndSubSection
+                  '';
+              in concatMapStrings f [8 16 24]
+            )}
+
+          EndSection
+        '')}
+
+        ${xrandrMonitorSections}
+      '';
+
+  }));
+
+}
+
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
new file mode 100644
index 00000000000..dc017563217
--- /dev/null
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -0,0 +1,155 @@
+# generate the script used to activate the configuration.
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  addAttributeName = mapAttrs (a: v: v // {
+    text = ''
+      #### Activation script snippet ${a}:
+      ${v.text}
+    '';
+  });
+
+  path =
+    [ pkgs.coreutils pkgs.gnugrep pkgs.findutils
+      pkgs.glibc # needed for getent
+      pkgs.shadow
+      pkgs.nettools # needed for hostname
+    ];
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    system.activationScripts = mkOption {
+      default = {};
+
+      example = {
+        stdio = {
+          text = ''
+            # Needed by some programs.
+            ln -sfn /proc/self/fd /dev/fd
+            ln -sfn /proc/self/fd/0 /dev/stdin
+            ln -sfn /proc/self/fd/1 /dev/stdout
+            ln -sfn /proc/self/fd/2 /dev/stderr
+          '';
+          deps = [];
+        };
+      };
+
+      description = ''
+        Activate the new configuration (i.e., update /etc, make accounts,
+        and so on).
+      '';
+
+      merge = mergeTypedOption "script" builtins.isAttrs (fold mergeAttrs {});
+
+      apply = set: {
+        script =
+          ''
+            #! ${pkgs.stdenv.shell}
+
+            systemConfig=@out@
+
+            export PATH=/empty
+            for i in ${toString path}; do
+                PATH=$PATH:$i/bin:$i/sbin
+            done
+
+            # Ensure a consistent umask.
+            umask 0022
+
+            ${
+              let
+                set' = mapAttrs (n: v: if builtins.isString v then noDepEntry v else v) set;
+                withHeadlines = addAttributeName set';
+              in textClosureMap id (withHeadlines) (attrNames withHeadlines)
+            }
+
+            # Make this configuration the current configuration.
+            # The readlink is there to ensure that when $systemConfig = /system
+            # (which is a symlink to the store), /run/current-system is still
+            # used as a garbage collection root.
+            ln -sfn "$(readlink -f "$systemConfig")" /run/current-system
+
+            # Prevent the current configuration from being garbage-collected.
+            ln -sfn /run/current-system /nix/var/nix/gcroots/current-system
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.activationScripts.stdio =
+      ''
+        # Needed by some programs.
+        ln -sfn /proc/self/fd /dev/fd
+        ln -sfn /proc/self/fd/0 /dev/stdin
+        ln -sfn /proc/self/fd/1 /dev/stdout
+        ln -sfn /proc/self/fd/2 /dev/stderr
+      '';
+
+    system.activationScripts.var =
+      ''
+        # Various log/runtime directories.
+
+        touch /var/run/utmp # must exist
+        chgrp ${toString config.ids.gids.utmp} /var/run/utmp
+        chmod 664 /var/run/utmp
+
+        mkdir -m 0755 -p /var/run/nix/current-load # for distributed builds
+        mkdir -m 0700 -p /var/run/nix/remote-stores
+
+        # Directory holding symlinks to currently running Upstart
+        # jobs.  Used to determine which jobs need to be restarted
+        # when switching to a new configuration.
+        mkdir -m 0700 -p /var/run/upstart-jobs
+
+        mkdir -m 0755 -p /var/log
+
+        touch /var/log/wtmp # must exist
+        chmod 644 /var/log/wtmp
+
+        touch /var/log/lastlog
+        chmod 644 /var/log/lastlog
+
+        mkdir -m 1777 -p /var/tmp
+
+        # Empty, read-only home directory of many system accounts.
+        mkdir -m 0555 -p /var/empty
+      '';
+
+    system.activationScripts.media =
+      ''
+        mkdir -m 0755 -p /media
+      '';
+
+    system.activationScripts.usrbinenv =
+      ''
+        mkdir -m 0755 -p /usr/bin
+        ln -sfn ${pkgs.coreutils}/bin/env /usr/bin/.env.tmp
+        mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env
+      '';
+
+    system.activationScripts.tmpfs =
+      ''
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devSize}" none /dev
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.devShmSize}" none /dev/shm
+        ${pkgs.utillinux}/bin/mount -o "remount,size=${config.boot.runSize}" none /run
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/system/activation/no-clone.nix b/nixos/modules/system/activation/no-clone.nix
new file mode 100644
index 00000000000..f15809e4d8b
--- /dev/null
+++ b/nixos/modules/system/activation/no-clone.nix
@@ -0,0 +1,11 @@
+# This configuration is not made to figure inside the module-list.nix to
+# allow clone of the first level.
+{pkgs, ...}:
+
+with pkgs.lib;
+
+{
+  boot.loader.grub.device = mkOverrideTemplate 0 {} "nodev";
+  nesting.children = mkOverrideTemplate 0 {} [];
+  nesting.clone = mkOverrideTemplate 0 {} [];
+}
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
new file mode 100644
index 00000000000..33ae3aef9fc
--- /dev/null
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -0,0 +1,362 @@
+#! @perl@
+
+use strict;
+use warnings;
+use File::Basename;
+use File::Slurp;
+use Sys::Syslog qw(:standard :macros);
+use Cwd 'abs_path';
+
+my $out = "@out@";
+
+my $startListFile = "/run/systemd/start-list";
+my $restartListFile = "/run/systemd/restart-list";
+my $reloadListFile = "/run/systemd/reload-list";
+
+my $action = shift @ARGV;
+
+if (!defined $action || ($action ne "switch" && $action ne "boot" && $action ne "test")) {
+    print STDERR <<EOF;
+Usage: $0 [switch|boot|test]
+
+switch: make the configuration the boot default and activate now
+boot:   make the configuration the boot default
+test:   activate the configuration, but don\'t make it the boot default
+EOF
+    exit 1;
+}
+
+die "This is not a NixOS installation (/etc/NIXOS is missing)!\n" unless -f "/etc/NIXOS";
+
+openlog("nixos", "", LOG_USER);
+
+# Install or update the bootloader.
+if ($action eq "switch" || $action eq "boot") {
+    system("@installBootLoader@ $out") == 0 or exit 1;
+}
+
+# Just in case the new configuration hangs the system, do a sync now.
+system("@coreutils@/bin/sync") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1";
+
+exit 0 if $action eq "boot";
+
+# Check if we can activate the new configuration.
+my $oldVersion = read_file("/run/current-system/init-interface-version", err_mode => 'quiet') // "";
+my $newVersion = read_file("$out/init-interface-version");
+
+if ($newVersion ne $oldVersion) {
+    print STDERR <<EOF;
+Warning: the new NixOS configuration has an ‘init’ that is
+incompatible with the current configuration.  The new configuration
+won\'t take effect until you reboot the system.
+EOF
+    exit 100;
+}
+
+syslog(LOG_NOTICE, "switching to system configuration $out");
+
+# Ignore SIGHUP so that we're not killed if we're running on (say)
+# virtual console 1 and we restart the "tty1" unit.
+$SIG{PIPE} = "IGNORE";
+
+sub getActiveUnits {
+    # FIXME: use D-Bus or whatever to query this, since parsing the
+    # output of list-units is likely to break.
+    my $lines = `@systemd@/bin/systemctl list-units --full`;
+    my $res = {};
+    foreach my $line (split '\n', $lines) {
+        chomp $line;
+        last if $line eq "";
+        $line =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s/ or next;
+        next if $1 eq "UNIT";
+        $res->{$1} = { load => $2, state => $3, substate => $4 };
+    }
+    return $res;
+}
+
+sub parseFstab {
+    my ($filename) = @_;
+    my ($fss, $swaps);
+    foreach my $line (read_file($filename, err_mode => 'quiet')) {
+        chomp $line;
+        $line =~ s/^\s*#.*//;
+        next if $line =~ /^\s*$/;
+        my @xs = split / /, $line;
+        if ($xs[2] eq "swap") {
+            $swaps->{$xs[0]} = { options => $xs[3] // "" };
+        } else {
+            $fss->{$xs[1]} = { device => $xs[0], fsType => $xs[2], options => $xs[3] // "" };
+        }
+    }
+    return ($fss, $swaps);
+}
+
+sub parseUnit {
+    my ($filename) = @_;
+    my $info = {};
+    foreach my $line (read_file($filename)) {
+        # FIXME: not quite correct.
+        $line =~ /^([^=]+)=(.*)$/ or next;
+        $info->{$1} = $2;
+    }
+    return $info;
+}
+
+sub boolIsTrue {
+    my ($s) = @_;
+    return $s eq "yes" || $s eq "true";
+}
+
+# Stop all services that no longer exist or have changed in the new
+# configuration.
+my (@unitsToStop, @unitsToSkip);
+my $activePrev = getActiveUnits;
+while (my ($unit, $state) = each %{$activePrev}) {
+    my $baseUnit = $unit;
+
+    # Recognise template instances.
+    $baseUnit = "$1\@.$2" if $unit =~ /^(.*)@[^\.]*\.(.*)$/;
+    my $prevUnitFile = "/etc/systemd/system/$baseUnit";
+    my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+
+    my $baseName = $baseUnit;
+    $baseName =~ s/\.[a-z]*$//;
+
+    if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
+        if (! -e $newUnitFile) {
+            push @unitsToStop, $unit;
+        }
+
+        elsif ($unit =~ /\.target$/) {
+            my $unitInfo = parseUnit($newUnitFile);
+
+            # Cause all active target units to be restarted below.
+            # This should start most changed units we stop here as
+            # well as any new dependencies (including new mounts and
+            # swap devices).  FIXME: the suspend target is sometimes
+            # active after the system has resumed, which probably
+            # should not be the case.  Just ignore it.
+            if ($unit ne "suspend.target" && $unit ne "hibernate.target" && $unit ne "hybrid-sleep.target") {
+                unless (boolIsTrue($unitInfo->{'RefuseManualStart'} // "no")) {
+                    write_file($startListFile, { append => 1 }, "$unit\n");
+                }
+            }
+
+            # Stop targets that have X-StopOnReconfiguration set.
+            # This is necessary to respect dependency orderings
+            # involving targets: if unit X starts after target Y and
+            # target Y starts after unit Z, then if X and Z have both
+            # changed, then X should be restarted after Z.  However,
+            # if target Y is in the "active" state, X and Z will be
+            # restarted at the same time because X's dependency on Y
+            # is already satisfied.  Thus, we need to stop Y first.
+            # Stopping a target generally has no effect on other units
+            # (unless there is a PartOf dependency), so this is just a
+            # bookkeeping thing to get systemd to do the right thing.
+            if (boolIsTrue($unitInfo->{'X-StopOnReconfiguration'} // "no")) {
+                push @unitsToStop, $unit;
+            }
+        }
+
+        elsif (abs_path($prevUnitFile) ne abs_path($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$/) {
+                # Reload the changed mount unit to force a remount.
+                write_file($reloadListFile, { append => 1 }, "$unit\n");
+            } elsif ($unit =~ /\.socket$/ || $unit =~ /\.path$/) {
+                # FIXME: do something?
+            } else {
+                my $unitInfo = parseUnit($newUnitFile);
+                if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
+                    push @unitsToSkip, $unit;
+                } else {
+                    # If this unit is socket-activated, then stop the
+                    # socket unit(s) as well, and restart the
+                    # socket(s) instead of the service.
+                    my $socketActivated = 0;
+                    if ($unit =~ /\.service$/) {
+                        my @sockets = split / /, ($unitInfo->{Sockets} // "");
+                        if (scalar @sockets == 0) {
+                            @sockets = ("$baseName.socket");
+                        }
+                        foreach my $socket (@sockets) {
+                            if (defined $activePrev->{$socket}) {
+                                push @unitsToStop, $socket;
+                                write_file($startListFile, { append => 1 }, "$socket\n");
+                                $socketActivated = 1;
+                            }
+                        }
+                    }
+
+                    if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes")) {
+
+                        # This unit should be restarted instead of
+                        # stopped and started.
+                        write_file($restartListFile, { append => 1 }, "$unit\n");
+
+                    } else {
+
+                        # If the unit is not socket-activated, record
+                        # that this unit needs to be started below.
+                        # We write this to a file to ensure that the
+                        # service gets restarted if we're interrupted.
+                        if (!$socketActivated) {
+                            write_file($startListFile, { append => 1 }, "$unit\n");
+                        }
+
+                        push @unitsToStop, $unit;
+
+                    }
+                }
+            }
+        }
+    }
+}
+
+sub pathToUnitName {
+    my ($path) = @_;
+    die unless substr($path, 0, 1) eq "/";
+    return "-" if $path eq "/";
+    $path = substr($path, 1);
+    $path =~ s/\//-/g;
+    # FIXME: handle - and unprintable characters.
+    return $path;
+}
+
+sub unique {
+    my %seen;
+    my @res;
+    foreach my $name (@_) {
+        next if $seen{$name};
+        $seen{$name} = 1;
+        push @res, $name;
+    }
+    return @res;
+}
+
+# Compare the previous and new fstab to figure out which filesystems
+# need a remount or need to be unmounted.  New filesystems are mounted
+# automatically by starting local-fs.target.  FIXME: might be nicer if
+# we generated units for all mounts; then we could unify this with the
+# unit checking code above.
+my ($prevFss, $prevSwaps) = parseFstab "/etc/fstab";
+my ($newFss, $newSwaps) = parseFstab "$out/etc/fstab";
+foreach my $mountPoint (keys %$prevFss) {
+    my $prev = $prevFss->{$mountPoint};
+    my $new = $newFss->{$mountPoint};
+    my $unit = pathToUnitName($mountPoint) . ".mount";
+    if (!defined $new) {
+        # Filesystem entry disappeared, so unmount it.
+        push @unitsToStop, $unit;
+    } elsif ($prev->{fsType} ne $new->{fsType} || $prev->{device} ne $new->{device}) {
+        # Filesystem type or device changed, so unmount and mount it.
+        write_file($startListFile, { append => 1 }, "$unit\n");
+        push @unitsToStop, $unit;
+    } elsif ($prev->{options} ne $new->{options}) {
+        # Mount options changes, so remount it.
+        write_file($reloadListFile, { append => 1 }, "$unit\n");
+    }
+}
+
+# Also handles swap devices.
+foreach my $device (keys %$prevSwaps) {
+    my $prev = $prevSwaps->{$device};
+    my $new = $newSwaps->{$device};
+    if (!defined $new) {
+        # Swap entry disappeared, so turn it off.  Can't use
+        # "systemctl stop" here because systemd has lots of alias
+        # units that prevent a stop from actually calling
+        # "swapoff".
+        print STDERR "stopping swap device: $device\n";
+        system("@utillinux@/sbin/swapoff", $device);
+    }
+    # FIXME: update swap options (i.e. its priority).
+}
+
+if (scalar @unitsToStop > 0) {
+    @unitsToStop = unique(@unitsToStop);
+    print STDERR "stopping the following units: ", join(", ", sort(@unitsToStop)), "\n";
+    system("@systemd@/bin/systemctl", "stop", "--", @unitsToStop); # FIXME: ignore errors?
+}
+
+print STDERR "NOT restarting the following units: ", join(", ", sort(@unitsToSkip)), "\n"
+    if scalar @unitsToSkip > 0;
+
+# Activate the new configuration (i.e., update /etc, make accounts,
+# and so on).
+my $res = 0;
+print STDERR "activating the configuration...\n";
+system("$out/activate", "$out") == 0 or $res = 2;
+
+# Restart systemd if necessary.
+if (abs_path("/proc/1/exe") ne abs_path("@systemd@/lib/systemd/systemd")) {
+    print STDERR "restarting systemd...\n";
+    system("@systemd@/bin/systemctl", "daemon-reexec") == 0 or $res = 2;
+}
+
+# Forget about previously failed services.
+system("@systemd@/bin/systemctl", "reset-failed");
+
+# Make systemd reload its units.
+system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3;
+
+# Restart changed services (those that have to be restarted rather
+# than stopped and started).
+my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // ""));
+if (scalar @restart > 0) {
+    print STDERR "restarting the following units: ", join(", ", sort(@restart)), "\n";
+    system("@systemd@/bin/systemctl", "restart", "--", @restart) == 0 or $res = 4;
+    unlink($restartListFile);
+}
+
+# Start all active targets, as well as changed units we stopped above.
+# The latter is necessary because some may not be dependencies of the
+# targets (i.e., they were manually started).  FIXME: detect units
+# 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') // ""));
+print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
+system("@systemd@/bin/systemctl", "start", "--", @start) == 0 or $res = 4;
+unlink($startListFile);
+
+# Reload units that need it.  This includes remounting changed mount
+# units.
+my @reload = unique(split '\n', read_file($reloadListFile, err_mode => 'quiet') // "");
+if (scalar @reload > 0) {
+    print STDERR "reloading the following units: ", join(", ", sort(@reload)), "\n";
+    system("@systemd@/bin/systemctl", "reload", "--", @reload) == 0 or $res = 4;
+    unlink($reloadListFile);
+}
+
+# Signal dbus to reload its configuration.
+system("@systemd@/bin/systemctl", "reload", "dbus.service");
+
+# Print failed and new units.
+my (@failed, @new, @restarting);
+my $activeNew = getActiveUnits;
+while (my ($unit, $state) = each %{$activeNew}) {
+    push @failed, $unit if $state->{state} eq "failed" || $state->{substate} eq "auto-restart";
+    push @new, $unit if $state->{state} ne "failed" && !defined $activePrev->{$unit};
+}
+
+print STDERR "the following new units were started: ", join(", ", sort(@new)), "\n"
+    if scalar @new > 0;
+
+if (scalar @failed > 0) {
+    print STDERR "warning: the following units failed: ", join(", ", sort(@failed)), "\n";
+    foreach my $unit (@failed) {
+        print STDERR "\n";
+        system("COLUMNS=1000 @systemd@/bin/systemctl status --no-pager '$unit' >&2");
+    }
+    $res = 4;
+}
+
+if ($res == 0) {
+    syslog(LOG_NOTICE, "finished switching to system configuration $out");
+} else {
+    syslog(LOG_ERR, "switching to system configuration $out failed (status $res)");
+}
+
+exit $res;
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
new file mode 100644
index 00000000000..32157e41985
--- /dev/null
+++ b/nixos/modules/system/activation/top-level.nix
@@ -0,0 +1,194 @@
+{ config, pkgs, modules, baseModules, ... }:
+
+with pkgs.lib;
+
+let
+
+
+  # This attribute is responsible for creating boot entries for
+  # child configuration. They are only (directly) accessible
+  # when the parent configuration is boot default. For example,
+  # 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;
+    map (childConfig:
+      (import ../../../lib/eval-config.nix {
+        inherit baseModules;
+        modules =
+           (optionals inheritParent modules)
+        ++ [ ./no-clone.nix ]
+        ++ [ childConfig ];
+      }).config.system.build.toplevel
+    ) list;
+
+  children =
+     cloner false config.nesting.children
+  ++ cloner true config.nesting.clone;
+
+
+  systemBuilder =
+    let
+      kernelPath = "${config.boot.kernelPackages.kernel}/" +
+        "${config.system.boot.loader.kernelFile}";
+    in ''
+      mkdir $out
+
+      if [ ! -f ${kernelPath} ]; then
+        echo "The bootloader cannot find the proper kernel image."
+        echo "(Expecting ${kernelPath})"
+        false
+      fi
+
+      ln -s ${kernelPath} $out/kernel
+      ln -s ${config.system.modulesTree} $out/kernel-modules
+
+      ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
+
+      echo "$activationScript" > $out/activate
+      substituteInPlace $out/activate --subst-var out
+      chmod u+x $out/activate
+      unset activationScript
+
+      cp ${config.system.build.bootStage2} $out/init
+      substituteInPlace $out/init --subst-var-by systemConfig $out
+
+      ln -s ${config.system.build.etc}/etc $out/etc
+      ln -s ${config.system.path} $out/sw
+      ln -s "$systemd" $out/systemd
+      ln -s ${config.hardware.firmware} $out/firmware
+
+      echo -n "$kernelParams" > $out/kernel-params
+      echo -n "$configurationName" > $out/configuration-name
+      echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
+      echo -n "$nixosVersion" > $out/nixos-version
+
+      mkdir $out/fine-tune
+      childCount=0
+      for i in $children; do
+        childCount=$(( childCount + 1 ))
+        ln -s $i $out/fine-tune/child-$childCount
+      done
+
+      mkdir $out/bin
+      substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
+      chmod +x $out/bin/switch-to-configuration
+
+      ${config.system.extraSystemBuilderCmds}
+    '';
+
+
+  # Putting it all together.  This builds a store path containing
+  # symlinks to the various parts of the built configuration (the
+  # kernel, the Upstart services, the init scripts, etc.) as well as a
+  # script `switch-to-configuration' that activates the configuration
+  # and makes it bootable.
+  system = pkgs.stdenv.mkDerivation {
+    name = "nixos-${config.system.nixosVersion}";
+    preferLocalBuild = true;
+    buildCommand = systemBuilder;
+
+    inherit (pkgs) utillinux coreutils;
+    systemd = config.systemd.package;
+
+    inherit children;
+    kernelParams =
+      config.boot.kernelParams ++ config.boot.extraKernelParams;
+    installBootLoader =
+      config.system.build.installBootLoader
+      or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
+    activationScript = config.system.activationScripts.script;
+    nixosVersion = config.system.nixosVersion;
+
+    jobs = map (j: j.name) (attrValues config.jobs);
+
+    # Pass the names of all Upstart tasks to the activation script.
+    tasks = attrValues (mapAttrs (n: v: if v.task then ["[${v.name}]=1"] else []) config.jobs);
+
+    # Pass the names of all Upstart jobs that shouldn't be restarted
+    # to the activation script.
+    noRestartIfChanged = attrValues (mapAttrs (n: v: if v.restartIfChanged then [] else ["[${v.name}]=1"]) config.jobs);
+
+    configurationName = config.boot.loader.grub.configurationName;
+
+    # Needed by switch-to-configuration.
+    perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
+  };
+
+
+in
+
+{
+  options = {
+
+    system.build = mkOption {
+      default = {};
+      description = ''
+        Attribute set of derivations used to setup the system.
+      '';
+    };
+
+    nesting.children = mkOption {
+      default = [];
+      description = ''
+        Additional configurations to build.
+      '';
+    };
+
+    nesting.clone = mkOption {
+      default = [];
+      description = ''
+        Additional configurations to build based on the current
+        configuration which is has a lower priority.
+      '';
+    };
+
+    system.boot.loader.id = mkOption {
+      default = "";
+      description = ''
+        Id string of the used bootloader.
+      '';
+    };
+
+    system.boot.loader.kernelFile = mkOption {
+      default = pkgs.stdenv.platform.kernelTarget;
+      type = types.uniq types.string;
+      description = ''
+        Name of the kernel file to be passed to the bootloader.
+      '';
+    };
+
+    system.copySystemConfiguration = mkOption {
+      default = false;
+      description = ''
+        If enabled, copies the NixOS configuration file
+        <literal>$NIXOS_CONFIG</literal> (usually
+        <filename>/etc/nixos/configuration.nix</filename>)
+        to the system store path.
+      '';
+    };
+
+    system.extraSystemBuilderCmds = mkOption {
+      default = "";
+      internal = true;
+      merge = concatStringsSep "\n";
+      description = ''
+        This code will be added to the builder creating the system store path.
+      '';
+    };
+
+  };
+
+
+  config = {
+
+    system.extraSystemBuilderCmds =
+      optionalString
+        config.system.copySystemConfiguration
+        "cp ${maybeEnv "NIXOS_CONFIG" "/etc/nixos/configuration.nix"} $out";
+
+    system.build.toplevel = system;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
new file mode 100644
index 00000000000..4ceabb20df5
--- /dev/null
+++ b/nixos/modules/system/boot/kernel.nix
@@ -0,0 +1,304 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  kernel = config.boot.kernelPackages.kernel;
+
+  kernelModulesConf = pkgs.writeText "nixos.conf"
+    ''
+      ${concatStringsSep "\n" config.boot.kernelModules}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.kernelPackages = mkOption {
+      default = pkgs.linuxPackages;
+      # We don't want to evaluate all of linuxPackages for the manual
+      # - some of it might not even evaluate correctly.
+      defaultText = "pkgs.linuxPackages";
+      example = "pkgs.linuxPackages_2_6_25";
+      description = ''
+        This option allows you to override the Linux kernel used by
+        NixOS.  Since things like external kernel module packages are
+        tied to the kernel you're using, it also overrides those.
+        This option is a function that takes Nixpkgs as an argument
+        (as a convenience), and returns an attribute set containing at
+        the very least an attribute <varname>kernel</varname>.
+        Additional attributes may be needed depending on your
+        configuration.  For instance, if you use the NVIDIA X driver,
+        then it also needs to contain an attribute
+        <varname>nvidia_x11</varname>.
+      '';
+    };
+
+    boot.kernelParams = mkOption {
+      default = [ ];
+      description = ''
+        The kernel parameters.  If you want to add additional
+        parameters, it's best to set
+        <option>boot.extraKernelParams</option>.
+      '';
+    };
+
+    boot.extraKernelParams = mkOption {
+      default = [ ];
+      example = [ "boot.trace" ];
+      description = "Additional user-defined kernel parameters.";
+    };
+
+    boot.consoleLogLevel = mkOption {
+      type = types.int;
+      default = 4;
+      description = ''
+        The kernel console log level.  Only log messages with a
+        priority numerically less than this will appear on the
+        console.
+      '';
+    };
+
+    boot.vesa = mkOption {
+      default = false;
+      description = ''
+        Whether to activate VESA video mode on boot.
+      '';
+    };
+
+    boot.extraModulePackages = mkOption {
+      default = [];
+      # !!! example = [pkgs.nvidia_x11];
+      description = "A list of additional packages supplying kernel modules.";
+    };
+
+    boot.kernelModules = mkOption {
+      default = [];
+      description = ''
+        The set of kernel modules to be loaded in the second stage of
+        the boot process.  Note that modules that are needed to
+        mount the root file system should be added to
+        <option>boot.initrd.availableKernelModules</option> or
+        <option>boot.initrd.kernelModules</option>.
+      '';
+    };
+
+    boot.initrd.availableKernelModules = mkOption {
+      default = [];
+      example = [ "sata_nv" "ext3" ];
+      description = ''
+        The set of kernel modules in the initial ramdisk used during the
+        boot process.  This set must include all modules necessary for
+        mounting the root device.  That is, it should include modules
+        for the physical device (e.g., SCSI drivers) and for the file
+        system (e.g., ext3).  The set specified here is automatically
+        closed under the module dependency relation, i.e., all
+        dependencies of the modules list here are included
+        automatically.  The modules listed here are available in the
+        initrd, but are only loaded on demand (e.g., the ext3 module is
+        loaded automatically when an ext3 filesystem is mounted, and
+        modules for PCI devices are loaded when they match the PCI ID
+        of a device in your system).  To force a module to be loaded,
+        include it in <option>boot.initrd.kernelModules</option>.
+      '';
+    };
+
+    boot.initrd.kernelModules = mkOption {
+      default = [];
+      description = "List of modules that are always loaded by the initrd.";
+    };
+
+    system.modulesTree = mkOption {
+      internal = true;
+      default = [];
+      description = ''
+        Tree of kernel modules.  This includes the kernel, plus modules
+        built outside of the kernel.  Combine these into a single tree of
+        symlinks because modprobe only supports one directory.
+      '';
+      merge = mergeListOption;
+      # Convert the list of path to only one path.
+      apply = pkgs.aggregateModules;
+    };
+
+    system.requiredKernelConfig = mkOption {
+      default = [];
+      example = literalExample ''
+        with config.lib.kernelConfig; [
+          (isYes "MODULES")
+          (isEnabled "FB_CON_DECOR")
+          (isEnabled "BLK_DEV_INITRD")
+        ]
+      '';
+      internal = true;
+      type = types.listOf types.attrs;
+      description = ''
+        This option allows modules to specify the kernel config options that
+        must be set (or unset) for the module to work. Please use the
+        lib.kernelConfig functions to build list elements.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build = { inherit kernel; };
+
+    system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
+
+    # Implement consoleLogLevel both in early boot and using sysctl
+    # (so you don't need to reboot to have changes take effect).
+    boot.kernelParams =
+      [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
+      optionals config.boot.vesa [ "vga=0x317" ];
+
+    boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
+
+    boot.kernelModules = [ "loop" ];
+
+    boot.initrd.availableKernelModules =
+      [ # Note: most of these (especially the SATA/PATA modules)
+        # shouldn't be included by default since nixos-hardware-scan
+        # detects them, but I'm keeping them for now for backwards
+        # compatibility.
+
+        # Some SATA/PATA stuff.
+        "ahci"
+        "sata_nv"
+        "sata_via"
+        "sata_sis"
+        "sata_uli"
+        "ata_piix"
+        "pata_marvell"
+
+        # Standard SCSI stuff.
+        "sd_mod"
+        "sr_mod"
+
+        # Standard IDE stuff.
+        "ide_cd"
+        "ide_disk"
+        "ide_generic"
+
+        # Support USB keyboards, in case the boot fails and we only have
+        # a USB keyboard.
+        "uhci_hcd"
+        "ehci_hcd"
+        "ehci_pci"
+        "ohci_hcd"
+        "xhci_hcd"
+        "usbhid"
+        "hid_generic"
+
+        # Unix domain sockets (needed by udev).
+        "unix"
+
+        # Misc. stuff.
+        "pcips2" "xtkbd"
+
+        # To wait for SCSI devices to appear.
+        "scsi_wait_scan"
+      ];
+
+    boot.initrd.kernelModules =
+      [ # For LVM.
+        "dm_mod"
+      ];
+
+    # The Linux kernel >= 2.6.27 provides firmware.
+    hardware.firmware = [ "${kernel}/lib/firmware" ];
+
+    # 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" ];
+        unitConfig =
+          { DefaultDependencies = "no";
+            Conflicts = "shutdown.target";
+          };
+        serviceConfig =
+          { Type = "oneshot";
+            RemainAfterExit = true;
+            ExecStart = "${config.systemd.package}/lib/systemd/systemd-modules-load";
+            # 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 ];
+      };
+
+    lib.kernelConfig = {
+      isYes = option: {
+        assertion = config: config.isYes option;
+        message = "CONFIG_${option} is not yes!";
+        configLine = "CONFIG_${option}=y";
+      };
+
+      isNo = option: {
+        assertion = config: config.isNo option;
+        message = "CONFIG_${option} is not no!";
+        configLine = "CONFIG_${option}=n";
+      };
+
+      isModule = option: {
+        assertion = config: config.isModule option;
+        message = "CONFIG_${option} is not built as a module!";
+        configLine = "CONFIG_${option}=m";
+      };
+
+      ### Usually you will just want to use these two
+      # True if yes or module
+      isEnabled = option: {
+        assertion = config: config.isEnabled option;
+        message = "CONFIG_${option} is not enabled!";
+        configLine = "CONFIG_${option}=y";
+      };
+
+      # True if no or omitted
+      isDisabled = option: {
+        assertion = config: config.isDisabled option;
+        message = "CONFIG_${option} is not disabled!";
+        configLine = "CONFIG_${option}=n";
+      };
+    };
+
+    # The config options that all modules can depend upon
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      # !!! Should this really be needed?
+      (isYes "MODULES")
+      (isYes "BINFMT_ELF")
+    ];
+
+    # nixpkgs kernels are assumed to have all required features
+    assertions = if config.boot.kernelPackages.kernel ? features then [] else
+      let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
+        { assertion = attrs.assertion cfg; inherit (attrs) message; }
+      ) config.system.requiredKernelConfig;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix
new file mode 100644
index 00000000000..b7821f9509f
--- /dev/null
+++ b/nixos/modules/system/boot/kexec.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, ... }:
+
+{
+  environment.systemPackages = [ pkgs.kexectools ];
+
+  systemd.services."prepare-kexec" =
+    { description = "Preparation for kexec";
+      wantedBy = [ "kexec.target" ];
+      before = [ "systemd-kexec.service" ];
+      unitConfig.DefaultDependencies = false;
+      serviceConfig.Type = "oneshot";
+      path = [ pkgs.kexectools ];
+      script =
+        ''
+          p=$(readlink -f /nix/var/nix/profiles/system)
+          if ! [ -d $p ]; then exit 1; fi
+          exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init"
+        '';
+    };
+
+}
\ No newline at end of file
diff --git a/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh
new file mode 100644
index 00000000000..2f550c98428
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub-builder.sh
@@ -0,0 +1,131 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin:$i/sbin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: efi-boot-stub-builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the efi system partition..."
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+# Also, efi executables need the .efi extension
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' | sed 's|@kernelFile@$|@kernelFile@.efi|'
+}
+
+# Copy a file from the Nix store to the EFI system partition
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="@efiSysMountPoint@/efi/nixos/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+# Copy its kernel, initrd, and startup script to the efi system partition
+# Add the efibootmgr entry if requested
+addEntry() {
+    local path="$1"
+    local generation="$2"
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    local initrd=$(readlink -f $path/initrd)
+    copyToKernelsDir $kernel; kernel=$result
+    copyToKernelsDir $initrd; initrd=$result
+
+    local startup="@efiSysMountPoint@/efi/nixos/generation-$generation-startup.nsh"
+    if ! test -e $startup; then
+        local dstTmp=$startup.tmp.$$
+	echo "$(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params)" > $dstTmp
+        mv $dstTmp $startup
+    fi
+    filesCopied[$startup]=1
+
+    if test -n "@runEfibootmgr@"; then
+      set +e
+      efibootmgr -c -d "@efiDisk@" -g -l $(echo $kernel | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') -L "NixOS $generation Generation" -p "@efiPartition@" \
+        -u systemConfig=$(readlink -f $path) init=$(readlink -f $path/init) initrd=$(echo $initrd | sed 's|@efiSysMountPoint@||' | sed 's|/|\\|g') $(cat $path/kernel-params) > /dev/null 2>&1
+      set -e
+    fi
+
+    if test $(readlink -f "$path") = "$default"; then
+      if test -n "@runEfibootmgr@"; then
+        set +e
+        defaultbootnum=$(efibootmgr | grep "NixOS $generation Generation" | sed 's/Boot//' | sed 's/\*.*//')
+	set -e
+      fi
+
+      if test -n "@installStartupNsh@"; then
+        sed 's|.*@kernelFile@.efi|@kernelFile@.efi|' < $startup > "@efiSysMountPoint@/startup.nsh"
+        cp $kernel "@efiSysMountPoint@/@kernelFile@.efi"
+      fi
+    fi
+}
+
+mkdir -p "@efiSysMountPoint@/efi/nixos/"
+
+# Remove all old boot manager entries
+if test -n "@runEfibootmgr@"; then
+  set +e
+  modprobe efivars > /dev/null 2>&1
+  for bootnum in $(efibootmgr | grep "NixOS" | grep "Generation" | sed 's/Boot//' | sed 's/\*.*//'); do
+    efibootmgr -B -b "$bootnum" > /dev/null 2>&1
+  done
+  set -e
+fi
+
+# Add all generations of the system profile to the system partition, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+if test -n "@runEfibootmgr@"; then
+  set +e
+  efibootmgr -o $defaultbootnum > /dev/null 2>&1
+  set -e
+fi
+
+if test -n "@efiShell@"; then
+  mkdir -pv "@efiSysMountPoint@"/efi/boot
+  cp "@efiShell@" "@efiSysMountPoint@"/efi/boot/boot"@targetArch@".efi
+fi
+
+# Remove obsolete files from the EFI system partition
+for fn in "@efiSysMountPoint@/efi/nixos/"*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
+
+# Run any extra commands users may need
+if test -n "@runEfibootmgr@"; then
+  set +e
+  @postEfiBootMgrCommands@
+  set -e
+fi
diff --git a/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix
new file mode 100644
index 00000000000..735784327bc
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi-boot-stub/efi-boot-stub.nix
@@ -0,0 +1,98 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+  efiBootStubBuilder = pkgs.substituteAll {
+    src = ./efi-boot-stub-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.glibc] ++ (pkgs.stdenv.lib.optionals config.boot.loader.efi.canTouchEfiVariables [pkgs.efibootmgr pkgs.module_init_tools]);
+    inherit (config.boot.loader.efiBootStub) installStartupNsh;
+
+    inherit (config.boot.loader.efi) efiSysMountPoint;
+
+    inherit (config.boot.loader.efi.efibootmgr) efiDisk efiPartition postEfiBootMgrCommands;
+
+    runEfibootmgr = config.boot.loader.efi.canTouchEfiVariables;
+
+    efiShell = if config.boot.loader.efiBootStub.installShell then
+      if pkgs.stdenv.isi686 then
+        pkgs.fetchurl {
+          url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/Ia32/Shell_Full.efi";
+          sha256 = "1gv6kyaspczdp7x8qnx5x76ilriaygkfs99ay7ihhdi6riclkhfl";
+        }
+      else
+        pkgs.fetchurl {
+          url = "https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2/EdkShellBinPkg/FullShell/X64/Shell_Full.efi";
+          sha256 = "1g18z84rlavxr5gsrh2g942rfr6znv9fs3fqww5m7dhmnysgyv8p";
+        }
+    else
+      null;
+
+    kernelFile = platform.kernelTarget;
+    targetArch = if pkgs.stdenv.isi686 then
+      "IA32"
+    else if pkgs.stdenv.isx86_64 then
+      "X64"
+    else
+      throw "Unsupported architecture";
+  };
+
+  # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
+  platform = pkgs.stdenv.platform;
+in
+{
+  options = {
+    boot = {
+      loader = {
+        efiBootStub = {
+
+          enable = mkOption {
+            default = false;
+            description = ''
+              Whether to use the linux kernel as an EFI bootloader.
+              When enabled, the kernel, initrd, and an EFI shell script
+              to boot the system are copied to the EFI system partition.
+            '';
+          };
+
+          installStartupNsh = mkOption {
+            default = false;
+            description = ''
+              Whether to install a startup.nsh in the root of the EFI system partition.
+              For now, it will just boot the latest version when run, the eventual goal
+              is to have a basic menu-type interface.
+            '';
+          };
+
+          installShell = mkOption {
+            default = false;
+            description = ''
+              Whether to install an EFI shell in \EFI\BOOT.
+              This _should_ only be needed for removable devices
+              (CDs, usb sticks, etc.), but it may be an option for broken
+              systems where efibootmgr doesn't work. Particularly useful in
+              conjunction with installStartupNsh
+            '';
+          };
+
+        };
+      };
+    };
+  };
+
+  config = mkIf config.boot.loader.efiBootStub.enable {
+    assertions = [ { assertion = ! config.boot.kernelPackages.kernel ? features || config.boot.kernelPackages.kernel.features ? efiBootStub; message = "This kernel does not support the EFI boot stub"; } ];
+  
+    system = {
+      build.installBootLoader = efiBootStubBuilder;
+      boot.loader.id = "efiBootStub";
+      boot.loader.kernelFile = platform.kernelTarget;
+      requiredKernelConfig = with config.lib.kernelConfig; [
+        (isYes "EFI_STUB")
+      ];
+    };
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/efi.nix b/nixos/modules/system/boot/loader/efi.nix
new file mode 100644
index 00000000000..827b3e39122
--- /dev/null
+++ b/nixos/modules/system/boot/loader/efi.nix
@@ -0,0 +1,49 @@
+{ pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options.boot.loader.efi = {
+    canTouchEfiVariables = mkOption {
+      default = false;
+
+      type = types.bool;
+
+      description = "Whether or not the installation process should modify efi boot variables.";
+    };
+
+    efibootmgr = {
+      efiDisk = mkOption {
+        default = "/dev/sda";
+
+        type = types.string;
+
+        description = "The disk that contains the EFI system partition.";
+      };
+
+      efiPartition = mkOption {
+        default = "1";
+        description = "The partition number of the EFI system partition.";
+      };
+
+      postEfiBootMgrCommands = mkOption {
+        default = "";
+        type = types.string;
+        description = ''
+          Shell commands to be executed immediately after efibootmgr has setup the system EFI.
+          Some systems do not follow the EFI specifications properly and insert extra entries.
+          Others will brick (fix by removing battery) on boot when it finds more than X entries.
+          This hook allows for running a few extra efibootmgr commands to combat these issues.
+        '';
+      };
+    };
+
+    efiSysMountPoint = mkOption {
+      default = "/boot";
+
+      type = types.string;
+
+      description = "Where the EFI System Partition is mounted.";
+    };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
new file mode 100644
index 00000000000..e723b9eb7cb
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
@@ -0,0 +1,106 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: generations-dir-builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot
+
+rm -Rf /boot/system* || true
+
+target=/boot/grub/menu.lst
+tmp=$target.tmp
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="/boot/kernels/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+    local path="$1"
+    local generation="$2"
+    local outdir=/boot/system-$generation
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    local initrd=$(readlink -f $path/initrd)
+
+    if test -n "@copyKernels@"; then
+        copyToKernelsDir $kernel; kernel=$result
+        copyToKernelsDir $initrd; initrd=$result
+    fi
+    
+    mkdir -p $outdir
+    ln -sf $(readlink -f $path) $outdir/system
+    ln -sf $(readlink -f $path/init) $outdir/init
+    ln -sf $initrd $outdir/initrd
+    ln -sf $kernel $outdir/kernel
+
+    if test $(readlink -f "$path") = "$default"; then
+      cp "$kernel" /boot/nixos-kernel
+      cp "$initrd" /boot/nixos-initrd
+      cp "$(readlink -f "$path/init")" /boot/nixos-init
+
+      mkdir -p /boot/default
+      # ln -sfT: overrides target even if it exists.
+      ln -sfT $(readlink -f $path) /boot/default/system
+      ln -sfT $(readlink -f $path/init) /boot/default/init
+      ln -sfT $initrd /boot/default/initrd
+      ln -sfT $kernel /boot/default/kernel
+    fi
+}
+
+if test -n "@copyKernels@"; then
+    mkdir -p /boot/kernels
+fi
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+# Remove obsolete files from /boot/kernels.
+for fn in /boot/kernels/*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
new file mode 100644
index 00000000000..9855c8c19dd
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  generationsDirBuilder = pkgs.substituteAll {
+    src = ./generations-dir-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+    inherit (config.boot.loader.generationsDir) copyKernels;
+  };
+
+  # Temporary check, for nixos to cope both with nixpkgs stdenv-updates and trunk
+  platform = pkgs.stdenv.platform;
+
+in
+
+{
+  options = {
+
+    boot.loader.generationsDir = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to create symlinks to the system generations under
+          <literal>/boot</literal>.  When enabled,
+          <literal>/boot/default/kernel</literal>,
+          <literal>/boot/default/initrd</literal>, etc., are updated to
+          point to the current generation's kernel image, initial RAM
+          disk, and other bootstrap files.
+
+          This optional is not necessary with boot loaders such as GNU GRUB
+          for which the menu is updated to point to the latest bootstrap
+          files.  However, it is needed for U-Boot on platforms where the
+          boot command line is stored in flash memory rather than in a
+          menu file.
+        '';
+      };
+
+      copyKernels = mkOption {
+        default = false;
+        description = "
+          Whether copy the necessary boot files into /boot, so
+          /nix/store is not needed by the boot loader.
+        ";
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.boot.loader.generationsDir.enable {
+
+    system.build.installBootLoader = generationsDirBuilder;
+    system.boot.loader.id = "generationsDir";
+    system.boot.loader.kernelFile = platform.kernelTarget;
+
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
new file mode 100644
index 00000000000..8e9f3253f87
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -0,0 +1,261 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.boot.loader.grub;
+
+  realGrub = if cfg.version == 1 then pkgs.grub else pkgs.grub2;
+
+  grub =
+    # Don't include GRUB if we're only generating a GRUB menu (e.g.,
+    # in EC2 instances).
+    if cfg.devices == ["nodev"]
+    then null
+    else realGrub;
+
+  f = x: if x == null then "" else "" + x;
+
+  grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML
+    { splashImage = f config.boot.loader.grub.splashImage;
+      grub = f grub;
+      shell = "${pkgs.stdenv.shell}";
+      fullVersion = (builtins.parseDrvName realGrub.name).version;
+      inherit (cfg)
+        version extraConfig extraPerEntryConfig extraEntries
+        extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
+        default devices;
+      path = (makeSearchPath "bin" [
+        pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils
+      ]) + ":" + (makeSearchPath "sbin" [
+        pkgs.mdadm
+      ]);
+    });
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.loader.grub = {
+
+      enable = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Whether to enable the GNU GRUB boot loader.
+        '';
+      };
+
+      version = mkOption {
+        default = 2;
+        example = 1;
+        type = types.int;
+        description = ''
+          The version of GRUB to use: <literal>1</literal> for GRUB
+          Legacy (versions 0.9x), or <literal>2</literal> (the
+          default) for GRUB 2.
+        '';
+      };
+
+      device = mkOption {
+        default = "";
+        example = "/dev/hda";
+        type = types.uniq types.string;
+        description = ''
+          The device on which the GRUB boot loader will be installed.
+          The special value <literal>nodev</literal> means that a GRUB
+          boot menu will be generated, but GRUB itself will not
+          actually be installed.  To install GRUB on multiple devices,
+          use <literal>boot.loader.grub.devices</literal>.
+        '';
+      };
+
+      devices = mkOption {
+        default = [];
+        example = [ "/dev/hda" ];
+        type = types.listOf types.string;
+        description = ''
+          The devices on which the boot loader, GRUB, will be
+          installed. Can be used instead of <literal>device</literal> to
+          install grub into multiple devices (e.g., if as softraid arrays holding /boot).
+        '';
+      };
+
+      # !!! How can we mark options as obsolete?
+      bootDevice = mkOption {
+        default = "";
+        description = "Obsolete.";
+      };
+
+      configurationName = mkOption {
+        default = "";
+        example = "Stable 2.6.21";
+        type = types.uniq types.string;
+        description = ''
+          GRUB entry name instead of default.
+        '';
+      };
+
+      extraPrepareConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Additional bash commands to be run at the script that
+          prepares the grub menu entries.
+        '';
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        example = "serial; terminal_output.serial";
+        type = types.lines;
+        description = ''
+          Additional GRUB commands inserted in the configuration file
+          just before the menu entries.
+        '';
+      };
+
+      extraPerEntryConfig = mkOption {
+        default = "";
+        example = "root (hd0)";
+        type = types.lines;
+        description = ''
+          Additional GRUB commands inserted in the configuration file
+          at the start of each NixOS menu entry.
+        '';
+      };
+
+      extraEntries = mkOption {
+        default = "";
+        type = types.lines;
+        example = ''
+          # GRUB 1 example (not GRUB 2 compatible)
+          title Windows
+            chainloader (hd0,1)+1
+
+          # GRUB 2 example
+          menuentry "Windows7" {
+            title Windows7
+            insmod ntfs
+            set root='(hd1,1)'
+            chainloader +1
+          }
+        '';
+        description = ''
+          Any additional entries you want added to the GRUB boot menu.
+        '';
+      };
+
+      extraEntriesBeforeNixOS = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether extraEntries are included before the default option.
+        '';
+      };
+
+      extraFiles = mkOption {
+        default = {};
+        example = literalExample ''
+          { "memtest.bin" = "${pkgs.memtest86plus}/memtest.bin"; }
+        '';
+        description = ''
+          A set of files to be copied to <filename>/boot</filename>.
+          Each attribute name denotes the destination file name in
+          <filename>/boot</filename>, while the corresponding
+          attribute value specifies the source file.
+        '';
+      };
+
+      splashImage = mkOption {
+        default =
+          if cfg.version == 1
+          then pkgs.fetchurl {
+            url = http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz;
+            sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59";
+          }
+          # GRUB 1.97 doesn't support gzipped XPMs.
+          else ./winkler-gnu-blue-640x480.png;
+        example = null;
+        description = ''
+          Background image used for GRUB.  It must be a 640x480,
+          14-colour image in XPM format, optionally compressed with
+          <command>gzip</command> or <command>bzip2</command>.  Set to
+          <literal>null</literal> to run GRUB in text mode.
+        '';
+      };
+
+      configurationLimit = mkOption {
+        default = 100;
+        example = 120;
+        type = types.int;
+        description = ''
+          Maximum of configurations in boot menu. GRUB has problems when
+          there are too many entries.
+        '';
+      };
+
+      copyKernels = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether the GRUB menu builder should copy kernels and initial
+          ramdisks to /boot.  This is done automatically if /boot is
+          on a different partition than /.
+        '';
+      };
+
+      timeout = mkOption {
+        default = 5;
+        type = types.int;
+        description = ''
+          Timeout (in seconds) until GRUB boots the default menu item.
+        '';
+      };
+
+      default = mkOption {
+        default = 0;
+        type = types.int;
+        description = ''
+          Index of the default menu item to be booted.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    boot.loader.grub.devices = optional (cfg.device != "") cfg.device;
+
+    system.build = mkAssert (cfg.devices != [])
+      "You must set the ‘boot.loader.grub.device’ option to make the system bootable."
+      { installBootLoader =
+          "PERL5LIB=${makePerlPath [ pkgs.perlPackages.XMLLibXML pkgs.perlPackages.XMLSAX ]} " +
+          "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}";
+        inherit grub;
+      };
+
+    # Common attribute for boot loaders so only one of them can be
+    # set at once.
+    system.boot.loader.id = "grub";
+
+    environment.systemPackages = [ grub ];
+
+    boot.loader.grub.extraPrepareConfig =
+      concatStrings (mapAttrsToList (n: v: ''
+        ${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}"
+      '') config.boot.loader.grub.extraFiles);
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
new file mode 100644
index 00000000000..a83733db63b
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -0,0 +1,265 @@
+use strict;
+use warnings;
+use XML::LibXML;
+use File::Basename;
+use File::Path;
+use File::stat;
+use File::Copy;
+use POSIX;
+use Cwd;
+
+my $defaultConfig = $ARGV[1] or die;
+
+my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
+
+sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
+
+sub readFile {
+    my ($fn) = @_; local $/ = undef;
+    open FILE, "<$fn" or return undef; my $s = <FILE>; close FILE;
+    local $/ = "\n"; chomp $s; return $s;
+}
+
+sub writeFile {
+    my ($fn, $s) = @_;
+    open FILE, ">$fn" or die "cannot create $fn: $!\n";
+    print FILE $s or die;
+    close FILE or die;
+}
+
+my $grub = get("grub");
+my $grubVersion = int(get("version"));
+my $extraConfig = get("extraConfig");
+my $extraPrepareConfig = get("extraPrepareConfig");
+my $extraPerEntryConfig = get("extraPerEntryConfig");
+my $extraEntries = get("extraEntries");
+my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
+my $splashImage = get("splashImage");
+my $configurationLimit = int(get("configurationLimit"));
+my $copyKernels = get("copyKernels") eq "true";
+my $timeout = int(get("timeout"));
+my $defaultEntry = int(get("default"));
+$ENV{'PATH'} = get("path");
+
+die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
+
+print STDERR "updating GRUB $grubVersion menu...\n";
+
+mkpath("/boot/grub", 0, 0700);
+
+
+# Discover whether /boot is on the same filesystem as / and
+# /nix/store.  If not, then all kernels and initrds must be copied to
+# /boot, and all paths in the GRUB config file must be relative to the
+# root of the /boot filesystem.  `$bootRoot' is the path to be
+# prepended to paths under /boot.
+my $bootRoot = "/boot";
+if (stat("/")->dev != stat("/boot")->dev) {
+    $bootRoot = "";
+    $copyKernels = 1;
+} elsif (stat("/boot")->dev != stat("/nix/store")->dev) {
+    $copyKernels = 1;
+}
+
+
+# Generate the header.
+my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
+
+if ($grubVersion == 1) {
+    $conf .= "
+        default $defaultEntry
+        timeout $timeout
+    ";
+    if ($splashImage) {
+        copy $splashImage, "/boot/background.xpm.gz" or die "cannot copy $splashImage to /boot\n";
+        $conf .= "splashimage $bootRoot/background.xpm.gz\n";
+    }
+}
+
+else {
+    $conf .= "
+        if [ -s \$prefix/grubenv ]; then
+          load_env
+        fi
+
+        # ‘grub-reboot’ sets a one-time saved entry, which we process here and
+        # then delete.
+        if [ \"\${saved_entry}\" ]; then
+          # The next line *has* to look exactly like this, otherwise KDM's
+          # reboot feature won't work properly with GRUB 2.
+          set default=\"\${saved_entry}\"
+          set saved_entry=
+          set prev_saved_entry=
+          save_env saved_entry
+          save_env prev_saved_entry
+          set timeout=1
+        else
+          set default=$defaultEntry
+          set timeout=$timeout
+        fi
+
+        if loadfont $bootRoot/grub/fonts/unicode.pf2; then
+          set gfxmode=640x480
+          insmod gfxterm
+          insmod vbe
+          terminal_output gfxterm
+        fi
+    ";
+
+    if ($splashImage) {
+        # FIXME: GRUB 1.97 doesn't resize the background image if it
+        # doesn't match the video resolution.
+        copy $splashImage, "/boot/background.png" or die "cannot copy $splashImage to /boot\n";
+        $conf .= "
+            insmod png
+            if background_image $bootRoot/background.png; then
+              set color_normal=white/black
+              set color_highlight=black/white
+            else
+              set menu_color_normal=cyan/blue
+              set menu_color_highlight=white/blue
+            fi
+        ";
+    }
+}
+
+$conf .= "$extraConfig\n";
+
+
+# Generate the menu entries.
+$conf .= "\n";
+
+my %copied;
+mkpath("/boot/kernels", 0, 0755) if $copyKernels;
+
+sub copyToKernelsDir {
+    my ($path) = @_;
+    return $path unless $copyKernels;
+    $path =~ /\/nix\/store\/(.*)/ or die;
+    my $name = $1; $name =~ s/\//-/g;
+    my $dst = "/boot/kernels/$name";
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if (! -e $dst) {
+        my $tmp = "$dst.tmp";
+        copy $path, $tmp or die "cannot copy $path to $tmp\n";
+        rename $tmp, $dst or die "cannot rename $tmp to $dst\n";
+    }
+    $copied{$dst} = 1;
+    return "$bootRoot/kernels/$name";
+}
+
+sub addEntry {
+    my ($name, $path) = @_;
+    return unless -e "$path/kernel" && -e "$path/initrd";
+
+    my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
+    my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
+    my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
+
+    # FIXME: $confName
+
+    my $kernelParams =
+        "systemConfig=" . Cwd::abs_path($path) . " " .
+        "init=" . Cwd::abs_path("$path/init") . " " .
+        readFile("$path/kernel-params");
+    my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";
+
+    if ($grubVersion == 1) {
+        $conf .= "title $name\n";
+        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
+        $conf .= "  kernel $xen $xenParams\n" if $xen;
+        $conf .= "  " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
+        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n\n";
+    } else {
+        $conf .= "menuentry \"$name\" {\n";
+        $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
+        $conf .= "  multiboot $xen $xenParams\n" if $xen;
+        $conf .= "  " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
+        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
+        $conf .= "}\n\n";
+    }
+}
+
+
+# Add default entries.
+$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS;
+
+addEntry("NixOS - Default", $defaultConfig);
+
+$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
+
+# extraEntries could refer to @bootRoot@, which we have to substitute
+$conf =~ s/\@bootRoot\@/$bootRoot/g;
+
+# Emit submenus for all system profiles.
+sub addProfile {
+    my ($profile, $description) = @_;
+
+    # Add entries for all generations of this profile.
+    $conf .= "submenu \"$description\" {\n" if $grubVersion == 2;
+
+    sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; }
+
+    my @links = sort
+        { nrFromGen($b) <=> nrFromGen($a) }
+        (glob "$profile-*-link");
+
+    my $curEntry = 0;
+    foreach my $link (@links) {
+        last if $curEntry++ >= $configurationLimit;
+        my $date = strftime("%F", localtime(lstat($link)->mtime));
+        my $version =
+            -e "$link/nixos-version"
+            ? readFile("$link/nixos-version")
+            : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
+        addEntry("NixOS - Configuration " . nrFromGen($link) . " ($date - $version)", $link);
+    }
+
+    $conf .= "}\n" if $grubVersion == 2;
+}
+
+addProfile "/nix/var/nix/profiles/system", "NixOS - All configurations";
+
+if ($grubVersion == 2) {
+    for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
+        my $name = basename($profile);
+        next unless $name =~ /^\w+$/;
+        addProfile $profile, "NixOS - Profile '$name'";
+    }
+}
+
+# Run extraPrepareConfig in sh
+if ($extraPrepareConfig ne "") {
+  system((get("shell"), "-c", $extraPrepareConfig));
+}
+
+# Atomically update the GRUB config.
+my $confFile = $grubVersion == 1 ? "/boot/grub/menu.lst" : "/boot/grub/grub.cfg";
+my $tmpFile = $confFile . ".tmp";
+writeFile($tmpFile, $conf);
+rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n";
+
+
+# Remove obsolete files from /boot/kernels.
+foreach my $fn (glob "/boot/kernels/*") {
+    next if defined $copied{$fn};
+    print STDERR "removing obsolete file $fn\n";
+    unlink $fn;
+}
+
+
+# Install GRUB if the version changed from the last time we installed
+# it.  FIXME: shouldn't we reinstall if ‘devices’ changed?
+my $prevVersion = readFile("/boot/grub/version") // "";
+if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1" || get("fullVersion") ne $prevVersion) {
+    foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) {
+        $dev = $dev->findvalue(".") or die;
+        next if $dev eq "nodev";
+        print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
+        system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
+            or die "$0: installation of GRUB on $dev failed\n";
+    }
+    writeFile("/boot/grub/version", get("fullVersion"));
+}
diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix
new file mode 100644
index 00000000000..a0726c01e20
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -0,0 +1,39 @@
+# This module adds Memtest86+ to the GRUB boot menu.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  memtest86 = pkgs.memtest86plus;
+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.
+      '';
+    };
+  };
+
+  config = mkIf config.boot.loader.grub.memtest86 {
+
+    boot.loader.grub.extraEntries = mkFixStrictness (
+      if config.boot.loader.grub.version == 2 then
+        ''
+          menuentry "Memtest86+" {
+            linux16 @bootRoot@/memtest.bin
+          }
+        ''
+      else
+        throw "Memtest86+ is not supported with GRUB 1.");
+
+    boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
+
+  };
+}
diff --git a/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png
new file mode 100644
index 00000000000..35bbb57b51e
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue-640x480.png
Binary files differdiff --git a/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README
new file mode 100644
index 00000000000..9616362dce2
--- /dev/null
+++ b/nixos/modules/system/boot/loader/grub/winkler-gnu-blue.README
@@ -0,0 +1,6 @@
+This is a resized version of
+
+  http://www.gnu.org/graphics/winkler-gnu-blue.png
+
+by Kyle Winkler and released under the Free Art License
+(http://artlibre.org/licence.php/lalgb.html).
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
new file mode 100644
index 00000000000..9ea224b51f6
--- /dev/null
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
@@ -0,0 +1,114 @@
+#! @python@/bin/python
+import argparse
+import shutil
+import os
+import errno
+import subprocess
+import glob
+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)
+
+system_dir = lambda generation: "/nix/var/nix/profiles/system-%d-link" % (generation)
+
+def write_entry(generation, kernel, initrd):
+    entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation)
+    generation_dir = os.readlink(system_dir(generation))
+    tmp_path = "%s.tmp" % (entry_file)
+    kernel_params = "systemConfig=%s init=%s/init " % (generation_dir, generation_dir)
+    with open("%s/kernel-params" % (generation_dir)) as params_file:
+        kernel_params = kernel_params + params_file.read()
+    with open(tmp_path, 'w') as f:
+        print >> f, "title NixOS"
+        print >> f, "version Generation %d" % (generation)
+        if machine_id is not None: print >> f, "machine-id %s" % (machine_id)
+        print >> f, "linux %s" % (kernel)
+        print >> f, "initrd %s" % (initrd)
+        print >> f, "options %s" % (kernel_params)
+    os.rename(tmp_path, entry_file)
+
+def write_loader_conf(generation):
+    with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
+        if "@timeout@" != "":
+            print >> f, "timeout @timeout@"
+        print >> f, "default nixos-generation-%d" % (generation)
+    os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
+
+def copy_from_profile(generation, name):
+    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))
+    return efi_file_path
+
+def add_entry(generation):
+    efi_kernel_path = copy_from_profile(generation, "kernel")
+    efi_initrd_path = copy_from_profile(generation, "initrd")
+    write_entry(generation, efi_kernel_path, efi_initrd_path)
+
+def mkdir_p(path):
+    try:
+        os.makedirs(path)
+    except OSError as e:
+        if e.errno != errno.EEXIST or not os.path.isdir(path):
+            raise
+
+def get_generations(profile):
+    gen_list = subprocess.check_output([
+        "@nix@/bin/nix-env",
+        "--list-generations",
+        "-p",
+        "/nix/var/nix/profiles/%s" % (profile)
+        ])
+    gen_lines = gen_list.split('\n')
+    gen_lines.pop()
+    return [ int(line.split()[0]) for line in gen_lines ]
+
+def remove_old_entries(gens):
+    slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
+    slice_end = -1 * len(".conf")
+    for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
+        try:
+            gen = int(path[slice_start:slice_end])
+            if not gen in gens:
+                os.unlink(path)
+        except ValueError:
+            pass
+    for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
+        if not path in known_paths:
+            os.unlink(path)
+
+parser = argparse.ArgumentParser(description='Update NixOS-related gummiboot files')
+parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
+args = parser.parse_args()
+
+# We deserve our own env var!
+if os.getenv("NIXOS_INSTALL_GRUB") == "1":
+    if "@canTouchEfiVariables@" == "1":
+        subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "install"])
+    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:
+    with open("/etc/machine-id") as machine_file:
+        machine_id = machine_file.readlines()[0]
+except IOError as e:
+    if e.errno != errno.ENOENT:
+        raise
+    machine_id = None
+
+gens = get_generations("system")
+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
new file mode 100644
index 00000000000..9193cd3bc53
--- /dev/null
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.boot.loader.gummiboot;
+
+  efi = config.boot.loader.efi;
+
+  gummibootBuilder = pkgs.substituteAll {
+    src = ./gummiboot-builder.py;
+
+    isExecutable = true;
+
+    inherit (pkgs) python gummiboot;
+
+    inherit (config.environment) nix;
+
+    inherit (cfg) timeout;
+
+    inherit (efi) efiSysMountPoint canTouchEfiVariables;
+  };
+in {
+  options.boot.loader.gummiboot = {
+    enable = mkOption {
+      default = false;
+
+      type = types.bool;
+
+      description = "Whether to enable the gummiboot UEFI boot manager";
+    };
+
+    timeout = mkOption {
+      default = null;
+
+      example = 4;
+
+      type = types.nullOr types.int;
+
+      description = ''
+        Timeout (in seconds) for how long to show the menu (null if none).
+        Note that even with no timeout the menu can be forced if the space
+        key is pressed during bootup
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
+
+        message = "This kernel does not support the EFI boot stub";
+      }
+    ];
+
+    system = {
+      build.installBootLoader = gummibootBuilder;
+
+      boot.loader.id = "gummiboot";
+
+      requiredKernelConfig = with config.lib.kernelConfig; [
+        (isYes "EFI_STUB")
+      ];
+    };
+  };
+}
diff --git a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
new file mode 100644
index 00000000000..502b3b63af2
--- /dev/null
+++ b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
@@ -0,0 +1,88 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+if test $# -ne 1; then
+    echo "Usage: init-script-builder.sh DEFAULT-CONFIG"
+    exit 1
+fi
+
+defaultConfig="$1"
+
+
+[ "$(stat -f -c '%i' /)" = "$(stat -f -c '%i' /boot)" ] || {
+  # see grub-menu-builder.sh
+  echo "WARNING: /boot being on a different filesystem not supported by init-script-builder.sh"
+}
+
+
+
+target="/sbin/init"
+targetOther="/boot/init-other-configurations-contents.txt"
+
+tmp="$target.tmp"
+tmpOther="$targetOther.tmp"
+
+
+configurationCounter=0
+numAlienEntries=`cat <<EOF | egrep '^[[:space:]]*title' | wc -l
+@extraEntries@
+EOF`
+
+
+
+
+# Add an entry to $targetOther
+addEntry() {
+    local name="$1"
+    local path="$2"
+    local shortSuffix="$3"
+
+    configurationCounter=$((configurationCounter + 1))
+
+    local stage2=$path/init
+
+    content="$(
+      echo "#!/bin/sh"
+      echo "# $name"
+      echo "# created by init-script-builder.sh"
+      echo "export systemConfig=$(readlink -f $path)"
+      echo "exec $stage2"
+    )"
+
+    [ "$path" != "$defaultConfig" ] || { 
+      echo "$content" > $tmp
+      echo "# older configurations: $targetOther" >> $tmp
+      chmod +x $tmp
+    }
+
+    echo -e "$content\n\n" >> $tmpOther
+}
+
+
+mkdir -p /boot /sbin
+
+addEntry "NixOS - Default" $defaultConfig ""
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for link in $((ls -d $defaultConfig/fine-tune/* ) | sort -n); do
+    date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+    addEntry "NixOS - variation" $link ""
+done
+
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    date=$(stat --printf="%y\n" $link | sed 's/\..*//')
+    kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *)
+    addEntry "NixOS - Configuration $generation ($date - $kernelVersion)" $link "$generation ($date)"
+done
+
+mv $tmpOther $targetOther
+mv $tmp $target
diff --git a/nixos/modules/system/boot/loader/init-script/init-script.nix b/nixos/modules/system/boot/loader/init-script/init-script.nix
new file mode 100644
index 00000000000..4b0fcd85b4b
--- /dev/null
+++ b/nixos/modules/system/boot/loader/init-script/init-script.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  initScriptBuilder = pkgs.substituteAll {
+    src = ./init-script-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.loader.initScript = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Some systems require a /sbin/init script which is started.
+          Or having it makes starting NixOS easier.
+          This applies to some kind of hosting services and user mode linux.
+
+          Additionally this script will create
+          /boot/init-other-configurations-contents.txt containing
+          contents of remaining configurations. You can copy paste them into
+          /sbin/init manually running a rescue system or such.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.boot.loader.initScript.enable {
+
+    system.build.installBootLoader = initScriptBuilder;
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder.sh b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
new file mode 100644
index 00000000000..f6ccfe493d8
--- /dev/null
+++ b/nixos/modules/system/boot/loader/raspberrypi/builder.sh
@@ -0,0 +1,109 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot/old
+
+# Convert a path to a file in the Nix store such as
+# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
+cleanName() {
+    local path="$1"
+    echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
+}
+
+# Copy a file from the Nix store to /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="/boot/old/$(cleanName $src)"
+    # Don't copy the file if $dst already exists.  This means that we
+    # have to create $dst atomically to prevent partially copied
+    # kernels or initrd if this script is ever interrupted.
+    if ! test -e $dst; then
+        local dstTmp=$dst.tmp.$$
+        cp $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+copyForced() {
+    local src="$1"
+    local dst="$2"
+    cp $src $dst.tmp
+    mv $dst.tmp $dst
+}
+
+outdir=/boot/old
+mkdir -p $outdir || true
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+    local path="$1"
+    local generation="$2"
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    # local initrd=$(readlink -f $path/initrd)
+
+    if test -n "@copyKernels@"; then
+        copyToKernelsDir $kernel; kernel=$result
+        # copyToKernelsDir $initrd; initrd=$result
+    fi
+    
+    echo $(readlink -f $path) > $outdir/$generation-system
+    echo $(readlink -f $path/init) > $outdir/$generation-init
+    cp $path/kernel-params $outdir/$generation-cmdline.txt
+    # echo $initrd > $outdir/$generation-initrd
+    echo $kernel > $outdir/$generation-kernel
+
+    if test $(readlink -f "$path") = "$default"; then
+      copyForced $kernel /boot/kernel.img
+      # copyForced $initrd /boot/initrd
+      cp "$(readlink -f "$path/init")" /boot/nixos-init
+      echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt
+
+      echo "$2" > /boot/defaultgeneration
+    fi
+}
+
+# Add all generations of the system profile to the menu, in reverse
+# (most recent to least recent) order.
+for generation in $(
+    (cd /nix/var/nix/profiles && ls -d system-*-link) \
+    | sed 's/system-\([0-9]\+\)-link/\1/' \
+    | sort -n -r); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+# Add the firmware files
+fwdir=@firmware@/share/raspberrypi/boot/
+copyForced $fwdir/bootcode.bin  /boot/bootcode.bin
+copyForced $fwdir/fixup.dat     /boot/fixup.dat
+copyForced $fwdir/fixup_cd.dat  /boot/fixup_cd.dat
+copyForced $fwdir/start.elf     /boot/start.elf
+copyForced $fwdir/start_cd.elf  /boot/start_cd.elf
+
+# Remove obsolete files from /boot/old.
+for fn in /boot/old/*linux* /boot/old/*initrd*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
new file mode 100644
index 00000000000..5bc856c3df0
--- /dev/null
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -0,0 +1,38 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  builder = pkgs.substituteAll {
+    src = ./builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+    firmware = pkgs.raspberrypifw;
+  };
+
+  platform = pkgs.stdenv.platform;
+
+in
+
+{
+  options = {
+
+    boot.loader.raspberryPi.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to create files with the system generations in
+        <literal>/boot</literal>.
+        <literal>/boot/old</literal> will hold files from old generations.
+      '';
+    };
+
+  };
+
+  config = mkIf config.boot.loader.raspberryPi.enable {
+    system.build.installBootLoader = builder;
+    system.boot.loader.id = "raspberrypi";
+    system.boot.loader.kernelFile = platform.kernelTarget;
+  };
+}
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
new file mode 100644
index 00000000000..29f5eb4fd77
--- /dev/null
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -0,0 +1,176 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  luks = config.boot.initrd.luks;
+
+  openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
+    # Wait for luksRoot to appear, e.g. if on a usb drive.
+    # XXX: copied and adapted from stage-1-init.sh - should be
+    # available as a function.
+    if ! test -e ${device}; then
+        echo -n "waiting 10 seconds for device ${device} to appear..."
+        for try in $(seq 10); do
+            sleep 1
+            if test -e ${device}; then break; fi
+            echo -n .
+        done
+        echo "ok"
+    fi
+
+    ${optionalString (keyFile != null) ''
+    if ! test -e ${keyFile}; then
+        echo -n "waiting 10 seconds for key file ${keyFile} to appear..."
+        for try in $(seq 10); do
+            sleep 1
+            if test -e ${keyFile}; then break; fi
+            echo -n .
+        done
+        echo "ok"
+    fi
+    ''}
+
+    # 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}"}"}
+  '';
+
+  isPreLVM = f: f.preLVM;
+  preLVM = filter isPreLVM luks.devices;
+  postLVM = filter (f: !(isPreLVM f)) luks.devices;
+
+in
+{
+
+  options = {
+    boot.initrd.luks.enable = mkOption {
+      default = false;
+      description = "Obsolete.";
+    };
+
+    boot.initrd.luks.mitigateDMAAttacks = mkOption {
+      default = true;
+      description = ''
+        Unless enabled, encryption keys can be easily recovered by an attacker with physical
+        access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port.
+        More information: http://en.wikipedia.org/wiki/DMA_attack
+
+        This option blacklists FireWire drivers, but doesn't remove them. You can manually
+        load the drivers if you need to use a FireWire device, but don't forget to unload them!
+      '';
+    };
+
+    boot.initrd.luks.cryptoModules = mkOption {
+      default =
+        [ "aes" "aes_generic" "blowfish" "twofish"
+          "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
+          (if pkgs.stdenv.system == "x86_64-linux" then "aes_x86_64" else "aes_i586")
+        ];
+      description = ''
+        A list of cryptographic kernel modules needed to decrypt the root device(s).
+        The default includes all common modules.
+      '';
+    };
+
+    boot.initrd.luks.devices = mkOption {
+      default = [ ];
+      example = [ { name = "luksroot"; device = "/dev/sda3"; preLVM = true; } ];
+      description = ''
+        The list of devices that should be decrypted using LUKS before trying to mount the
+        root partition. This works for both LVM-over-LUKS and LUKS-over-LVM setups.
+
+        The devices are decrypted to the device mapper names defined.
+
+        Make sure that initrd has the crypto modules needed for decryption.
+      '';
+
+      type = types.listOf types.optionSet;
+
+      options = {
+
+        name = mkOption {
+          example = "luksroot";
+          type = types.string;
+          description = "Named to be used for the generated device in /dev/mapper.";
+        };
+
+        device = mkOption {
+          example = "/dev/sda2";
+          type = types.string;
+          description = "Path of the underlying block device.";
+        };
+
+        keyFile = mkOption {
+          default = null;
+          example = "/dev/sdb1";
+          type = types.nullOr types.string;
+          description = ''
+            The name of the file (can be a raw device or a partition) that
+            should be used as the decryption key for the encrypted device. If
+            not specified, you will be prompted for a passphrase instead.
+          '';
+        };
+
+        keyFileSize = mkOption {
+          default = null;
+          example = 4096;
+          type = types.nullOr types.int;
+          description = ''
+            The size of the key file. Use this if only the beginning of the
+            key file should be used as a key (often the case if a raw device
+            or partition is used as key file). If not specified, the whole
+            <literal>keyFile</literal> will be used decryption, instead of just
+            the first <literal>keyFileSize</literal> bytes.
+          '';
+        };
+
+        preLVM = mkOption {
+          default = true;
+          type = types.bool;
+          description = "Whether the luksOpen will be attempted before LVM scan or after it.";
+        };
+
+        allowDiscards = mkOption {
+          default = false;
+          type = types.bool;
+          description = ''
+            Whether to allow TRIM requests to the underlying device. This option
+            has security implications, please read the LUKS documentation before
+            activating in.
+          '';
+        };
+
+      };
+    };
+  };
+
+  config = mkIf (luks.devices != []) {
+
+    # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested
+    boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
+      ["firewire_ohci" "firewire_core" "firewire_sbp2"];
+
+    # Some modules that may be needed for mounting anything ciphered
+    boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" ] ++ luks.cryptoModules;
+
+    # 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
+    '';
+
+    boot.initrd.extraUtilsCommandsTest = ''
+      $out/bin/cryptsetup --version
+    '';
+
+    boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
+    boot.initrd.postDeviceCommands = concatMapStrings openCommand postLVM;
+
+    environment.systemPackages = [ pkgs.cryptsetup ];
+  };
+}
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
new file mode 100644
index 00000000000..8b2762e2526
--- /dev/null
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -0,0 +1,112 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    system.sbin.modprobe = mkOption {
+      internal = true;
+      default = pkgs.writeTextFile {
+        name = "modprobe";
+        destination = "/sbin/modprobe";
+        executable = true;
+        text =
+          ''
+            #! ${pkgs.stdenv.shell}
+            export MODULE_DIR=/run/current-system/kernel-modules/lib/modules
+
+            # Fall back to the kernel modules used at boot time if the
+            # modules in the current configuration don't match the
+            # running kernel.
+            if [ ! -d "$MODULE_DIR/$(${pkgs.coreutils}/bin/uname -r)" ]; then
+                MODULE_DIR=/run/booted-system/kernel-modules/lib/modules/
+            fi
+
+            exec ${pkgs.kmod}/sbin/modprobe "$@"
+          '';
+      };
+      description = ''
+        Wrapper around modprobe that sets the path to the modules
+        tree.
+      '';
+    };
+
+    boot.blacklistedKernelModules = mkOption {
+      default = [];
+      example = [ "cirrusfb" "i2c_piix4" ];
+      description = ''
+        List of names of kernel modules that should not be loaded
+        automatically by the hardware probing code.
+      '';
+    };
+
+    boot.extraModprobeConfig = mkOption {
+      default = "";
+      example =
+        ''
+          options parport_pc io=0x378 irq=7 dma=1
+        '';
+      description = ''
+        Any additional configuration to be appended to the generated
+        <filename>modprobe.conf</filename>.  This is typically used to
+        specify module options.  See
+        <citerefentry><refentrytitle>modprobe.conf</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+      type = types.lines;
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.etc = singleton
+      { source = pkgs.writeText "modprobe.conf"
+          ''
+            ${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
+              blacklist ${name}
+            '')}
+            ${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
+        # in the right location in the Nix store for kernel modules).
+        # We need this when the kernel (or some module) auto-loads a
+        # module.
+        echo ${config.system.sbin.modprobe}/sbin/modprobe > /proc/sys/kernel/modprobe
+      '';
+
+    environment.variables.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
+
+  };
+
+}
diff --git a/nixos/modules/system/boot/readonly-mountpoint.c b/nixos/modules/system/boot/readonly-mountpoint.c
new file mode 100644
index 00000000000..27b66687382
--- /dev/null
+++ b/nixos/modules/system/boot/readonly-mountpoint.c
@@ -0,0 +1,20 @@
+#include <sys/statvfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char ** argv) {
+	struct statvfs stat;
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s PATH", argv[0]);
+		exit(2);
+	}
+	if (statvfs(argv[1], &stat) != 0) {
+		perror("statvfs");
+		exit(3);
+	}
+	if (stat.f_flag & ST_RDONLY)
+		exit(0);
+	else
+		exit(1);
+}
+
diff --git a/nixos/modules/system/boot/shutdown.nix b/nixos/modules/system/boot/shutdown.nix
new file mode 100644
index 00000000000..ad71a2e816e
--- /dev/null
+++ b/nixos/modules/system/boot/shutdown.nix
@@ -0,0 +1,27 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  # This unit saves the value of the system clock to the hardware
+  # clock on shutdown.
+  systemd.units."save-hwclock.service" =
+    { wantedBy = [ "shutdown.target" ];
+
+      text =
+        ''
+          [Unit]
+          Description=Save Hardware Clock
+          DefaultDependencies=no
+          Before=shutdown.target
+
+          [Service]
+          Type=oneshot
+          ExecStart=${pkgs.utillinux}/sbin/hwclock --systohc ${if config.time.hardwareClockInLocalTime then "--localtime" else "--utc"}
+        '';
+    };
+
+  boot.kernel.sysctl."kernel.poweroff_cmd" = "${config.systemd.package}/sbin/poweroff";
+
+}
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
new file mode 100644
index 00000000000..e3e07c08580
--- /dev/null
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -0,0 +1,374 @@
+#! @shell@
+
+targetRoot=/mnt-root
+console=tty1
+
+export LD_LIBRARY_PATH=@extraUtils@/lib
+export PATH=@extraUtils@/bin:@extraUtils@/sbin
+
+
+fail() {
+    if [ -n "$panicOnFail" ]; then exit 1; fi
+
+    # If starting stage 2 failed, allow the user to repair the problem
+    # in an interactive shell.
+    cat <<EOF
+
+An error occured 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:
+
+EOF
+    if [ -n "$allowShell" ]; then cat <<EOF
+  i) to launch an interactive shell
+  f) to start an interactive shell having pid 1 (needed if you want to
+     start stage 2's init manually)
+EOF
+    fi
+    cat <<EOF
+  r) to reboot immediately
+  *) to ignore the error and continue
+EOF
+
+    read reply
+
+    if [ -n "$allowShell" -a "$reply" = f ]; then
+        exec setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console"
+    elif [ -n "$allowShell" -a "$reply" = i ]; then
+        echo "Starting interactive shell..."
+        setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
+    elif [ "$reply" = r ]; then
+        echo "Rebooting..."
+        reboot -f
+    else
+        echo "Continuing..."
+    fi
+}
+
+trap 'fail' 0
+
+
+# Print a greeting.
+echo
+echo "<<< NixOS Stage 1 >>>"
+echo
+
+
+# Mount special file systems.
+mkdir -p /etc
+touch /etc/fstab # to shut up mount
+touch /etc/mtab # to shut up mke2fs
+mkdir -p /proc
+mount -t proc none /proc
+mkdir -p /sys
+mount -t sysfs none /sys
+mount -t devtmpfs -o "size=@devSize@" none /dev
+mkdir -p /run
+mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
+
+
+# Process the kernel command line.
+export stage2Init=/init
+for o in $(cat /proc/cmdline); do
+    case $o in
+        console=*)
+            set -- $(IFS==; echo $o)
+            params=$2
+            set -- $(IFS=,; echo $params)
+            console=$1
+            ;;
+        init=*)
+            set -- $(IFS==; echo $o)
+            stage2Init=$2
+            ;;
+        boot.trace|debugtrace)
+            # Show each command.
+            set -x
+            ;;
+        boot.shell_on_fail)
+            allowShell=1
+            ;;
+        boot.debug1|debug1) # stop right away
+            allowShell=1
+            fail
+            ;;
+        boot.debug1devices) # stop after loading modules and creating device nodes
+            allowShell=1
+            debug1devices=1
+            ;;
+        boot.debug1mounts) # stop after mounting file systems
+            allowShell=1
+            debug1mounts=1
+            ;;
+        boot.panic_on_fail|stage1panic=1)
+            panicOnFail=1
+            ;;
+        root=*)
+            # If a root device is specified on the kernel command
+            # line, make it available through the symlink /dev/root.
+            # Recognise LABEL= and UUID= to support UNetbootin.
+            set -- $(IFS==; echo $o)
+            if [ $2 = "LABEL" ]; then
+                root="/dev/disk/by-label/$3"
+            elif [ $2 = "UUID" ]; then
+                root="/dev/disk/by-uuid/$3"
+            else
+                root=$2
+            fi
+            ln -s "$root" /dev/root
+            ;;
+    esac
+done
+
+
+# Load the required kernel modules.
+mkdir -p /lib
+ln -s @modulesClosure@/lib/modules /lib/modules
+echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
+for i in @kernelModules@; do
+    echo "loading module $(basename $i)..."
+    modprobe $i || true
+done
+
+
+# Create device nodes in /dev.
+echo "running udev..."
+mkdir -p /etc/udev
+ln -sfn @udevRules@ /etc/udev/rules.d
+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
+@extraUtils@/bin/busybox loadkmap < "@busyboxKeymap@"
+
+
+# XXX: Use case usb->lvm will still fail, usb->luks->lvm is covered
+@preLVMCommands@
+
+
+echo "starting device mapper and LVM..."
+lvm vgchange -ay
+
+if test -n "$debug1devices"; then fail; fi
+
+
+@postDeviceCommands@
+
+
+# Try to resume - all modules are loaded now, and devices exist
+if test -e /sys/power/tuxonice/resume; then
+    if test -n "$(cat /sys/power/tuxonice/resume)"; then
+        echo 0 > /sys/power/tuxonice/user_interface/enabled
+        echo 1 > /sys/power/tuxonice/do_resume || echo "failed to resume..."
+    fi
+fi
+
+if test -e /sys/power/resume -a -e /sys/power/disk; then
+    echo "@resumeDevice@" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
+    echo shutdown > /sys/power/disk
+fi
+
+
+# Return true if the machine is on AC power, or if we can't determine
+# whether it's on AC power.
+onACPower() {
+    ! test -d "/proc/acpi/battery" ||
+    ! ls /proc/acpi/battery/BAT[0-9]* > /dev/null 2>&1 ||
+    ! cat /proc/acpi/battery/BAT*/state | grep "^charging state" | grep -q "discharg"
+}
+
+
+# Check the specified file system, if appropriate.
+checkFS() {
+    local device="$1"
+    local fsType="$2"
+
+    # Only check block devices.
+    if [ ! -b "$device" ]; then return 0; fi
+
+    # Don't check ROM filesystems.
+    if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
+
+    # If we couldn't figure out the FS type, then skip fsck.
+    if [ "$fsType" = auto ]; then
+        echo 'cannot check filesystem with type "auto"!'
+        return 0
+    fi
+
+    # Optionally, skip fsck on journaling filesystems.  This option is
+    # a hack - it's mostly because e2fsck on ext3 takes much longer to
+    # recover the journal than the ext3 implementation in the kernel
+    # does (minutes versus seconds).
+    if test -z "@checkJournalingFS@" -a \
+        \( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
+        -o "$fsType" = xfs -o "$fsType" = jfs \)
+    then
+        return 0
+    fi
+
+    # Don't run `fsck' if the machine is on battery power.  !!! Is
+    # this a good idea?
+    if ! onACPower; then
+        echo "on battery power, so no \`fsck' will be performed on \`$device'"
+        return 0
+    fi
+
+    echo "checking $device..."
+
+    fsckFlags=
+    if test "$fsType" != "btrfs"; then
+        fsckFlags="-V -a"
+    fi
+    fsck $fsckFlags "$device"
+    fsckResult=$?
+
+    if test $(($fsckResult | 2)) = $fsckResult; then
+        echo "fsck finished, rebooting..."
+        sleep 3
+        reboot -f
+    fi
+
+    if test $(($fsckResult | 4)) = $fsckResult; then
+        echo "$device has unrepaired errors, please fix them manually."
+        fail
+    fi
+
+    if test $fsckResult -ge 8; then
+        echo "fsck on $device failed."
+        fail
+    fi
+
+    return 0
+}
+
+
+# Function for mounting a file system.
+mountFS() {
+    local device="$1"
+    local mountPoint="$2"
+    local options="$3"
+    local fsType="$4"
+
+    if [ "$fsType" = auto ]; then
+        fsType=$(blkid -o value -s TYPE "$device")
+        if [ -z "$fsType" ]; then fsType=auto; fi
+    fi
+
+    echo "$device /mnt-root$mountPoint $fsType $options" >> /etc/fstab
+
+    checkFS "$device" "$fsType"
+
+    echo "mounting $device on $mountPoint..."
+
+    mkdir -p "/mnt-root$mountPoint" || true
+
+    # For CIFS mounts, retry a few times before giving up.
+    local n=0
+    while true; do
+        mount "/mnt-root$mountPoint" && break
+        if [ "$fsType" != cifs -o "$n" -ge 10 ]; then fail; break; fi
+        echo "retrying..."
+        n=$((n + 1))
+    done
+}
+
+
+# Try to find and mount the root device.
+mkdir /mnt-root
+
+exec 3< @fsInfo@
+
+while read -u 3 mountPoint; do
+    read -u 3 device
+    read -u 3 fsType
+    read -u 3 options
+
+    # !!! Really quick hack to support bind mounts, i.e., where the
+    # "device" should be taken relative to /mnt-root, not /.  Assume
+    # that every device that starts with / but doesn't start with /dev
+    # is a bind mount.
+    pseudoDevice=
+    case $device in
+        /dev/*)
+            ;;
+        //*)
+            # Don't touch SMB/CIFS paths.
+            pseudoDevice=1
+            ;;
+        /*)
+            device=/mnt-root$device
+            ;;
+        *)
+            # Not an absolute path; assume that it's a pseudo-device
+            # like an NFS path (e.g. "server:/path").
+            pseudoDevice=1
+            ;;
+    esac
+
+    # USB storage devices tend to appear with some delay.  It would be
+    # great if we had a way to synchronously wait for them, but
+    # alas...  So just wait for a few seconds for the device to
+    # appear.  If it doesn't appear, try to mount it anyway (and
+    # probably fail).  This is a fallback for non-device "devices"
+    # that we don't properly recognise.
+    if test -z "$pseudoDevice" -a ! -e $device; then
+        echo -n "waiting for device $device to appear..."
+        for try in $(seq 1 20); do
+            sleep 1
+            if test -e $device; then break; fi
+            echo -n "."
+        done
+        echo
+    fi
+
+    # Wait once more for the udev queue to empty, just in case it's
+    # doing something with $device right now.
+    udevadm settle || true
+
+    mountFS "$device" "$mountPoint" "$options" "$fsType"
+done
+
+exec 3>&-
+
+
+@postMountCommands@
+
+
+# Stop udevd.
+udevadm control --exit || true
+
+# Kill any remaining processes, just to be sure we're not taking any
+# with us into stage 2. unionfs-fuse mounts require the unionfs process.
+pkill -9 -v '(1|unionfs)'
+
+
+if test -n "$debug1mounts"; then fail; fi
+
+
+# Restore /proc/sys/kernel/modprobe to its original value.
+echo /sbin/modprobe > /proc/sys/kernel/modprobe
+
+
+# Start stage 2.  `switch_root' deletes all files in the ramfs on the
+# current root.  Note that $stage2Init might be an absolute symlink,
+# in which case "-e" won't work because we're not in the chroot yet.
+if ! test -e "$targetRoot/$stage2Init" -o -L "$targetRoot/$stage2Init"; then
+    echo "stage 2 init script ($targetRoot/$stage2Init) not found"
+    fail
+fi
+
+mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
+
+mount --move /proc $targetRoot/proc
+mount --move /sys $targetRoot/sys
+mount --move /dev $targetRoot/dev
+mount --move /run $targetRoot/run
+
+exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
+
+fail # should never be reached
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
new file mode 100644
index 00000000000..3836d639513
--- /dev/null
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -0,0 +1,343 @@
+# This module builds the initial ramdisk, which contains an init
+# script that performs the first stage of booting the system: it loads
+# 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, ... }:
+
+with pkgs.lib;
+
+let
+
+  udev = config.systemd.package;
+
+  kernelPackages = config.boot.kernelPackages;
+  modulesTree = config.system.modulesTree;
+
+
+  # Determine the set of modules that we need to mount the root FS.
+  modulesClosure = pkgs.makeModulesClosure {
+    rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
+    kernel = modulesTree;
+    allowMissing = true;
+  };
+
+
+  needsCifsUtils = kernelPackages.kernel ? features
+                && kernelPackages.kernel.features ? needsCifsUtils
+                && kernelPackages.kernel.features.needsCifsUtils
+                && any (fs: fs.fsType == "cifs") fileSystems;
+
+  busybox =
+    if needsCifsUtils
+    then pkgs.busybox.override {
+           extraConfig = ''
+             CONFIG_FEATURE_MOUNT_CIFS n
+             CONFIG_FEATURE_MOUNT_HELPERS y
+           '';
+         }
+    else pkgs.busybox;
+
+
+  # Some additional utilities needed in stage 1, like mount, lvm, fsck
+  # etc.  We don't want to bring in all of those packages, so we just
+  # copy what we need.  Instead of using statically linked binaries,
+  # we just copy what we need from Glibc and use patchelf to make it
+  # work.
+  extraUtils = pkgs.runCommand "extra-utils"
+    { buildInputs = [pkgs.nukeReferences];
+      allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
+      doublePatchelf = pkgs.stdenv.isArm;
+    }
+    ''
+      mkdir -p $out/bin $out/lib
+
+      # Copy what we need from Glibc.
+      cp -pv ${pkgs.glibc}/lib/ld*.so.? $out/lib
+      cp -pv ${pkgs.glibc}/lib/libc.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libm.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libpthread.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/librt.so.* $out/lib
+      cp -pv ${pkgs.glibc}/lib/libdl.so.* $out/lib
+      cp -pv ${pkgs.gcc.gcc}/lib*/libgcc_s.so.* $out/lib
+
+      # Copy BusyBox.
+      cp -rvd ${busybox}/{bin,sbin} $out/
+      chmod -R u+w $out
+
+      # Copy some utillinux stuff.
+      cp -v ${pkgs.utillinux}/sbin/blkid $out/bin
+      cp -pdv ${pkgs.utillinux}/lib/libblkid*.so.* $out/lib
+      cp -pdv ${pkgs.utillinux}/lib/libuuid*.so.* $out/lib
+
+      # Copy dmsetup and lvm.
+      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
+
+      # Add RAID mdadm tool.
+      cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm
+
+      # Copy udev.
+      cp -v ${udev}/lib/systemd/systemd-udevd ${udev}/bin/udevadm $out/bin
+      cp -v ${udev}/lib/udev/*_id $out/bin
+      cp -pdv ${udev}/lib/libudev.so.* $out/lib
+      cp -v ${pkgs.kmod}/lib/libkmod.so.* $out/lib
+      cp -v ${pkgs.acl}/lib/libacl.so.* $out/lib
+      cp -v ${pkgs.attr}/lib/libattr.so.* $out/lib
+
+      # Copy modprobe.
+      cp -v ${pkgs.kmod}/bin/kmod $out/bin/
+      ln -s kmod $out/bin/modprobe
+
+      # Maybe copy cifs utils
+      ${optionalString needsCifsUtils ''
+        cp -v ${pkgs.cifs_utils}/sbin/mount.cifs $out/bin
+      ''}
+
+      ${config.boot.initrd.extraUtilsCommands}
+
+      # Strip binaries further than normal.
+      chmod -R u+w $out
+      stripDirs "lib bin" "-s"
+
+      # Run patchelf to make the programs refer to the copied libraries.
+      for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done
+
+      for i in $out/bin/*; do
+          if ! test -L $i; then
+              echo "patching $i..."
+              patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+              if [ -n "$doublePatchelf" ]; then
+                  patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
+              fi
+          fi
+      done
+
+      # Make sure that the patchelf'ed binaries still work.
+      echo "testing patched programs..."
+      $out/bin/ash -c 'echo hello world' | grep "hello world"
+      export LD_LIBRARY_PATH=$out/lib
+      $out/bin/mount --help 2>&1 | grep "BusyBox"
+      $out/bin/udevadm --version
+      $out/bin/dmsetup --version 2>&1 | tee -a log | grep "version:"
+      LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep "LVM"
+      $out/bin/mdadm --version
+
+      ${config.boot.initrd.extraUtilsCommandsTest}
+    ''; # */
+
+
+  # The initrd only has to mount / or any FS marked as necessary for
+  # booting (such as the FS containing /nix/store, or an FS needed for
+  # mounting /, like / on a loopback).
+  fileSystems = filter
+    (fs: fs.neededForBoot || elem fs.mountPoint [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/etc" ])
+    (attrValues config.fileSystems);
+
+
+  udevRules = pkgs.stdenv.mkDerivation {
+    name = "udev-rules";
+    buildCommand = ''
+      ensureDir $out
+
+      echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
+
+      cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
+      cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
+      cp -v ${pkgs.mdadm}/lib/udev/rules.d/*.rules $out/
+
+      for i in $out/*.rules; do
+          substituteInPlace $i \
+            --replace ata_id ${extraUtils}/bin/ata_id \
+            --replace scsi_id ${extraUtils}/bin/scsi_id \
+            --replace cdrom_id ${extraUtils}/bin/cdrom_id \
+            --replace ${pkgs.utillinux}/sbin/blkid ${extraUtils}/bin/blkid \
+            --replace /sbin/blkid ${extraUtils}/bin/blkid \
+            --replace ${pkgs.lvm2}/sbin ${extraUtils}/bin \
+            --replace /sbin/mdadm ${extraUtils}/bin/mdadm
+      done
+
+      # Work around a bug in QEMU, which doesn't implement the "READ
+      # DISC INFORMATION" SCSI command:
+      #   https://bugzilla.redhat.com/show_bug.cgi?id=609049
+      # As a result, `cdrom_id' doesn't print
+      # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
+      # /dev/disk/by-label symlinks from being created.  We need these
+      # in the NixOS installation CD, so use ID_CDROM_MEDIA in the
+      # corresponding udev rules for now.  This was the behaviour in
+      # udev <= 154.  See also
+      #   http://www.spinics.net/lists/hotplug/msg03935.html
+      substituteInPlace $out/60-persistent-storage.rules \
+        --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
+    ''; # */
+  };
+
+
+  # The binary keymap for busybox to load at boot.
+  busyboxKeymap = pkgs.runCommand "boottime-keymap"
+    { preferLocalBuild = true; }
+    ''
+      ${pkgs.kbd}/bin/loadkeys -qb "${config.i18n.consoleKeyMap}" > $out ||
+        ${pkgs.kbd}/bin/loadkeys -qbu "${config.i18n.consoleKeyMap}" > $out
+    '';
+
+
+  # The init script of boot stage 1 (loading kernel modules for
+  # mounting the root FS).
+  bootStage1 = pkgs.substituteAll {
+    src = ./stage-1-init.sh;
+
+    shell = "${extraUtils}/bin/ash";
+
+    isExecutable = true;
+
+    inherit udevRules extraUtils modulesClosure busyboxKeymap;
+
+    inherit (config.boot) resumeDevice devSize runSize;
+
+    inherit (config.boot.initrd) checkJournalingFS
+      preLVMCommands postDeviceCommands postMountCommands kernelModules;
+
+    fsInfo =
+      let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType fs.options ];
+      in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
+  };
+
+
+  # The closure of the init script of boot stage 1 is what we put in
+  # the initial RAM disk.
+  initialRamdisk = pkgs.makeInitrd {
+    inherit (config.boot.initrd) compressor;
+
+    contents =
+      [ { object = bootStage1;
+          symlink = "/init";
+        }
+        { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
+          symlink = "/etc/mdadm.conf";
+        }
+      ];
+  };
+
+in
+
+{
+  options = {
+
+    boot.resumeDevice = mkOption {
+      default = "";
+      example = "0:0";
+      description = "
+        Device for manual resume attempt during boot. Looks like
+        major:minor. ls -l /dev/SWAP_PARTION shows them.
+      ";
+    };
+
+    boot.initrd.checkJournalingFS = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to run fsck on journaling filesystems such as ext3.
+      '';
+    };
+
+    boot.initrd.mdadmConf = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Contents of /etc/mdadm.conf at initrd.
+      '';
+    };
+
+    boot.initrd.preLVMCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately before lvm discovery.
+      '';
+    };
+
+    boot.initrd.postDeviceCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately after stage 1 of the
+        boot has loaded kernel modules and created device nodes in
+        /dev.
+      '';
+    };
+
+    boot.initrd.postMountCommands = mkOption {
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed immediately after the stage 1
+        filesystems have been mounted.
+      '';
+    };
+
+    boot.initrd.extraUtilsCommands = mkOption {
+      internal = true;
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed in the builder of the
+        extra-utils derivation.  This can be used to provide
+        additional utilities in the initial ramdisk.
+      '';
+    };
+
+    boot.initrd.extraUtilsCommandsTest = mkOption {
+      internal = true;
+      default = "";
+      type = with types; string;
+      description = ''
+        Shell commands to be executed in the builder of the
+        extra-utils derivation after patchelf has done its
+        job.  This can be used to test additional utilities
+        copied in extraUtilsCommands.
+      '';
+    };
+
+    boot.initrd.compressor = mkOption {
+      default = "gzip -9";
+
+      type = types.string;
+
+      description = "The compressor to use on the initrd";
+
+      example = "xz";
+    };
+
+    fileSystems = mkOption {
+      options.neededForBoot = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If set, this file system will be mounted in the initial
+          ramdisk.  By default, this applies to the root file system
+          and to the file system containing
+          <filename>/nix/store</filename>.
+        '';
+      };
+    };
+
+  };
+
+  config = {
+
+    system.build.bootStage1 = bootStage1;
+    system.build.initialRamdisk = initialRamdisk;
+    system.build.extraUtils = extraUtils;
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "TMPFS")
+      (isYes "BLK_DEV_INITRD")
+    ];
+
+  };
+}
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
new file mode 100644
index 00000000000..2fadd3de1f0
--- /dev/null
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -0,0 +1,173 @@
+#! @shell@
+
+systemConfig=@systemConfig@
+
+export HOME=/root
+
+
+# Print a greeting.
+echo
+echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m"
+echo
+
+
+# Set the PATH.
+setPath() {
+    local dirs="$1"
+    export PATH=/empty
+    for i in $dirs; do
+        PATH=$PATH:$i/bin
+        if test -e $i/sbin; then
+            PATH=$PATH:$i/sbin
+        fi
+    done
+}
+
+setPath "@path@"
+
+
+# Normally, stage 1 mounts the root filesystem read/writable.
+# However, in some environments, stage 2 is executed directly, and the
+# root is read-only.  So make it writable here.
+mount -n -o remount,rw /
+
+
+# Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
+# stage 1, we need to do that here.
+if [ ! -e /proc/1 ]; then
+    mkdir -m 0755 -p /proc
+    mount -n -t proc none /proc
+    mkdir -m 0755 -p /dev
+    mount -t devtmpfs none /dev
+fi
+
+
+echo "booting system configuration $systemConfig" > /dev/kmsg
+
+
+# Make /nix/store a read-only bind mount to enforce immutability of
+# the Nix store.  Note that we can't use "chown root:nixbld" here
+# because users/groups might not exist yet.
+chown 0:30000 /nix/store
+chmod 1775 /nix/store
+if [ -n "@readOnlyStore@" ]; then
+    if ! readonly-mountpoint /nix/store; then
+        mount --bind /nix/store /nix/store
+        mount -o remount,ro,bind /nix/store
+    fi
+fi
+
+
+# Provide a /etc/mtab.
+mkdir -m 0755 -p /etc
+test -e /etc/fstab || touch /etc/fstab # to shut up mount
+rm -f /etc/mtab* # not that we care about stale locks
+ln -s /proc/mounts /etc/mtab
+
+
+# Process the kernel command line.
+for o in $(cat /proc/cmdline); do
+    case $o in
+        boot.debugtrace)
+            # Show each command.
+            set -x
+            ;;
+        resume=*)
+            set -- $(IFS==; echo $o)
+            resumeDevice=$2
+            ;;
+    esac
+done
+
+
+# More special file systems, initialise required directories.
+mkdir -m 0755 /dev/shm
+mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /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
+mkdir -m 0755 -p /var /var/log /var/lib /var/db
+mkdir -m 0755 -p /nix/var
+mkdir -m 0700 -p /root
+mkdir -m 0755 -p /bin # for the /bin/sh symlink
+mkdir -m 0755 -p /home
+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
+
+
+# Create a tmpfs on /run to hold runtime state for programs such as
+# udev (if stage 1 hasn't already done so).
+if ! mountpoint -q /run; then
+    rm -rf /run
+    mkdir -m 0755 -p /run
+    mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
+fi
+
+mkdir -m 0755 -p /run/lock
+
+
+# For backwards compatibility, symlink /var/run to /run, and /var/lock
+# to /run/lock.
+ln -s /run /var/run
+ln -s /run/lock /var/lock
+
+
+# Clear the resume device.
+if test -n "$resumeDevice"; then
+    mkswap "$resumeDevice" || echo 'Failed to clear saved image.'
+fi
+
+
+# Run the script that performs all configuration activation that does
+# not have to be done at boot time.
+echo "running activation script..."
+$systemConfig/activate
+
+
+# Record the boot configuration.
+ln -sfn "$systemConfig" /run/booted-system
+
+# Prevent the booted system form being garbage-collected If it weren't
+# a gcroot, if we were running a different kernel, switched system,
+# and garbage collected all, we could not load kernel modules anymore.
+ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system
+
+
+# Run any user-specified commands.
+@shell@ @postBootCommands@
+
+
+# Start systemd.
+echo "starting systemd..."
+PATH=/run/current-system/systemd/lib/systemd \
+    MODULE_DIR=/run/booted-system/kernel-modules/lib/modules \
+    LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive \
+    exec systemd --log-target=journal # --log-level=debug --log-target=console --crash-shell
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
new file mode 100644
index 00000000000..ff17535e418
--- /dev/null
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -0,0 +1,100 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  kernel = config.boot.kernelPackages.kernel;
+  activateConfiguration = config.system.activationScripts.script;
+
+  readonlyMountpoint = pkgs.runCommand "readonly-mountpoint" {} ''
+    mkdir -p $out/bin
+    cc -O3 ${./readonly-mountpoint.c} -o $out/bin/readonly-mountpoint
+    strip -s $out/bin/readonly-mountpoint
+  '';
+
+  bootStage2 = pkgs.substituteAll {
+    src = ./stage-2-init.sh;
+    shellDebug = "${pkgs.bashInteractive}/bin/bash";
+    isExecutable = true;
+    inherit (config.boot) devShmSize runSize cleanTmpDir;
+    inherit (config.nix) readOnlyStore;
+    ttyGid = config.ids.gids.tty;
+    path =
+      [ pkgs.coreutils
+        pkgs.utillinux
+        pkgs.sysvtools
+      ] ++ (optional config.boot.cleanTmpDir pkgs.findutils)
+      ++ optional config.nix.readOnlyStore readonlyMountpoint;
+    postBootCommands = pkgs.writeText "local-cmds"
+      ''
+        ${config.boot.postBootCommands}
+        ${config.powerManagement.powerUpCommands}
+      '';
+  };
+
+in
+
+{
+  options = {
+
+    boot = {
+
+      postBootCommands = mkOption {
+        default = "";
+        example = "rm -f /var/log/messages";
+        type = types.string;
+        description = ''
+          Shell commands to be executed just before systemd is started.
+        '';
+      };
+
+      devSize = mkOption {
+        default = "5%";
+        example = "32m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /dev tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      devShmSize = mkOption {
+        default = "50%";
+        example = "256m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /dev/shm tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      runSize = mkOption {
+        default = "25%";
+        example = "256m";
+        type = types.uniq types.string;
+        description = ''
+          Size limit for the /run tmpfs. Look at mount(8), tmpfs size option,
+          for the accepted syntax.
+        '';
+      };
+
+      cleanTmpDir = mkOption {
+        default = false;
+        example = true;
+        description = ''
+          Delete all files in /tmp/ during boot.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = {
+
+    system.build.bootStage2 = bootStage2;
+
+  };
+}
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
new file mode 100644
index 00000000000..dfb9036ab4d
--- /dev/null
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -0,0 +1,364 @@
+{ config, pkgs }:
+
+with pkgs.lib;
+
+rec {
+
+  unitOptions = {
+
+    enable = mkOption {
+      default = true;
+      types = 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.
+      '';
+    };
+
+    description = mkOption {
+      default = "";
+      types = types.uniq types.string;
+      description = "Description of this unit used in systemd messages and progress indicators.";
+    };
+
+    requires = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started, and stop
+        this unit when the specified units are stopped or fail.
+      '';
+    };
+
+    wants = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started.
+      '';
+    };
+
+    after = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay this unit until they have started.
+      '';
+    };
+
+    before = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay them until this unit has started.
+      '';
+    };
+
+    bindsTo = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Like ‘requires’, but in addition, if the specified units
+        unexpectedly disappear, this unit will be stopped as well.
+      '';
+    };
+
+    partOf = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are stopped or restarted, then this
+        unit is stopped or restarted as well.
+      '';
+    };
+
+    conflicts = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started, then this unit is stopped
+        and vice versa.
+      '';
+    };
+
+    requiredBy = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = "Units that require (i.e. depend on and need to go down with) this unit.";
+    };
+
+    wantedBy = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = "Units that want (i.e. depend on) this unit.";
+    };
+
+    unitConfig = mkOption {
+      default = {};
+      example = { RequiresMountsFor = "/data"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Unit]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+    restartTriggers = mkOption {
+      default = [];
+      description = ''
+        An arbitrary list of items such as derivations.  If any item
+        in the list changes between reconfigurations, the service will
+        be restarted.
+      '';
+    };
+
+  };
+
+
+  serviceOptions = unitOptions // {
+
+    environment = mkOption {
+      default = {};
+      type = types.attrs;
+      example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
+      description = "Environment variables passed to the service's processes.";
+    };
+
+    path = mkOption {
+      default = [];
+      apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}";
+      description = ''
+        Packages added to the service's <envar>PATH</envar>
+        environment variable.  Both the <filename>bin</filename>
+        and <filename>sbin</filename> subdirectories of each
+        package are added.
+      '';
+    };
+
+    serviceConfig = mkOption {
+      default = {};
+      example =
+        { StartLimitInterval = 10;
+          RestartSec = 5;
+        };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Service]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.service</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+
+      check = v:
+        let assertValueOneOf = name: values: attr:
+              let val = getAttr name attr;
+              in optional ( hasAttr name attr && !elem val values) "${name} ${val} not known to systemd";
+            checkType = assertValueOneOf "Type" ["simple" "forking" "oneshot" "dbus" "notify" "idle"];
+            checkRestart = assertValueOneOf "Restart" ["no" "on-success" "on-failure" "on-abort" "always"];
+            errors = concatMap (c: c v) [checkType checkRestart];
+        in if errors == [] then true
+           else builtins.trace (concatStringsSep "\n" errors) false;
+    };
+
+    script = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      description = "Shell commands executed as the service's main process.";
+    };
+
+    scriptArgs = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      description = "Arguments passed to the main process script.";
+    };
+
+    preStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed before the service's main process
+        is started.
+      '';
+    };
+
+    postStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the service's main process
+        is started.
+      '';
+    };
+
+    postStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the service's main process
+        has exited.
+      '';
+    };
+
+    restartIfChanged = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether the service should be restarted during a NixOS
+        configuration switch if its definition has changed.
+      '';
+    };
+
+    stopIfChanged = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        If set, a changed unit is restarted by calling
+        <command>systemctl stop</command> in the old configuration,
+        then <command>systemctl start</command> in the new one.
+        Otherwise, it is restarted in a single step using
+        <command>systemctl restart</command> in the new configuration.
+        The latter is less correct because it runs the
+        <literal>ExecStop</literal> commands from the new
+        configuration.
+      '';
+    };
+
+    startAt = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      example = "Sun 14:00:00";
+      description = ''
+        Automatically start this unit at the given date/time, which
+        must be in the format described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry>.  This is equivalent
+        to adding a corresponding timer unit with
+        <option>OnCalendar</option> set to the value given here.
+      '';
+    };
+
+  };
+
+
+  socketOptions = unitOptions // {
+
+    listenStreams = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      example = [ "0.0.0.0:993" "/run/my-socket" ];
+      description = ''
+        For each item in this list, a <literal>ListenStream</literal>
+        option in the <literal>[Socket]</literal> section will be created.
+      '';
+    };
+
+    socketConfig = mkOption {
+      default = {};
+      example = { ListenStream = "/run/my-socket"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Socket]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.socket</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
+
+  timerOptions = unitOptions // {
+
+    timerConfig = mkOption {
+      default = {};
+      example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Timer]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> and
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+  };
+
+
+  mountOptions = unitOptions // {
+
+    what = mkOption {
+      example = "/dev/sda1";
+      type = types.uniq types.string;
+      description = "Absolute path of device node, file or other resource. (Mandatory)";
+    };
+
+    where = mkOption {
+      example = "/mnt";
+      type = types.uniq types.string;
+      description = ''
+        Absolute path of a directory of the mount point.
+        Will be created if it doesn't exist. (Mandatory)
+      '';
+    };
+
+    type = mkOption {
+      default = "";
+      example = "ext4";
+      type = types.uniq types.string;
+      description = "File system type.";
+    };
+
+    options = mkOption {
+      default = "";
+      example = "noatime";
+      type = types.string;
+      merge = concatStringsSep ",";
+      description = "Options used to mount the file system.";
+    };
+
+    mountConfig = mkOption {
+      default = {};
+      example = { DirectoryMode = "0775"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Mount]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.mount</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+  };
+
+  automountOptions = unitOptions // {
+
+    where = mkOption {
+      example = "/mnt";
+      type = types.uniq types.string;
+      description = ''
+        Absolute path of a directory of the mount point.
+        Will be created if it doesn't exist. (Mandatory)
+      '';
+    };
+
+    automountConfig = mkOption {
+      default = {};
+      example = { DirectoryMode = "0775"; };
+      type = types.attrs;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[Automount]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.automount</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+  };
+
+}
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
new file mode 100644
index 00000000000..a5e1165574c
--- /dev/null
+++ b/nixos/modules/system/boot/systemd.nix
@@ -0,0 +1,678 @@
+{ config, pkgs, utils, ... }:
+
+with pkgs.lib;
+with utils;
+with import ./systemd-unit-options.nix { inherit config pkgs; };
+
+let
+
+  cfg = config.systemd;
+
+  systemd = cfg.package;
+
+  makeUnit = name: unit:
+    pkgs.runCommand "unit" { inherit (unit) text; preferLocalBuild = true; }
+      (if unit.enable then  ''
+        mkdir -p $out
+        echo -n "$text" > $out/${name}
+      '' else ''
+        mkdir -p $out
+        ln -s /dev/null $out/${name}
+      '');
+
+  upstreamUnits =
+    [ # Targets.
+      "basic.target"
+      "sysinit.target"
+      "sockets.target"
+      "graphical.target"
+      "multi-user.target"
+      "getty.target"
+      "network.target"
+      "network-online.target"
+      "nss-lookup.target"
+      "nss-user-lookup.target"
+      "time-sync.target"
+      #"cryptsetup.target"
+      "sigpwr.target"
+      "timers.target"
+      "paths.target"
+
+      # Rescue mode.
+      "rescue.target"
+      "rescue.service"
+
+      # Udev.
+      "systemd-udevd-control.socket"
+      "systemd-udevd-kernel.socket"
+      "systemd-udevd.service"
+      "systemd-udev-settle.service"
+      "systemd-udev-trigger.service"
+
+      # Hardware (started by udev when a relevant device is plugged in).
+      "sound.target"
+      "bluetooth.target"
+      "printer.target"
+      "smartcard.target"
+
+      # Login stuff.
+      "systemd-logind.service"
+      "autovt@.service"
+      #"systemd-vconsole-setup.service"
+      "systemd-user-sessions.service"
+      "dbus-org.freedesktop.login1.service"
+      "user@.service"
+
+      # Journal.
+      "systemd-journald.socket"
+      "systemd-journald.service"
+      "systemd-journal-flush.service"
+      "syslog.socket"
+
+      # SysV init compatibility.
+      "systemd-initctl.socket"
+      "systemd-initctl.service"
+
+      # Kernel module loading.
+      #"systemd-modules-load.service"
+
+      # Filesystems.
+      "systemd-fsck@.service"
+      "systemd-fsck-root.service"
+      "systemd-remount-fs.service"
+      "local-fs.target"
+      "local-fs-pre.target"
+      "remote-fs.target"
+      "remote-fs-pre.target"
+      "swap.target"
+      "dev-hugepages.mount"
+      "dev-mqueue.mount"
+      "sys-fs-fuse-connections.mount"
+      "sys-kernel-config.mount"
+      "sys-kernel-debug.mount"
+
+      # Hibernate / suspend.
+      "hibernate.target"
+      "suspend.target"
+      "sleep.target"
+      "hybrid-sleep.target"
+      "systemd-hibernate.service"
+      "systemd-suspend.service"
+      "systemd-hybrid-sleep.service"
+      "systemd-shutdownd.socket"
+      "systemd-shutdownd.service"
+
+      # Reboot stuff.
+      "reboot.target"
+      "systemd-reboot.service"
+      "poweroff.target"
+      "systemd-poweroff.service"
+      "halt.target"
+      "systemd-halt.service"
+      "ctrl-alt-del.target"
+      "shutdown.target"
+      "umount.target"
+      "final.target"
+      "kexec.target"
+      "systemd-kexec.service"
+
+      # Password entry.
+      "systemd-ask-password-console.path"
+      "systemd-ask-password-console.service"
+      "systemd-ask-password-wall.path"
+      "systemd-ask-password-wall.service"
+    ]
+
+    ++ optionals cfg.enableEmergencyMode [
+      "emergency.target"
+      "emergency.service"
+    ];
+
+  upstreamWants =
+    [ #"basic.target.wants"
+      "sysinit.target.wants"
+      "sockets.target.wants"
+      "local-fs.target.wants"
+      "multi-user.target.wants"
+      "shutdown.target.wants"
+      "timers.target.wants"
+    ];
+
+  makeJobScript = name: text:
+    let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; };
+    in "${x}/bin/${name}";
+
+  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 != "") {
+          Description = config.description;
+        };
+    };
+  };
+
+  serviceConfig = { name, config, ... }: {
+    config = {
+      # Default path for systemd services.  Should be quite minimal.
+      path =
+        [ pkgs.coreutils
+          pkgs.findutils
+          pkgs.gnugrep
+          pkgs.gnused
+          systemd
+        ];
+    };
+  };
+
+  mountConfig = { name, config, ... }: {
+    config = {
+      mountConfig =
+        { What = config.what;
+          Where = config.where;
+        } // optionalAttrs (config.type != "") {
+          Type = config.type;
+        } // optionalAttrs (config.options != "") {
+          Options = config.options;
+        };
+    };
+  };
+
+  automountConfig = { name, config, ... }: {
+    config = {
+      automountConfig =
+        { Where = config.where;
+        };
+    };
+  };
+
+  toOption = x:
+    if x == true then "true"
+    else if x == false then "false"
+    else toString x;
+
+  attrsToSection = as:
+    concatStrings (concatLists (mapAttrsToList (name: value:
+      map (x: ''
+          ${name}=${toOption x}
+        '')
+        (if isList value then value else [value]))
+        as));
+
+  targetToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+        '';
+    };
+
+  serviceToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Service]
+          Environment=PATH=${def.path}
+          Environment=LD_LIBRARY_PATH=
+          ${let env = cfg.globalEnvironment // def.environment;
+            in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
+          ${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"}
+          ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
+
+          ${optionalString (def.preStart != "") ''
+            ExecStartPre=${makeJobScript "${name}-pre-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.preStart}
+            ''}
+          ''}
+
+          ${optionalString (def.script != "") ''
+            ExecStart=${makeJobScript "${name}-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.script}
+            ''} ${def.scriptArgs}
+          ''}
+
+          ${optionalString (def.postStart != "") ''
+            ExecStartPost=${makeJobScript "${name}-post-start" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.postStart}
+            ''}
+          ''}
+
+          ${optionalString (def.postStop != "") ''
+            ExecStopPost=${makeJobScript "${name}-post-stop" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.postStop}
+            ''}
+          ''}
+
+          ${attrsToSection def.serviceConfig}
+        '';
+    };
+
+  socketToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Socket]
+          ${attrsToSection def.socketConfig}
+          ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
+        '';
+    };
+
+  timerToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Timer]
+          ${attrsToSection def.timerConfig}
+        '';
+    };
+
+  mountToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Mount]
+          ${attrsToSection def.mountConfig}
+        '';
+    };
+
+  automountToUnit = name: def:
+    { inherit (def) wantedBy requiredBy enable;
+      text =
+        ''
+          [Unit]
+          ${attrsToSection def.unitConfig}
+
+          [Automount]
+          ${attrsToSection def.automountConfig}
+        '';
+    };
+
+  nixosUnits = mapAttrsToList makeUnit cfg.units;
+
+  units = pkgs.runCommand "units" { preferLocalBuild = true; }
+    ''
+      mkdir -p $out
+      for i in ${toString upstreamUnits}; do
+        fn=${systemd}/example/systemd/system/$i
+        if ! [ -e $fn ]; then echo "missing $fn"; false; fi
+        if [ -L $fn ]; then
+          cp -pd $fn $out/
+        else
+          ln -s $fn $out/
+        fi
+      done
+
+      for i in ${toString upstreamWants}; do
+        fn=${systemd}/example/systemd/system/$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
+        done
+      done
+
+      for i in ${toString nixosUnits}; do
+        ln -s $i/* $out/
+      done
+
+      for i in ${toString cfg.packages}; do
+        ln -s $i/etc/systemd/system/* $out/
+      done
+
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            mkdir -p $out/${name2}.wants
+            ln -sfn ../${name} $out/${name2}.wants/
+          '') unit.wantedBy) cfg.units)}
+
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            mkdir -p $out/${name2}.requires
+            ln -sfn ../${name} $out/${name2}.requires/
+          '') unit.requiredBy) cfg.units)}
+
+      ln -s ${cfg.defaultUnit} $out/default.target
+
+      ln -s rescue.target $out/kbrequest.target
+
+      mkdir -p $out/getty.target.wants/
+      ln -s ../getty@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/
+    ''; # */
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    systemd.package = mkOption {
+      default = pkgs.systemd;
+      type = types.package;
+      description = "The systemd package.";
+    };
+
+    systemd.units = mkOption {
+      description = "Definition of systemd units.";
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = {
+        text = mkOption {
+          types = types.uniq types.string;
+          description = "Text of this systemd unit.";
+        };
+        enable = mkOption {
+          default = true;
+          types = 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 = [];
+          types = types.listOf types.string;
+          description = "Units that require (i.e. depend on and need to go down with) this unit.";
+        };
+        wantedBy = mkOption {
+          default = [];
+          types = types.listOf types.string;
+          description = "Units that want (i.e. depend on) this unit.";
+        };
+      };
+    };
+
+    systemd.packages = mkOption {
+      default = [];
+      type = types.listOf types.package;
+      description = "Packages providing systemd units.";
+    };
+
+    systemd.targets = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ unitOptions unitConfig ];
+      description = "Definition of systemd target units.";
+    };
+
+    systemd.services = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ serviceOptions unitConfig serviceConfig ];
+      description = "Definition of systemd service units.";
+    };
+
+    systemd.sockets = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ socketOptions unitConfig ];
+      description = "Definition of systemd socket units.";
+    };
+
+    systemd.timers = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ timerOptions unitConfig ];
+      description = "Definition of systemd timer units.";
+    };
+
+    systemd.mounts = mkOption {
+      default = [];
+      type = types.listOf types.optionSet;
+      options = [ mountOptions unitConfig mountConfig ];
+      description = ''
+        Definition of systemd mount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    systemd.automounts = mkOption {
+      default = [];
+      type = types.listOf types.optionSet;
+      options = [ automountOptions unitConfig automountConfig ];
+      description = ''
+        Definition of systemd automount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    systemd.defaultUnit = mkOption {
+      default = "multi-user.target";
+      type = types.uniq types.string;
+      description = "Default unit started when the system boots.";
+    };
+
+    systemd.globalEnvironment = mkOption {
+      type = types.attrs;
+      default = {};
+      example = { TZ = "CET"; };
+      description = ''
+        Environment variables passed to <emphasis>all</emphasis> systemd units.
+      '';
+    };
+
+    services.journald.console = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      description = "If non-empty, write log messages to the specified TTY device.";
+    };
+
+    services.journald.rateLimitInterval = mkOption {
+      default = "10s";
+      type = types.uniq types.string;
+      description = ''
+        Configures the rate limiting interval that is applied to all
+        messages generated on the system. This rate limiting is applied
+        per-service, so that two services which log do not interfere with
+        each other's limit. The value may be specified in the following
+        units: s, min, h, ms, us. To turn off any kind of rate limiting,
+        set either value to 0.
+      '';
+    };
+
+    services.journald.rateLimitBurst = mkOption {
+      default = 100;
+      type = types.uniq types.int;
+      description = ''
+        Configures the rate limiting burst limit (number of messages per
+        interval) that is applied to all messages generated on the system.
+        This rate limiting is applied per-service, so that two services
+        which log do not interfere with each other's limit.
+      '';
+    };
+
+    services.logind.extraConfig = mkOption {
+      default = "";
+      type = types.uniq types.string;
+      example = "HandleLidSwitch=ignore";
+      description = ''
+        Extra config options for systemd-logind. See man logind.conf for
+        available options.
+      '';
+    };
+
+    systemd.enableEmergencyMode = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to enable emergency mode, which is an
+        <command>sulogin</command> shell started on the console if
+        mounting a filesystem fails.  Since some machines (like EC2
+        instances) have no console of any kind, emergency mode doesn't
+        make sense, and it's better to continue with the boot insofar
+        as possible.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build.units = units;
+
+    environment.systemPackages = [ systemd ];
+
+    environment.etc."systemd/system".source = units;
+
+    environment.etc."systemd/system.conf".text =
+      ''
+        [Manager]
+      '';
+
+    environment.etc."systemd/journald.conf".text =
+      ''
+        [Journal]
+        RateLimitInterval=${config.services.journald.rateLimitInterval}
+        RateLimitBurst=${toString config.services.journald.rateLimitBurst}
+        ${optionalString (config.services.journald.console != "") ''
+          ForwardToConsole=yes
+          TTYPath=${config.services.journald.console}
+        ''}
+      '';
+
+    environment.etc."systemd/logind.conf".text =
+      ''
+        [Login]
+        ${config.services.logind.extraConfig}
+      '';
+
+    environment.etc."systemd/sleep.conf".text =
+      ''
+        [Sleep]
+      '';
+
+    system.activationScripts.systemd = stringAfter [ "groups" ]
+      ''
+        mkdir -m 0755 -p /var/lib/udev
+        mkdir -p /var/log/journal
+        chmod 0755 /var/log/journal
+
+        # Regenerate the hardware database /var/lib/udev/hwdb.bin
+        # whenever systemd changes.
+        if [ ! -e /var/lib/udev/prev-systemd -o "$(readlink /var/lib/udev/prev-systemd)" != ${systemd} ]; then
+          echo "regenerating udev hardware database..."
+          ${systemd}/bin/udevadm hwdb --update && ln -sfn ${systemd} /var/lib/udev/prev-systemd
+        fi
+
+        # Make all journals readable to users in the wheel and adm
+        # groups, in addition to those in the systemd-journal group.
+        # Users can always read their own journals.
+        ${pkgs.acl}/bin/setfacl -nm g:wheel:rx,d:g:wheel:rx,g:adm:rx,d:g:adm:rx /var/log/journal
+      '';
+
+    # Target for ‘charon send-keys’ to hook into.
+    systemd.targets.keys =
+      { description = "Security Keys";
+      };
+
+    systemd.units =
+      mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
+      // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+      // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
+      // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
+      // listToAttrs (map
+                   (v: let n = escapeSystemdPath v.where;
+                       in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
+      // listToAttrs (map
+                   (v: let n = escapeSystemdPath v.where;
+                       in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
+
+    system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [
+      "CGROUPS" "AUTOFS4_FS" "DEVTMPFS"
+    ];
+
+    environment.shellAliases =
+      { start = "systemctl start";
+        stop = "systemctl stop";
+        restart = "systemctl restart";
+        status = "systemctl status";
+      };
+
+    users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
+
+    # Generate timer units for all services that have a ‘startAt’ value.
+    systemd.timers =
+      mapAttrs (name: service:
+        { wantedBy = [ "timers.target" ];
+          timerConfig.OnCalendar = service.startAt;
+        })
+        (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.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";
+        };
+      };
+
+  };
+}
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
new file mode 100644
index 00000000000..91fcdcf2435
--- /dev/null
+++ b/nixos/modules/system/etc/etc.nix
@@ -0,0 +1,117 @@
+# Management of static files in /etc.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  etc' = filter (f: f.enable) (attrValues config.environment.etc);
+
+  etc = pkgs.stdenv.mkDerivation {
+    name = "etc";
+
+    builder = ./make-etc.sh;
+
+    preferLocalBuild = true;
+
+    /* !!! Use toXML. */
+    sources = map (x: x.source) etc';
+    targets = map (x: x.target) etc';
+    modes = map (x: x.mode) etc';
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    environment.etc = mkOption {
+      type = types.loaOf types.optionSet;
+      default = {};
+      example =
+        { hosts =
+            { source = "/nix/store/.../etc/dir/file.conf.example";
+              mode = "0440";
+            };
+          "default/useradd".text = "GROUP=100 ...";
+        };
+      description = ''
+        Set of files that have to be linked in <filename>/etc</filename>.
+      '';
+
+      options = singleton ({ name, config, ... }:
+        { options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether this /etc file should be generated.  This
+                option allows specific /etc files to be disabled.
+              '';
+            };
+
+            target = mkOption {
+              description = ''
+                Name of symlink (relative to
+                <filename>/etc</filename>).  Defaults to the attribute
+                name.
+              '';
+            };
+
+            text = mkOption {
+              default = null;
+              type = types.nullOr types.string;
+              description = "Text of the file.";
+            };
+
+            source = mkOption {
+              types = types.path;
+              description = "Path of the source file.";
+            };
+
+            mode = mkOption {
+              default = "symlink";
+              example = "0600";
+              description = ''
+                If set to something else than <literal>symlink</literal>,
+                the file is copied instead of symlinked, with the given
+                file mode.
+              '';
+            };
+
+          };
+
+          config = {
+            target = mkDefault name;
+            source = mkIf (config.text != null)
+              (mkDefault (pkgs.writeText "etc-file" config.text));
+          };
+
+        });
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    system.build.etc = etc;
+
+    system.activationScripts.etc = stringAfter [ "stdio" ]
+      ''
+        # Set up the statically computed bits of /etc.
+        echo "setting up /etc..."
+        ${pkgs.perl}/bin/perl ${./setup-etc.pl} ${etc}/etc
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh
new file mode 100644
index 00000000000..7cf68db9ddc
--- /dev/null
+++ b/nixos/modules/system/etc/make-etc.sh
@@ -0,0 +1,42 @@
+source $stdenv/setup
+
+mkdir -p $out/etc
+
+set -f
+sources_=($sources)
+targets_=($targets)
+modes_=($modes)
+set +f
+
+for ((i = 0; i < ${#targets_[@]}; i++)); do
+    source="${sources_[$i]}"
+    target="${targets_[$i]}"
+
+    if [[ "$source" =~ '*' ]]; then
+
+        # If the source name contains '*', perform globbing.
+        mkdir -p $out/etc/$target
+        for fn in $source; do
+            ln -s "$fn" $out/etc/$target/
+        done
+
+    else
+        
+        mkdir -p $out/etc/$(dirname $target)
+        if ! [ -e $out/etc/$target ]; then
+            ln -s $source $out/etc/$target
+        else
+            echo "duplicate entry $target -> $source"
+            if test "$(readlink $out/etc/$target)" != "$source"; then
+                echo "mismatched duplicate entry $(readlink $out/etc/$target) <-> $source"
+                exit 1
+            fi
+        fi
+        
+        if test "${modes_[$i]}" != symlink; then
+            echo "${modes_[$i]}" > $out/etc/$target.mode
+        fi
+        
+    fi
+done
+
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
new file mode 100644
index 00000000000..7cb6d2a6a45
--- /dev/null
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -0,0 +1,68 @@
+use strict;
+use File::Find;
+use File::Copy;
+use File::Path;
+use File::Basename;
+
+my $etc = $ARGV[0] or die;
+my $static = "/etc/static";
+
+sub atomicSymlink {
+    my ($source, $target) = @_;
+    my $tmp = "$target.tmp";
+    unlink $tmp;
+    symlink $source, $tmp or return 1;
+    rename $tmp, $target or return 1;
+    return 1;
+}
+
+
+# Atomically update /etc/static to point at the etc files of the
+# current configuration.
+atomicSymlink $etc, $static or die;
+
+
+# Remove dangling symlinks that point to /etc/static.  These are
+# configuration files that existed in a previous configuration but not
+# in the current one.  For efficiency, don't look under /etc/nixos
+# (where all the NixOS sources live).
+sub cleanup {
+    if ($File::Find::name eq "/etc/nixos") {
+        $File::Find::prune = 1;
+        return;
+    }
+    if (-l $_) {
+        my $target = readlink $_;
+        if (substr($target, 0, length $static) eq $static) {
+            my $x = "/etc/static/" . substr($File::Find::name, length "/etc/");
+            unless (-l $x) {
+                print STDERR "removing obsolete symlink ‘$File::Find::name’...\n";
+                unlink "$_";
+            }
+        }
+    }
+}
+
+find(\&cleanup, "/etc");
+
+
+# For every file in the etc tree, create a corresponding symlink in
+# /etc to /etc/static.  The indirection through /etc/static is to make
+# switching to a new configuration somewhat more atomic.
+sub link {
+    my $fn = substr $File::Find::name, length($etc) + 1 or next;
+    my $target = "/etc/$fn";
+    File::Path::make_path(dirname $target);
+    if (-e "$_.mode") {
+        open MODE, "<$_.mode";
+        my $mode = <MODE>; chomp $mode;
+        close MODE;
+        copy "$static/$fn", "$target.tmp" or warn;
+        chmod oct($mode), "$target.tmp" or warn;
+        rename "$target.tmp", $target or warn;
+    } elsif (-l "$_") {
+        atomicSymlink "$static/$fn", $target or warn;
+    }
+}
+
+find(\&link, $etc);
diff --git a/nixos/modules/system/upstart/upstart.nix b/nixos/modules/system/upstart/upstart.nix
new file mode 100644
index 00000000000..5d5139b7a57
--- /dev/null
+++ b/nixos/modules/system/upstart/upstart.nix
@@ -0,0 +1,286 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+with import ../boot/systemd-unit-options.nix { inherit config pkgs; };
+
+let
+
+  userExists = u:
+    (u == "") || any (uu: uu.name == u) (attrValues config.users.extraUsers);
+
+  groupExists = g:
+    (g == "") || any (gg: gg.name == g) (attrValues config.users.extraGroups);
+
+  makeJobScript = name: content: "${pkgs.writeScriptBin name content}/bin/${name}";
+
+  # From a job description, generate an systemd unit file.
+  makeUnit = job:
+
+    let
+      hasMain = job.script != "" || job.exec != "";
+
+      env = job.environment;
+
+      preStartScript = makeJobScript "${job.name}-pre-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStart}
+        '';
+
+      startScript = makeJobScript "${job.name}-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${if job.script != "" then job.script else ''
+            exec ${job.exec}
+          ''}
+        '';
+
+      postStartScript = makeJobScript "${job.name}-post-start"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStart}
+        '';
+
+      preStopScript = makeJobScript "${job.name}-pre-stop"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStop}
+        '';
+
+      postStopScript = makeJobScript "${job.name}-post-stop"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStop}
+        '';
+    in {
+
+      inherit (job) description requires before partOf environment path restartIfChanged unitConfig;
+
+      after =
+        (if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else
+         if job.startOn == "started udev" then [ "systemd-udev.service" ] else
+         if job.startOn == "started network-interfaces" then [ "network-interfaces.target" ] else
+         if job.startOn == "started networking" then [ "network.target" ] else
+         if job.startOn == "ip-up" then [] else
+         if job.startOn == "" || job.startOn == "startup" then [] else
+         builtins.trace "Warning: job ‘${job.name}’ has unknown startOn value ‘${job.startOn}’." []
+        ) ++ job.after;
+
+      wants = 
+        (if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else []
+        ) ++ job.wants;
+
+      wantedBy =
+        (if job.startOn == "" then [] else
+         if job.startOn == "ip-up" then [ "ip-up.target" ] else
+         [ "multi-user.target" ]) ++ job.wantedBy;
+
+      serviceConfig =
+        job.serviceConfig
+        // optionalAttrs (job.preStart != "" && (job.script != "" || job.exec != ""))
+          { ExecStartPre = preStartScript; }
+        // optionalAttrs (job.preStart != "" && job.script == "" && job.exec == "")
+          { ExecStart = preStartScript; }
+        // optionalAttrs (job.script != "" || job.exec != "")
+          { ExecStart = startScript; }
+        // optionalAttrs (job.postStart != "")
+          { ExecStartPost = postStartScript; }
+        // optionalAttrs (job.preStop != "")
+          { ExecStop = preStopScript; }
+        // optionalAttrs (job.postStop != "")
+          { ExecStopPost = postStopScript; }
+        // (if job.script == "" && job.exec == "" then { Type = "oneshot"; RemainAfterExit = true; } else
+            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)
+          { Restart = "always"; }
+        // optionalAttrs job.task
+          { Type = "oneshot"; RemainAfterExit = false; };
+    };
+
+
+  jobOptions = serviceOptions // {
+
+    name = mkOption {
+      # !!! The type should ensure that this could be a filename.
+      type = types.string;
+      example = "sshd";
+      description = ''
+        Name of the Upstart job.
+      '';
+    };
+
+    startOn = mkOption {
+      # !!! Re-enable this once we're on Upstart >= 0.6.
+      #type = types.string;
+      default = "";
+      description = ''
+        The Upstart event that triggers this job to be started.
+        If empty, the job will not start automatically.
+      '';
+    };
+
+    stopOn = mkOption {
+      type = types.string;
+      default = "starting shutdown";
+      description = ''
+        The Upstart event that triggers this job to be stopped.
+      '';
+    };
+
+    postStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the job is started (i.e. after
+        the job's main process is started), but before the job is
+        considered “running”.
+      '';
+    };
+
+    preStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed before the job is stopped
+        (i.e. before Upstart kills the job's main process).  This can
+        be used to cleanly shut down a daemon.
+      '';
+    };
+
+    postStop = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed after the job has stopped
+        (i.e. after the job's main process has terminated).
+      '';
+    };
+
+    exec = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Command to start the job's main process.  If empty, the
+        job has no main process, but can still have pre/post-start
+        and pre/post-stop scripts, and is considered “running”
+        until it is stopped.
+      '';
+    };
+
+    respawn = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to restart the job automatically if its process
+        ends unexpectedly.
+      '';
+    };
+
+    task = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether this job is a task rather than a service.  Tasks
+        are executed only once, while services are restarted when
+        they exit.
+      '';
+    };
+
+    daemonType = mkOption {
+      type = types.string;
+      default = "none";
+      description = ''
+        Determines how Upstart detects when a daemon should be
+        considered “running”.  The value <literal>none</literal> means
+        that the daemon is considered ready immediately.  The value
+        <literal>fork</literal> means that the daemon will fork once.
+        The value <literal>daemon</literal> means that the daemon will
+        fork twice.  The value <literal>stop</literal> means that the
+        daemon will raise the SIGSTOP signal to indicate readiness.
+      '';
+    };
+
+    setuid = mkOption {
+      type = types.string;
+      check = userExists;
+      default = "";
+      description = ''
+        Run the daemon as a different user.
+      '';
+    };
+
+    setgid = mkOption {
+      type = types.string;
+      check = groupExists;
+      default = "";
+      description = ''
+        Run the daemon as a different group.
+      '';
+    };
+
+    path = mkOption {
+      default = [];
+      description = ''
+        Packages added to the job's <envar>PATH</envar> environment variable.
+        Both the <filename>bin</filename> and <filename>sbin</filename>
+        subdirectories of each package are added.
+      '';
+    };
+
+  };
+
+
+  upstartJob = { name, config, ... }: {
+
+    options = {
+
+      unit = mkOption {
+        default = makeUnit config;
+        description = "Generated definition of the systemd unit corresponding to this job.";
+      };
+
+    };
+
+    config = {
+
+      # The default name is the name extracted from the attribute path.
+      name = mkDefaultValue name;
+
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    jobs = mkOption {
+      default = {};
+      description = ''
+        This option defines the system jobs started and managed by the
+        Upstart daemon.
+      '';
+      type = types.loaOf types.optionSet;
+      options = [ jobOptions upstartJob ];
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    systemd.services =
+      flip mapAttrs' config.jobs (name: job:
+        nameValuePair job.name job.unit);
+
+  };
+
+}
diff --git a/nixos/modules/tasks/cpu-freq.nix b/nixos/modules/tasks/cpu-freq.nix
new file mode 100644
index 00000000000..3b21d831087
--- /dev/null
+++ b/nixos/modules/tasks/cpu-freq.nix
@@ -0,0 +1,51 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  ###### interface
+
+  options = {
+
+    powerManagement.cpuFreqGovernor = mkOption {
+      default = "";
+      example = "ondemand";
+      type = types.uniq types.string;
+      description = ''
+        Configure the governor used to regulate the frequence of the
+        available CPUs. By default, the kernel configures the
+        on-demand governor.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (config.powerManagement.cpuFreqGovernor != "") {
+
+    environment.systemPackages = [ pkgs.cpufrequtils ];
+
+    jobs.cpufreq =
+      { description = "CPU Frequency Governor Setup";
+
+        after = [ "systemd-modules-load.service" ];
+        wantedBy = [ "multi-user.target" ];
+
+        path = [ pkgs.cpufrequtils ];
+
+        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
+        '';
+      };
+  };
+
+}
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
new file mode 100644
index 00000000000..4ca309f5a10
--- /dev/null
+++ b/nixos/modules/tasks/filesystems.nix
@@ -0,0 +1,216 @@
+{ config, pkgs, utils, ... }:
+
+with pkgs.lib;
+with utils;
+
+let
+
+  fileSystems = attrValues config.fileSystems;
+
+  prioOption = prio: optionalString (prio !=null) " pri=${toString prio}";
+
+  fileSystemOpts = { name, ... }: {
+
+    options = {
+
+      mountPoint = mkOption {
+        example = "/mnt/usb";
+        type = types.uniq types.string;
+        description = "Location of the mounted the file system.";
+      };
+
+      device = mkOption {
+        default = null;
+        example = "/dev/sda";
+        type = types.uniq (types.nullOr types.string);
+        description = "Location of the device.";
+      };
+
+      label = mkOption {
+        default = null;
+        example = "root-partition";
+        type = types.uniq (types.nullOr types.string);
+        description = "Label of the device (if any).";
+      };
+
+      fsType = mkOption {
+        default = "auto";
+        example = "ext3";
+        type = types.uniq types.string;
+        description = "Type of the file system.";
+      };
+
+      options = mkOption {
+        default = "defaults,relatime";
+        example = "data=journal";
+        type = types.string;
+        merge = pkgs.lib.concatStringsSep ",";
+        description = "Options used to mount the file system.";
+      };
+
+      autoFormat = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          If the device does not currently contain a filesystem (as
+          determined by <command>blkid</command>, then automatically
+          format it with the filesystem type specified in
+          <option>fsType</option>.  Use with caution.
+        '';
+      };
+
+      noCheck = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Disable running fsck on this filesystem.";
+      };
+
+    };
+
+    config = {
+      mountPoint = mkDefault name;
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    fileSystems = mkOption {
+      example = {
+        "/".device = "/dev/hda1";
+        "/data" = {
+          device = "/dev/hda2";
+          fsType = "ext3";
+          options = "data=journal";
+        };
+        "/bigdisk".label = "bigdisk";
+      };
+      type = types.loaOf types.optionSet;
+      options = [ fileSystemOpts ];
+      description = ''
+        The file systems to be mounted.  It must include an entry for
+        the root directory (<literal>mountPoint = \"/\"</literal>).  Each
+        entry in the list is an attribute set with the following fields:
+        <literal>mountPoint</literal>, <literal>device</literal>,
+        <literal>fsType</literal> (a file system type recognised by
+        <command>mount</command>; defaults to
+        <literal>\"auto\"</literal>), and <literal>options</literal>
+        (the mount options passed to <command>mount</command> using the
+        <option>-o</option> flag; defaults to <literal>\"defaults\"</literal>).
+
+        Instead of specifying <literal>device</literal>, you can also
+        specify a volume label (<literal>label</literal>) for file
+        systems that support it, such as ext2/ext3 (see <command>mke2fs
+        -L</command>).
+      '';
+    };
+
+    system.fsPackages = mkOption {
+      internal = true;
+      default = [ ];
+      description = "Packages supplying file system mounters and checkers.";
+    };
+
+    boot.supportedFilesystems = mkOption {
+      default = [ ];
+      example = [ "btrfs" ];
+      type = types.listOf types.string;
+      description = "Names of supported filesystem types.";
+    };
+
+    boot.initrd.supportedFilesystems = mkOption {
+      default = [ ];
+      example = [ "btrfs" ];
+      type = types.listOf types.string;
+      description = "Names of supported filesystem types in the initial ramdisk.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    boot.supportedFilesystems = map (fs: fs.fsType) fileSystems;
+
+    boot.initrd.supportedFilesystems =
+      map (fs: fs.fsType)
+        (filter (fs: fs.mountPoint == "/" || fs.neededForBoot) fileSystems);
+
+    # Add the mount helpers to the system path so that `mount' can find them.
+    system.fsPackages = [ pkgs.dosfstools ];
+
+    environment.systemPackages =
+      [ pkgs.ntfs3g pkgs.cifs_utils ]
+      ++ config.system.fsPackages;
+
+    environment.etc.fstab.text =
+      ''
+        # This is a generated file.  Do not edit!
+
+        # Filesystems.
+        ${flip concatMapStrings fileSystems (fs:
+            (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}")
+            + " " + fs.mountPoint
+            + " " + fs.fsType
+            + " " + fs.options
+            + " 0"
+            + " " + (if fs.fsType == "none" || fs.device == "none" || fs.fsType == "btrfs" || fs.fsType == "tmpfs" || fs.noCheck then "0" else
+                     if fs.mountPoint == "/" then "1" else "2")
+            + "\n"
+        )}
+
+        # Swap devices.
+        ${flip concatMapStrings config.swapDevices (sw:
+            "${sw.device} none swap${prioOption sw.priority}\n"
+        )}
+      '';
+
+    # Provide a target that pulls in all filesystems.
+    systemd.targets.fs =
+      { description = "All File Systems";
+        wants = [ "local-fs.target" "remote-fs.target" ];
+      };
+
+    # Emit systemd services to format requested filesystems.
+    systemd.services =
+      let
+
+        formatDevice = fs:
+          let
+            mountPoint' = escapeSystemdPath fs.mountPoint;
+            device' = escapeSystemdPath fs.device;
+          in nameValuePair "mkfs-${device'}"
+          { description = "Initialisation of Filesystem ${fs.device}";
+            wantedBy = [ "${mountPoint'}.mount" ];
+            before = [ "${mountPoint'}.mount" "systemd-fsck@${device'}.service" ];
+            requires = [ "${device'}.device" ];
+            after = [ "${device'}.device" ];
+            path = [ pkgs.utillinux ] ++ config.system.fsPackages;
+            script =
+              ''
+                if ! [ -e "${fs.device}" ]; then exit 1; fi
+                # FIXME: this is scary.  The test could be more robust.
+                type=$(blkid -p -s TYPE -o value "${fs.device}" || true)
+                if [ -z "$type" ]; then
+                  echo "creating ${fs.fsType} filesystem on ${fs.device}..."
+                  mkfs.${fs.fsType} "${fs.device}"
+                fi
+              '';
+            unitConfig.RequiresMountsFor = [ "${dirOf fs.device}" ];
+            unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+            serviceConfig.Type = "oneshot";
+          };
+
+      in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems));
+
+  };
+
+}
diff --git a/nixos/modules/tasks/filesystems/btrfs.nix b/nixos/modules/tasks/filesystems/btrfs.nix
new file mode 100644
index 00000000000..d95a32e2e3f
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/btrfs.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inInitrd = any (fs: fs == "btrfs") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+  config = mkIf (any (fs: fs == "btrfs") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.btrfsProgs ];
+
+    boot.initrd.kernelModules = mkIf inInitrd [ "btrfs" "crc32c" ];
+
+    boot.initrd.extraUtilsCommands = mkIf inInitrd
+      ''
+        mkdir -p $out/bin
+        cp -v ${pkgs.btrfsProgs}/bin/btrfs $out/bin
+        ln -sv btrfs $out/bin/btrfsck
+        ln -sv btrfsck $out/bin/fsck.btrfs
+        # !!! Increases uncompressed initrd by 240k
+        cp -pv ${pkgs.zlib}/lib/libz.so* $out/lib
+        cp -pv ${pkgs.lzo}/lib/liblzo2.so* $out/lib
+      '';
+
+    boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
+      ''
+        $out/bin/btrfs --version
+      '';
+
+    boot.initrd.postDeviceCommands = mkIf inInitrd
+      ''
+        btrfs device scan
+      '';
+
+    # !!! This is broken.  There should be a udev rule to do this when
+    # new devices are discovered.
+    jobs.udev.postStart =
+      ''
+        ${pkgs.btrfsProgs}/bin/btrfs device scan
+      '';
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/ext.nix b/nixos/modules/tasks/filesystems/ext.nix
new file mode 100644
index 00000000000..24592e9d588
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/ext.nix
@@ -0,0 +1,22 @@
+{ config, pkgs, ... }:
+
+{
+  config = {
+
+    system.fsPackages = [ pkgs.e2fsprogs ];
+
+    boot.initrd.availableKernelModules = [ "ext2" "ext3" "ext4" ];
+
+    boot.initrd.extraUtilsCommands =
+      ''
+        # Copy e2fsck and friends.
+        cp -v ${pkgs.e2fsprogs}/sbin/e2fsck $out/bin
+        cp -v ${pkgs.e2fsprogs}/sbin/tune2fs $out/bin
+        ln -sv e2fsck $out/bin/fsck.ext2
+        ln -sv e2fsck $out/bin/fsck.ext3
+        ln -sv e2fsck $out/bin/fsck.ext4
+        cp -pdv ${pkgs.e2fsprogs}/lib/lib*.so.* $out/lib
+      '';
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
new file mode 100644
index 00000000000..2b720a93b89
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inInitrd = any (fs: fs == "nfs") config.boot.initrd.supportedFilesystems;
+
+  nfsStateDir = "/var/lib/nfs";
+
+  rpcMountpoint = "${nfsStateDir}/rpc_pipefs";
+
+  idmapdConfFile = pkgs.writeText "idmapd.conf" ''
+    [General]
+    Pipefs-Directory = ${rpcMountpoint}
+    ${optionalString (config.networking.domain != "")
+      "Domain = ${config.networking.domain}"}
+
+    [Mapping]
+    Nobody-User = nobody
+    Nobody-Group = nogroup
+
+    [Translation]
+    Method = nsswitch
+  '';
+
+in
+
+{
+
+  ###### implementation
+
+  config = mkIf (any (fs: fs == "nfs" || fs == "nfs4") config.boot.supportedFilesystems) {
+
+    services.rpcbind.enable = true;
+
+    system.fsPackages = [ pkgs.nfsUtils ];
+
+    boot.kernelModules = [ "sunrpc" ];
+
+    boot.initrd.kernelModules = mkIf inInitrd [ "nfs" ];
+
+    systemd.services.statd =
+      { description = "NFSv3 Network Status Monitor";
+
+        path = [ pkgs.nfsUtils pkgs.sysvtools pkgs.utillinux ];
+
+        wantedBy = [ "network-online.target" "multi-user.target" ];
+        before = [ "network-online.target" ];
+        requires = [ "basic.target" "rpcbind.service" ];
+        after = [ "basic.target" "rpcbind.service" "network.target" ];
+
+        unitConfig.DefaultDependencies = false; # don't stop during shutdown
+
+        preStart =
+          ''
+            mkdir -p ${nfsStateDir}/sm
+            mkdir -p ${nfsStateDir}/sm.bak
+            sm-notify -d
+          '';
+
+        serviceConfig.Type = "forking";
+        serviceConfig.ExecStart = "@${pkgs.nfsUtils}/sbin/rpc.statd rpc.statd --no-notify";
+        serviceConfig.Restart = "always";
+      };
+
+    systemd.services.idmapd =
+      { description = "NFSv4 ID Mapping Daemon";
+
+        path = [ pkgs.sysvtools pkgs.utillinux ];
+
+        wantedBy = [ "network-online.target" "multi-user.target" ];
+        before = [ "network-online.target" ];
+        requires = [ "rpcbind.service" ];
+        after = [ "rpcbind.service" ];
+
+        preStart =
+          ''
+            mkdir -p ${rpcMountpoint}
+            mount -t rpc_pipefs rpc_pipefs ${rpcMountpoint}
+          '';
+
+        postStop =
+          ''
+            umount ${rpcMountpoint}
+          '';
+
+        serviceConfig.Type = "forking";
+        serviceConfig.ExecStart = "@${pkgs.nfsUtils}/sbin/rpc.idmapd rpc.idmapd -c ${idmapdConfFile}";
+        serviceConfig.Restart = "always";
+      };
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/reiserfs.nix b/nixos/modules/tasks/filesystems/reiserfs.nix
new file mode 100644
index 00000000000..f8c6a700004
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/reiserfs.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inInitrd = any (fs: fs == "reiserfs") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+  config = mkIf (any (fs: fs == "reiserfs") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.reiserfsprogs ];
+
+    boot.initrd.kernelModules = mkIf inInitrd [ "reiserfs" ];
+
+    boot.initrd.extraUtilsCommands = mkIf inInitrd
+      ''
+        cp -v ${pkgs.reiserfsprogs}/sbin/reiserfsck $out/bin
+        ln -sv reiserfsck $out/bin/fsck.reiserfs
+      '';
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/unionfs-fuse.nix b/nixos/modules/tasks/filesystems/unionfs-fuse.nix
new file mode 100644
index 00000000000..177c97f85c7
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/unionfs-fuse.nix
@@ -0,0 +1,24 @@
+{ config, pkgs, ... }:
+
+{
+  config = pkgs.lib.mkMerge [
+    (pkgs.lib.mkIf (pkgs.lib.any (fs: fs == "unionfs-fuse") config.boot.initrd.supportedFilesystems) {
+      boot.initrd.kernelModules = [ "fuse" ];
+  
+      boot.initrd.extraUtilsCommands = ''
+        cp -v ${pkgs.fuse}/lib/libfuse* $out/lib
+        cp -v ${pkgs.unionfs-fuse}/bin/unionfs $out/bin
+      '';
+  
+      boot.initrd.postDeviceCommands = ''
+          # Hacky!!! fuse hard-codes the path to mount
+          mkdir -p /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.utillinux.name}/bin
+          ln -s $(which mount) /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.utillinux.name}/bin
+          ln -s $(which umount) /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.utillinux.name}/bin
+        '';
+    })
+    (pkgs.lib.mkIf (pkgs.lib.any (fs: fs == "unionfs-fuse") config.boot.supportedFilesystems) {
+      system.fsPackages = [ pkgs.unionfs-fuse ];
+    })
+  ];
+}
diff --git a/nixos/modules/tasks/filesystems/vfat.nix b/nixos/modules/tasks/filesystems/vfat.nix
new file mode 100644
index 00000000000..5ca72f142b7
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/vfat.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inInitrd = any (fs: fs == "vfat") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+  config = mkIf (any (fs: fs == "vfat") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.dosfstools ];
+
+    boot.initrd.kernelModules = mkIf inInitrd [ "vfat" "nls_cp437" "nls_iso8859-1" ];
+
+    boot.initrd.extraUtilsCommands = mkIf inInitrd
+      ''
+        cp -v ${pkgs.dosfstools}/sbin/dosfsck $out/bin
+        ln -sv dosfsck $out/bin/fsck.vfat
+      '';
+
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/xfs.nix b/nixos/modules/tasks/filesystems/xfs.nix
new file mode 100644
index 00000000000..5f9eb741c2a
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/xfs.nix
@@ -0,0 +1,29 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  inInitrd = any (fs: fs == "xfs") config.boot.initrd.supportedFilesystems;
+
+in
+
+{
+  config = mkIf (any (fs: fs == "xfs") config.boot.supportedFilesystems) {
+
+    system.fsPackages = [ pkgs.xfsprogs ];
+
+    boot.initrd.kernelModules = mkIf inInitrd [ "xfs" "crc32c" ];
+
+    boot.initrd.extraUtilsCommands = mkIf inInitrd
+      ''
+        cp -v ${pkgs.xfsprogs}/sbin/fsck.xfs $out/bin
+      '';
+
+    # Trick just to set 'sh' after the extraUtils nuke-refs.
+    boot.initrd.extraUtilsCommandsTest = mkIf inInitrd
+      ''
+        sed -i -e 's,^#!.*,#!'$out/bin/sh, $out/bin/fsck.xfs
+      '';
+  };
+}
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
new file mode 100644
index 00000000000..c1955b14691
--- /dev/null
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+#
+# todo:
+#   - crontab for scrubs, etc
+#   - zfs tunables
+#   - /etc/zfs/zpool.cache handling
+
+
+with pkgs.lib;
+
+let
+
+  cfgSpl = config.boot.spl;
+  inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
+  inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
+  kernel = config.boot.kernelPackages;
+
+in
+
+{
+
+  ###### interface
+  
+  options = { 
+    boot.spl.hostid = mkOption { 
+      default = "";
+      example = "0xdeadbeef";
+      description = ''
+        ZFS uses a system's hostid to determine if a storage pool (zpool) is
+        native to this system, and should thus be imported automatically.
+        Unfortunately, this hostid can change under linux from boot to boot (by
+        changing network adapters, for instance). Specify a unique 32 bit hostid in
+        hex here for zfs to prevent getting a random hostid between boots and having to
+        manually import pools.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf ( inInitrd || inSystem ) {
+
+    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
+        '';
+      postDeviceCommands =
+        ''
+          zpool import -f -a -d /dev
+          zfs mount -a
+        '';
+    };
+
+    systemd.services."zpool-import" = {
+      description = "Import zpools";
+      after = [ "systemd-udev-settle.service" ];
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        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;
+        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. 
+  };
+}
diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix
new file mode 100644
index 00000000000..9f294a5f93e
--- /dev/null
+++ b/nixos/modules/tasks/kbd.nix
@@ -0,0 +1,73 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  vconsoleConf = pkgs.writeText "vconsole.conf"
+    ''
+      KEYMAP=${config.i18n.consoleKeyMap}
+      FONT=${config.i18n.consoleFont}
+    '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    # most options are defined in i18n.nix
+
+    # FIXME: still needed?
+    boot.extraTTYs = mkOption {
+      default = [];
+      example = ["tty8" "tty9"];
+      description = ''
+        Tty (virtual console) devices, in addition to the consoles on
+        which mingetty and syslogd run, that must be initialised.
+        Only useful if you have some program that you want to run on
+        some fixed console.  For example, the NixOS installation CD
+        opens the manual in a web browser on console 7, so it sets
+        <option>boot.extraTTYs</option> to <literal>["tty7"]</literal>.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ pkgs.kbd ];
+
+    # Let systemd-vconsole-setup.service do the work of setting up the
+    # virtual consoles.  FIXME: trigger a restart of
+    # systemd-vconsole-setup.service if /etc/vconsole.conf changes.
+    environment.etc."vconsole.conf".source = vconsoleConf;
+
+    # This is identical to the systemd-vconsole-setup.service unit
+    # shipped with systemd, except that it uses /dev/tty1 instead of
+    # /dev/tty0 to prevent putting the X server in non-raw mode, and
+    # it has a restart trigger.
+    systemd.services."systemd-vconsole-setup" =
+      { description = "Setup Virtual Console";
+        wantedBy = [ "sysinit.target" "multi-user.target" ];
+        before = [ "sysinit.target" "shutdown.target" ];
+        unitConfig =
+          { DefaultDependencies = "no";
+            Conflicts = "shutdown.target";
+            ConditionPathExists = "/dev/tty1";
+          };
+        serviceConfig =
+          { Type = "oneshot";
+            RemainAfterExit = true;
+            ExecStart = "${config.systemd.package}/lib/systemd/systemd-vconsole-setup /dev/tty1";
+          };
+        restartTriggers = [ vconsoleConf ];
+      };
+
+  };
+
+}
diff --git a/nixos/modules/tasks/lvm.nix b/nixos/modules/tasks/lvm.nix
new file mode 100644
index 00000000000..0e0272388c7
--- /dev/null
+++ b/nixos/modules/tasks/lvm.nix
@@ -0,0 +1,15 @@
+{ config, pkgs, ... }:
+
+{
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages = [ pkgs.lvm2 ];
+
+    services.udev.packages = [ pkgs.lvm2 ];
+
+  };
+
+}
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
new file mode 100644
index 00000000000..0177d6396df
--- /dev/null
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -0,0 +1,447 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.networking;
+  interfaces = attrValues cfg.interfaces;
+  hasVirtuals = any (i: i.virtual) interfaces;
+
+  interfaceOpts = { name, ... }: {
+
+    options = {
+
+      name = mkOption {
+        example = "eth0";
+        type = types.string;
+        description = "Name of the interface.";
+      };
+
+      ipAddress = mkOption {
+        default = null;
+        example = "10.0.0.1";
+        type = types.nullOr types.string;
+        description = ''
+          IP address of the interface.  Leave empty to configure the
+          interface using DHCP.
+        '';
+      };
+
+      prefixLength = mkOption {
+        default = null;
+        example = 24;
+        type = types.nullOr types.int;
+        description = ''
+          Subnet mask of the interface, specified as the number of
+          bits in the prefix (<literal>24</literal>).
+        '';
+      };
+
+      subnetMask = mkOption {
+        default = "";
+        example = "255.255.255.0";
+        type = types.string;
+        description = ''
+          Subnet mask of the interface, specified as a bitmask.
+          This is deprecated; use <option>prefixLength</option>
+          instead.
+        '';
+      };
+
+      macAddress = mkOption {
+        default = null;
+        example = "00:11:22:33:44:55";
+        type = types.nullOr types.string;
+        description = ''
+          MAC address of the interface. Leave empty to use the default.
+        '';
+      };
+
+      virtual = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether this interface is virtual and should be created by tunctl.
+          This is mainly useful for creating bridges between a host a virtual
+          network such as VPN or a virtual machine.
+
+          Defaults to tap device, unless interface contains "tun" in its name.
+        '';
+      };
+
+      virtualOwner = mkOption {
+        default = "root";
+        type = types.uniq types.string;
+        description = ''
+          In case of a virtual device, the user who owns it.
+        '';
+      };
+
+      proxyARP = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Turn on proxy_arp for this device (and proxy_ndp for ipv6).
+          This is mainly useful for creating pseudo-bridges between a real
+          interface and a virtual network such as VPN or a virtual machine for
+          interfaces that don't support real bridging (most wlan interfaces).
+          As ARP proxying acts slightly above the link-layer, below-ip traffic
+          isn't bridged, so things like DHCP won't work. The advantage above
+          using NAT lies in the fact that no IP addresses are shared, so all
+          hosts are reachable/routeable.
+
+          WARNING: turns on ip-routing, so if you have multiple interfaces, you
+          should think of the consequence and setup firewall rules to limit this.
+        '';
+      };
+
+    };
+
+    config = {
+      name = mkDefault name;
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.hostName = mkOption {
+      default = "nixos";
+      description = ''
+        The name of the machine.  Leave it empty if you want to obtain
+        it from a DHCP server (if using DHCP).
+      '';
+    };
+
+    networking.enableIPv6 = mkOption {
+      default = true;
+      description = ''
+        Whether to enable support for IPv6.
+      '';
+    };
+
+    networking.defaultGateway = mkOption {
+      default = "";
+      example = "131.211.84.1";
+      description = ''
+        The default gateway.  It can be left empty if it is auto-detected through DHCP.
+      '';
+    };
+
+    networking.defaultGatewayWindowSize = mkOption {
+      default = null;
+      example = 524288;
+      type = types.nullOr types.int;
+      description = ''
+        The window size of the default gateway. It limits maximal data bursts that TCP peers
+        are allowed to send to us.
+      '';
+    };
+
+    networking.nameservers = mkOption {
+      default = [];
+      example = ["130.161.158.4" "130.161.33.17"];
+      description = ''
+        The list of nameservers.  It can be left empty if it is auto-detected through DHCP.
+      '';
+    };
+
+    networking.domain = mkOption {
+      default = "";
+      example = "home";
+      description = ''
+        The domain.  It can be left empty if it is auto-detected through DHCP.
+      '';
+    };
+
+    networking.localCommands = mkOption {
+      default = "";
+      example = "text=anything; echo You can put $text here.";
+      description = ''
+        Shell commands to be executed at the end of the
+        <literal>network-setup</literal> systemd service.  Note that if
+        you are using DHCP to obtain the network configuration,
+        interfaces may not be fully configured yet.
+      '';
+    };
+
+    networking.interfaces = mkOption {
+      default = {};
+      example =
+        { eth0 = {
+            ipAddress = "131.211.84.78";
+            subnetMask = "255.255.255.128";
+          };
+        };
+      description = ''
+        The configuration for each network interface.  If
+        <option>networking.useDHCP</option> is true, then every
+        interface not listed here will be configured using DHCP.
+      '';
+      type = types.loaOf types.optionSet;
+      options = [ interfaceOpts ];
+    };
+
+    networking.bridges = mkOption {
+      default = { };
+      example =
+        { br0.interfaces = [ "eth0" "eth1" ];
+          br1.interfaces = [ "eth2" "wlan0" ];
+        };
+      description =
+        ''
+          This option allows you to define Ethernet bridge devices
+          that connect physical networks together.  The value of this
+          option is an attribute set.  Each attribute specifies a
+          bridge, with the attribute name specifying the name of the
+          bridge's network interface.
+        '';
+
+      type = types.attrsOf types.optionSet;
+
+      options = {
+
+        interfaces = mkOption {
+          example = [ "eth0" "eth1" ];
+          type = types.listOf types.string;
+          description =
+            "The physical network interfaces connected by the bridge.";
+        };
+
+      };
+
+    };
+
+    networking.useDHCP = mkOption {
+      default = true;
+      merge = mergeEnableOption;
+      description = ''
+        Whether to use DHCP to obtain an IP address and other
+        configuration for all network interfaces that are not manually
+        configured.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    boot.kernelModules = optional cfg.enableIPv6 "ipv6" ++ optional hasVirtuals "tun";
+
+    environment.systemPackages =
+      [ pkgs.host
+        pkgs.iproute
+        pkgs.iputils
+        pkgs.nettools
+        pkgs.wirelesstools
+        pkgs.iw
+        pkgs.rfkill
+        pkgs.openresolv
+      ]
+      ++ optional (cfg.bridges != {}) pkgs.bridge_utils
+      ++ optional hasVirtuals pkgs.tunctl
+      ++ optional cfg.enableIPv6 pkgs.ndisc6;
+
+    security.setuidPrograms = [ "ping" "ping6" ];
+
+    systemd.targets."network-interfaces" =
+      { description = "All Network Interfaces";
+        wantedBy = [ "network.target" ];
+        unitConfig.X-StopOnReconfiguration = true;
+      };
+
+    systemd.services =
+      let
+
+        networkSetup =
+          { description = "Networking Setup";
+
+            after = [ "network-interfaces.target" ];
+            before = [ "network.target" ];
+            wantedBy = [ "network.target" ];
+
+            path = [ pkgs.iproute ];
+
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+
+            script =
+              ''
+                # Set the static DNS configuration, if given.
+                ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
+                ${optionalString (cfg.nameservers != [] && cfg.domain != "") ''
+                  domain ${cfg.domain}
+                ''}
+                ${flip concatMapStrings cfg.nameservers (ns: ''
+                  nameserver ${ns}
+                '')}
+                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
+
+                # Set the default gateway.
+                ${optionalString (cfg.defaultGateway != "") ''
+                  # FIXME: get rid of "|| true" (necessary to make it idempotent).
+                  ip route add default via "${cfg.defaultGateway}" ${
+                    optionalString (cfg.defaultGatewayWindowSize != null)
+                      "window ${cfg.defaultGatewayWindowSize}"} || true
+                ''}
+
+                # Turn on forwarding if any interface has enabled proxy_arp.
+                ${optionalString (any (i: i.proxyARP) interfaces) ''
+                  echo 1 > /proc/sys/net/ipv4/ip_forward
+                ''}
+
+                # Run any user-specified commands.
+                ${cfg.localCommands}
+              '';
+          };
+
+        # For each interface <foo>, create a job ‘<foo>-cfg.service"
+        # that performs static configuration.  It has a "wants"
+        # dependency on ‘<foo>.service’, which is supposed to create
+        # the interface and need not exist (i.e. for hardware
+        # interfaces).  It has a binds-to dependency on the actual
+        # network device, so it only gets started after the interface
+        # has appeared, and it's stopped when the interface
+        # disappears.
+        configureInterface = i: nameValuePair "${i.name}-cfg"
+          (let mask =
+                if i.prefixLength != null then toString i.prefixLength else
+                if i.subnetMask != "" then i.subnetMask else "32";
+          in
+          { description = "Configuration of ${i.name}";
+            wantedBy = [ "network-interfaces.target" ];
+            bindsTo = [ "sys-subsystem-net-devices-${i.name}.device" ];
+            after = [ "sys-subsystem-net-devices-${i.name}.device" ];
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.iproute pkgs.gawk ];
+            script =
+              ''
+                echo "bringing up interface..."
+                ip link set "${i.name}" up
+              ''
+              + optionalString (i.macAddress != null)
+                ''
+                  echo "setting MAC address to ${i.macAddress}..."
+                  ip link set "${i.name}" address "${i.macAddress}"
+                ''
+              + optionalString (i.ipAddress != null)
+                ''
+                  cur=$(ip -4 -o a show dev "${i.name}" | awk '{print $4}')
+                  # 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 [ "$cur" != "${i.ipAddress}/${mask}" ]; then
+                    echo "configuring interface..."
+                    ip -4 addr flush dev "${i.name}"
+                    ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}"
+                    # 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
+                ''
+              + optionalString i.proxyARP
+                ''
+                  echo 1 > /proc/sys/net/ipv4/conf/${i.name}/proxy_arp
+                ''
+              + optionalString (i.proxyARP && cfg.enableIPv6)
+                ''
+                  echo 1 > /proc/sys/net/ipv6/conf/${i.name}/proxy_ndp
+                '';
+          });
+
+        createTunDevice = i: nameValuePair "${i.name}"
+          { description = "Virtual Network Interface ${i.name}";
+            requires = [ "dev-net-tun.device" ];
+            after = [ "dev-net-tun.device" ];
+            wantedBy = [ "network.target" ];
+            requiredBy = [ "sys-subsystem-net-devices-${i.name}.device" ];
+            serviceConfig =
+              { Type = "oneshot";
+                RemainAfterExit = true;
+                ExecStart = "${pkgs.tunctl}/bin/tunctl -t '${i.name}' -u '${i.virtualOwner}'";
+                ExecStop = "${pkgs.tunctl}/bin/tunctl -d '${i.name}'";
+              };
+          };
+
+        createBridgeDevice = n: v:
+          let
+            deps = map (i: "sys-subsystem-net-devices-${i}.device") v.interfaces;
+          in
+          { description = "Bridge Interface ${n}";
+            wantedBy = [ "network.target" "sys-subsystem-net-devices-${n}.device" ];
+            bindsTo = deps;
+            after = deps;
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.bridge_utils pkgs.iproute ];
+            script =
+              ''
+                brctl addbr "${n}"
+
+                # Set bridge's hello time to 0 to avoid startup delays.
+                brctl setfd "${n}" 0
+
+                ${flip concatMapStrings v.interfaces (i: ''
+                  brctl addif "${n}" "${i}"
+                  ip link set "${i}" up
+                  ip addr flush dev "${i}"
+
+                  echo "bringing up network device ${n}..."
+                  ip link set "${n}" up
+                '')}
+
+                # !!! Should delete (brctl delif) any interfaces that
+                # no longer belong to the bridge.
+              '';
+            postStop =
+              ''
+                ip link set "${n}" down
+                brctl delbr "${n}"
+              '';
+          };
+
+      in listToAttrs (
+           map configureInterface interfaces ++
+           map createTunDevice (filter (i: i.virtual) interfaces))
+         // mapAttrs createBridgeDevice cfg.bridges
+         // { "network-setup" = networkSetup; };
+
+    # Set the host and domain names in the activation script.  Don't
+    # clear it if it's not configured in the NixOS configuration,
+    # since it may have been set by dhclient in the meantime.
+    system.activationScripts.hostname =
+      optionalString (config.networking.hostName != "") ''
+        hostname "${config.networking.hostName}"
+      '';
+    system.activationScripts.domain =
+      optionalString (config.networking.domain != "") ''
+        domainname "${config.networking.domain}"
+      '';
+
+    services.udev.extraRules =
+      ''
+        KERNEL=="tun", TAG+="systemd"
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/tasks/scsi-link-power-management.nix b/nixos/modules/tasks/scsi-link-power-management.nix
new file mode 100644
index 00000000000..4ab67aee395
--- /dev/null
+++ b/nixos/modules/tasks/scsi-link-power-management.nix
@@ -0,0 +1,44 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  ###### interface
+
+  options = {
+
+    powerManagement.scsiLinkPolicy = mkOption {
+      default = "";
+      example = "min_power";
+      type = types.uniq types.string;
+      description = ''
+        Configure the SCSI link power management policy. By default,
+        the kernel configures "max_performance".
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (config.powerManagement.scsiLinkPolicy != "") {
+
+    jobs."scsi-link-pm" =
+      { description = "SCSI Link Power Management Policy";
+
+        startOn = "stopped udevtrigger";
+
+        task = true;
+
+        script = ''
+          shopt -s nullglob
+          for x in /sys/class/scsi_host/host*/link_power_management_policy; do
+            echo ${config.powerManagement.scsiLinkPolicy} > $x
+          done
+        '';
+      };
+
+  };
+
+}
diff --git a/nixos/modules/tasks/swraid.nix b/nixos/modules/tasks/swraid.nix
new file mode 100644
index 00000000000..3b4aa9875f2
--- /dev/null
+++ b/nixos/modules/tasks/swraid.nix
@@ -0,0 +1,11 @@
+{ config, pkgs, ... }:
+
+{
+
+  environment.systemPackages = [ pkgs.mdadm ];
+
+  services.udev.packages = [ pkgs.mdadm ];
+
+  boot.initrd.availableKernelModules = [ "md_mod" "raid0" "raid1" "raid456" ];
+
+}
diff --git a/nixos/modules/tasks/tty-backgrounds-combine.sh b/nixos/modules/tasks/tty-backgrounds-combine.sh
new file mode 100644
index 00000000000..1e0d8758a6e
--- /dev/null
+++ b/nixos/modules/tasks/tty-backgrounds-combine.sh
@@ -0,0 +1,32 @@
+source $stdenv/setup
+
+ttys=($ttys)
+themes=($themes)
+
+ensureDir $out
+
+defaultName=$(cd $default && ls | grep -v default)
+echo $defaultName
+ln -s $default/$defaultName $out/$defaultName
+ln -s $defaultName $out/default
+
+for ((n = 0; n < ${#ttys[*]}; n++)); do
+    tty=${ttys[$n]}
+    theme=${themes[$n]}
+
+    echo "TTY $tty -> $theme"
+
+    if [ "$theme" != default ]; then
+        themeName=$(cd $theme && ls | grep -v default)
+        ln -sfn $theme/$themeName $out/$themeName
+    else
+        themeName=default
+    fi
+
+    if test -e $out/$tty; then
+        echo "Multiple themes defined for the same TTY!"
+        exit 1
+    fi
+
+    ln -sfn $themeName $out/$tty
+done
diff --git a/nixos/modules/testing/minimal-kernel.nix b/nixos/modules/testing/minimal-kernel.nix
new file mode 100644
index 00000000000..0418de800c8
--- /dev/null
+++ b/nixos/modules/testing/minimal-kernel.nix
@@ -0,0 +1,28 @@
+{ config, pkgs, ... }:
+
+let
+  configfile = builtins.storePath (builtins.toFile "config" (pkgs.lib.concatStringsSep "\n"
+    (map (builtins.getAttr "configLine") config.system.requiredKernelConfig))
+  );
+
+  origKernel = pkgs.linuxManualConfig {
+    inherit (pkgs.linux) src version;
+    inherit configfile;
+    allowImportFromDerivation = true;
+    kernelPatches = [ pkgs.kernelPatches.cifs_timeout_2_6_38 ];
+  };
+
+  kernel = origKernel // (derivation (origKernel.drvAttrs // {
+    configurePhase = ''
+      runHook preConfigure
+      mkdir ../build
+      make $makeFlags "''${makeFlagsArray[@]}" mrproper
+      make $makeFlags "''${makeFlagsArray[@]}" KCONFIG_ALLCONFIG=${configfile} allnoconfig
+      runHook postConfigure
+    '';
+  }));
+
+   kernelPackages = pkgs.linuxPackagesFor kernel;
+in {
+  boot.kernelPackages = kernelPackages;
+}
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
new file mode 100644
index 00000000000..28494e1c7b2
--- /dev/null
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -0,0 +1,91 @@
+# This module allows the test driver to connect to the virtual machine
+# via a root shell attached to port 514.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let kernel = config.boot.kernelPackages.kernel; in
+
+{
+
+  config = {
+
+    systemd.services.backdoor =
+      { wantedBy = [ "multi-user.target" ];
+        requires = [ "dev-hvc0.device" "dev-ttyS0.device" ];
+        after = [ "dev-hvc0.device" "dev-ttyS0.device" ];
+        script =
+          ''
+            export USER=root
+            export HOME=/root
+            export DISPLAY=:0.0
+            source /etc/profile
+            cd /tmp
+            exec < /dev/hvc0 > /dev/hvc0
+            while ! exec 2> /dev/ttyS0; do sleep 0.1; done
+            echo "connecting to host..." >&2
+            stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
+            echo
+            PS1= exec /bin/sh
+          '';
+        serviceConfig.KillSignal = "SIGHUP";
+      };
+
+    # Prevent agetty from being instantiated on ttyS0, since it
+    # interferes with the backdoor (writes to ttyS0 will randomly fail
+    # with EIO).  Likewise for hvc0.
+    systemd.services."serial-getty@ttyS0".enable = false;
+    systemd.services."serial-getty@hvc0".enable = false;
+
+    boot.initrd.postDeviceCommands =
+      ''
+        # Using acpi_pm as a clock source causes the guest clock to
+        # slow down under high host load.  This is usually a bad
+        # thing, but for VM tests it should provide a bit more
+        # determinism (e.g. if the VM runs at lower speed, then
+        # timeouts in the VM should also be delayed).
+        echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource
+      '';
+
+    boot.postBootCommands =
+      ''
+        # Panic on out-of-memory conditions rather than letting the
+        # OOM killer randomly get rid of processes, since this leads
+        # to failures that are hard to diagnose.
+        echo 2 > /proc/sys/vm/panic_on_oom
+
+        # Coverage data is written into /tmp/coverage-data.
+        mkdir -p /tmp/xchg/coverage-data
+      '';
+
+    # If the kernel has been built with coverage instrumentation, make
+    # it available under /proc/gcov.
+    boot.kernelModules = [ "gcov-proc" ];
+
+    # Panic if an error occurs in stage 1 (rather than waiting for
+    # user intervention).
+    boot.kernelParams =
+      [ "console=tty1" "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
+
+    # `xwininfo' is used by the test driver to query open windows.
+    environment.systemPackages = [ pkgs.xorg.xwininfo ];
+
+    # Log everything to the serial console.
+    services.journald.console = "/dev/console";
+
+    # Prevent tests from accessing the Internet.
+    networking.defaultGateway = mkOverride 150 "";
+    networking.nameservers = mkOverride 150 [ ];
+
+    systemd.globalEnvironment.GCOV_PREFIX = "/tmp/xchg/coverage-data";
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "SERIAL_8250_CONSOLE")
+      (isYes "SERIAL_8250")
+      (isEnabled "VIRTIO_CONSOLE")
+    ];
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/amazon-config.nix b/nixos/modules/virtualisation/amazon-config.nix
new file mode 100644
index 00000000000..e816ed2d183
--- /dev/null
+++ b/nixos/modules/virtualisation/amazon-config.nix
@@ -0,0 +1,5 @@
+{ config, pkgs, modulesPath, ... }:
+
+{
+  imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ];
+}
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
new file mode 100644
index 00000000000..7e04f0d2911
--- /dev/null
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -0,0 +1,163 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  imports = [ ../profiles/headless.nix ./ec2-data.nix ];
+
+  system.build.amazonImage =
+    pkgs.vmTools.runInLinuxVM (
+      pkgs.runCommand "amazon-image"
+        { preVM =
+            ''
+              mkdir $out
+              diskImage=$out/nixos.img
+              ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "4G"
+              mv closure xchg/
+            '';
+          buildInputs = [ pkgs.utillinux pkgs.perl ];
+          exportReferencesGraph =
+            [ "closure" config.system.build.toplevel ];
+        }
+        ''
+          # Create an empty filesystem and mount it.
+          ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda
+          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda
+          mkdir /mnt
+          mount /dev/vda /mnt
+
+          # The initrd expects these directories to exist.
+          mkdir /mnt/dev /mnt/proc /mnt/sys
+
+          mount -o bind /proc /mnt/proc
+
+          # Copy all paths in the closure to the filesystem.
+          storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
+
+          mkdir -p /mnt/nix/store
+          echo "copying everything (will take a while)..."
+          cp -prd $storePaths /mnt/nix/store/
+
+          # Register the paths in the Nix database.
+          printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
+              chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
+
+          # Create the system profile to allow nixos-rebuild to work.
+          chroot /mnt ${config.environment.nix}/bin/nix-env \
+              -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
+
+          # `nixos-rebuild' requires an /etc/NIXOS.
+          mkdir -p /mnt/etc
+          touch /mnt/etc/NIXOS
+
+          # `switch-to-configuration' requires a /bin/sh
+          mkdir -p /mnt/bin
+          ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
+
+          # Install a configuration.nix.
+          mkdir -p /mnt/etc/nixos
+          cp ${./amazon-config.nix} /mnt/etc/nixos/configuration.nix
+
+          # Generate the GRUB menu.
+          chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
+
+          umount /mnt/proc
+          umount /mnt
+        ''
+    );
+
+  fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+  boot.initrd.kernelModules = [ "xen-blkfront" ];
+  boot.kernelModules = [ "xen-netfront" ];
+
+  # Generate a GRUB menu.  Amazon's pv-grub uses this to boot our kernel/initrd.
+  boot.loader.grub.version = 1;
+  boot.loader.grub.device = "nodev";
+  boot.loader.grub.timeout = 0;
+  boot.loader.grub.extraPerEntryConfig = "root (hd0)";
+
+  boot.initrd.postDeviceCommands =
+    ''
+      # Force udev to exit to prevent random "Device or resource busy
+      # while trying to open /dev/xvda" errors from fsck.
+      udevadm control --exit || true
+      kill -9 -1
+    '';
+
+  # Mount all formatted ephemeral disks and activate all swap devices.
+  # We cannot do this with the ‘fileSystems’ and ‘swapDevices’ options
+  # because the set of devices is dependent on the instance type
+  # (e.g. "m1.large" has one ephemeral filesystem and one swap device,
+  # while "m1.large" has two ephemeral filesystems and no swap
+  # devices).  Also, put /tmp and /var on /disk0, since it has a lot
+  # more space than the root device.  Similarly, "move" /nix to /disk0
+  # by layering a unionfs-fuse mount on top of it so we have a lot more space for
+  # Nix operations.
+  boot.initrd.postMountCommands =
+    ''
+      diskNr=0
+      diskForUnionfs=
+      for device in /dev/xvd[abcde]*; do
+          if [ "$device" = /dev/xvda -o "$device" = /dev/xvda1 ]; then continue; fi
+          fsType=$(blkid -o value -s TYPE "$device" || true)
+          if [ "$fsType" = swap ]; then
+              echo "activating swap device $device..."
+              swapon "$device" || true
+          elif [ "$fsType" = ext3 ]; then
+              mp="/disk$diskNr"
+              diskNr=$((diskNr + 1))
+              echo "mounting $device on $mp..."
+              if mountFS "$device" "$mp" "" ext3; then
+                  if [ -z "$diskForUnionfs" ]; then diskForUnionfs="$mp"; fi
+              fi
+          else
+              echo "skipping unknown device type $device"
+          fi
+      done
+
+      if [ -n "$diskForUnionfs" ]; then
+          mkdir -m 755 -p $targetRoot/$diskForUnionfs/root
+
+          mkdir -m 1777 -p $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
+          mount --bind $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
+
+          if [ ! -e $targetRoot/.ebs ]; then
+              mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/var $targetRoot/var
+              mount --bind $targetRoot/$diskForUnionfs/root/var $targetRoot/var
+
+              mkdir -p /unionfs-chroot/ro-nix
+              mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
+
+              mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/nix
+              mkdir -p /unionfs-chroot/rw-nix
+              mount --rbind $targetRoot/$diskForUnionfs/root/nix /unionfs-chroot/rw-nix
+
+              unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
+          fi
+      fi
+    '';
+
+  boot.initrd.extraUtilsCommands =
+    ''
+      # We need swapon in the initrd.
+      cp ${pkgs.utillinux}/sbin/swapon $out/bin
+    '';
+
+  # Don't put old configurations in the GRUB menu.  The user has no
+  # way to select them anyway.
+  boot.loader.grub.configurationLimit = 0;
+
+  # Allow root logins only using the SSH key that the user specified
+  # at instance creation time.
+  services.openssh.enable = true;
+  services.openssh.permitRootLogin = "without-password";
+
+  # Force getting the hostname from EC2.
+  networking.hostName = mkDefault "";
+
+  # Always include cryptsetup so that Charon can use it.
+  environment.systemPackages = [ pkgs.cryptsetup ];
+
+  boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
+}
diff --git a/nixos/modules/virtualisation/ec2-data.nix b/nixos/modules/virtualisation/ec2-data.nix
new file mode 100644
index 00000000000..fccf45e0e19
--- /dev/null
+++ b/nixos/modules/virtualisation/ec2-data.nix
@@ -0,0 +1,99 @@
+# This module defines an Upstart job that obtains the SSH key and host
+# name of virtual machines running on Amazon EC2, Eucalyptus and
+# OpenStack Compute (Nova).
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+    ec2.metadata = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to allow access to EC2 metadata.
+      '';
+    };
+  };
+
+  config = {
+
+    systemd.services."fetch-ec2-data" =
+      { description = "Fetch EC2 Data";
+
+        wantedBy = [ "multi-user.target" ];
+        before = [ "sshd.service" ];
+        after = [ "network.target" ];
+
+        path = [ pkgs.curl pkgs.iproute ];
+
+        script =
+          ''
+            ip route del blackhole 169.254.169.254/32 || true
+
+            curl="curl --retry 3 --retry-delay 0 --fail"
+
+            echo "setting host name..."
+            ${optionalString (config.networking.hostName == "") ''
+              ${pkgs.nettools}/bin/hostname $($curl http://169.254.169.254/1.0/meta-data/hostname)
+            ''}
+
+            # Don't download the SSH key if it has already been injected
+            # into the image (a Nova feature).
+            if ! [ -e /root/.ssh/authorized_keys ]; then
+                echo "obtaining SSH key..."
+                mkdir -p /root/.ssh
+                $curl -o /root/key.pub http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key
+                if [ $? -eq 0 -a -e /root/key.pub ]; then
+                    if ! grep -q -f /root/key.pub /root/.ssh/authorized_keys; then
+                        cat /root/key.pub >> /root/.ssh/authorized_keys
+                        echo "new key added to authorized_keys"
+                    fi
+                    chmod 600 /root/.ssh/authorized_keys
+                    rm -f /root/key.pub
+                fi
+            fi
+
+            # Extract the intended SSH host key for this machine from
+            # the supplied user data, if available.  Otherwise sshd will
+            # generate one normally.
+            $curl http://169.254.169.254/2011-01-01/user-data > /root/user-data || true
+            key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' /root/user-data)"
+            key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' /root/user-data)"
+            if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then
+                mkdir -m 0755 -p /etc/ssh
+                (umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key)
+                echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub
+            fi
+
+            ${optionalString (! config.ec2.metadata) ''
+            # Since the user data is sensitive, prevent it from being
+            # accessed from now on.
+            ip route add blackhole 169.254.169.254/32
+            ''}
+          '';
+
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+      };
+
+    systemd.services."print-host-key" =
+      { description = "Print SSH Host Key";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "sshd.service" ];
+        script =
+          ''
+            # Print the host public key on the console so that the user
+            # can obtain it securely by parsing the output of
+            # ec2-get-console-output.
+            echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" > /dev/console
+            ${pkgs.openssh}/bin/ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub > /dev/console
+            echo "-----END SSH HOST KEY FINGERPRINTS-----" > /dev/console
+          '';
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+      };
+
+  };
+}
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
new file mode 100644
index 00000000000..52dab153094
--- /dev/null
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -0,0 +1,148 @@
+# Upstart jobs for libvirtd.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.virtualisation.libvirtd;
+  configFile = pkgs.writeText "libvirtd.conf" ''
+    unix_sock_group = "libvirtd"
+    unix_sock_rw_perms = "0770"
+    auth_unix_ro = "none"
+    auth_unix_rw = "none"
+    ${cfg.extraConfig}
+  '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    virtualisation.libvirtd.enable =
+      mkOption {
+        default = false;
+        description =
+          ''
+            This option enables libvirtd, a daemon that manages
+            virtual machines. Users in the "libvirtd" group can interact with
+            the daemon (e.g. to start or stop VMs) using the
+            <command>virsh</command> command line tool, among others.
+          '';
+      };
+
+    virtualisation.libvirtd.enableKVM =
+      mkOption {
+        default = true;
+        description =
+          ''
+            This option enables support for QEMU/KVM in libvirtd.
+          '';
+      };
+
+    virtualisation.libvirtd.extraConfig =
+      mkOption {
+        default = "";
+        description =
+          ''
+            Extra contents appended to the libvirtd configuration file,
+            libvirtd.conf.
+          '';
+      };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages =
+      [ pkgs.libvirt ]
+       ++ optional cfg.enableKVM pkgs.qemu_kvm;
+
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services.libvirtd =
+      { description = "Libvirt Virtual Machine Management Daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "systemd-udev-settle.service" ];
+
+        path =
+          [ pkgs.bridge_utils pkgs.dmidecode pkgs.dnsmasq
+            pkgs.ebtables
+          ] ++ optional cfg.enableKVM pkgs.qemu_kvm;
+
+        preStart =
+          ''
+            mkdir -p /var/log/libvirt/qemu -m 755
+            rm -f /var/run/libvirtd.pid
+
+            mkdir -p /var/lib/libvirt -m 700
+            mkdir -p /var/lib/libvirt/dnsmasq -m 700
+
+            # Libvirt unfortunately writes mutable state (such as
+            # runtime changes to VM, network or filter configurations)
+            # to /etc.  So we can't use environment.etc to make the
+            # default network and filter definitions available, since
+            # libvirt will then modify the originals in the Nix store.
+            # So here we copy them instead.  Ugly.
+            for i in $(cd ${pkgs.libvirt}/etc && echo \
+                libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
+                libvirt/nwfilter/*.xml );
+            do
+                mkdir -p /etc/$(dirname $i) -m 755
+                cp -fpd ${pkgs.libvirt}/etc/$i /etc/$i
+            done
+          ''; # */
+
+        serviceConfig.ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" --daemon --verbose'';
+        serviceConfig.Type = "forking";
+        serviceConfig.KillMode = "process"; # when stopping, leave the VMs alone
+
+        # Wait until libvirtd is ready to accept requests.
+        postStart =
+          ''
+            for ((i = 0; i < 60; i++)); do
+                if ${pkgs.libvirt}/bin/virsh list > /dev/null; then exit 0; fi
+                sleep 1
+            done
+            exit 1 # !!! seems to be ignored
+          '';
+      };
+
+    jobs."libvirt-guests" =
+      { description = "Libvirt Virtual Machines";
+
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "libvirtd.service" ];
+        after = [ "libvirtd.service" ];
+
+        # We want to suspend VMs only on shutdown, but Upstart is broken.
+        #stopOn = "";
+
+        restartIfChanged = false;
+
+        path = [ pkgs.gettext pkgs.libvirt pkgs.gawk ];
+
+        preStart =
+          ''
+            mkdir -p /var/lock/subsys -m 755
+            ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true
+          '';
+
+        postStop = "${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop";
+
+        serviceConfig.Type = "oneshot";
+        serviceConfig.RemainAfterExit = true;
+      };
+
+    users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/nova-config.nix b/nixos/modules/virtualisation/nova-config.nix
new file mode 100644
index 00000000000..f8239cdec51
--- /dev/null
+++ b/nixos/modules/virtualisation/nova-config.nix
@@ -0,0 +1,5 @@
+{ config, pkgs, modulesPath, ... }:
+
+{
+  imports = [ "${modulesPath}/virtualisation/nova-image.nix" ];
+}
diff --git a/nixos/modules/virtualisation/nova-image.nix b/nixos/modules/virtualisation/nova-image.nix
new file mode 100644
index 00000000000..ab625dba11d
--- /dev/null
+++ b/nixos/modules/virtualisation/nova-image.nix
@@ -0,0 +1,115 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  imports = [ ../profiles/qemu-guest.nix ../profiles/headless.nix ./ec2-data.nix ];
+
+  system.build.novaImage =
+    pkgs.vmTools.runInLinuxVM (
+      pkgs.runCommand "nova-image"
+        { preVM =
+            ''
+              mkdir $out
+              diskImage=$out/image
+              ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "4G"
+              mv closure xchg/
+            '';
+          buildInputs = [ pkgs.utillinux pkgs.perl ];
+          exportReferencesGraph =
+            [ "closure" config.system.build.toplevel ];
+        }
+        ''
+          # Create a single / partition.
+          ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
+          ${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
+          . /sys/class/block/vda1/uevent
+          mknod /dev/vda1 b $MAJOR $MINOR
+
+          # Create an empty filesystem and mount it.
+          ${pkgs.e2fsprogs}/sbin/mkfs.ext3 -L nixos /dev/vda1
+          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
+          mkdir /mnt
+          mount /dev/vda1 /mnt
+
+          # The initrd expects these directories to exist.
+          mkdir /mnt/dev /mnt/proc /mnt/sys
+          mount --bind /proc /mnt/proc
+          mount --bind /dev /mnt/dev
+          mount --bind /sys /mnt/sys
+
+          # Copy all paths in the closure to the filesystem.
+          storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
+
+          mkdir -p /mnt/nix/store
+          ${pkgs.rsync}/bin/rsync -av $storePaths /mnt/nix/store/
+
+          # Register the paths in the Nix database.
+          printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
+              chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
+
+          # Create the system profile to allow nixos-rebuild to work.
+          chroot /mnt ${config.environment.nix}/bin/nix-env \
+              -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
+
+          # `nixos-rebuild' requires an /etc/NIXOS.
+          mkdir -p /mnt/etc
+          touch /mnt/etc/NIXOS
+
+          # Install a configuration.nix.
+          mkdir -p /mnt/etc/nixos
+          cp ${./nova-config.nix} /mnt/etc/nixos/configuration.nix
+
+          # Generate the GRUB menu.
+          chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
+
+          umount /mnt/proc /mnt/dev /mnt/sys
+          umount /mnt
+        ''
+    );
+
+  fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+  boot.kernelParams = [ "console=ttyS0" ];
+
+  boot.loader.grub.version = 2;
+  boot.loader.grub.device = "/dev/vda";
+  boot.loader.grub.timeout = 0;
+
+  # Put /tmp and /var on /ephemeral0, which has a lot more space.
+  # Unfortunately we can't do this with the `fileSystems' option
+  # because it has no support for creating the source of a bind
+  # mount.  Also, "move" /nix to /ephemeral0 by layering a unionfs-fuse
+  # mount on top of it so we have a lot more space for Nix operations.
+  /*
+  boot.initrd.postMountCommands =
+    ''
+      mkdir -m 1777 -p $targetRoot/ephemeral0/tmp
+      mkdir -m 1777 -p $targetRoot/tmp
+      mount --bind $targetRoot/ephemeral0/tmp $targetRoot/tmp
+
+      mkdir -m 755 -p $targetRoot/ephemeral0/var
+      mkdir -m 755 -p $targetRoot/var
+      mount --bind $targetRoot/ephemeral0/var $targetRoot/var
+
+      mkdir -p /unionfs-chroot/ro-nix
+      mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
+
+      mkdir -p /unionfs-chroot/rw-nix
+      mkdir -m 755 -p $targetRoot/ephemeral0/nix
+      mount --rbind $targetRoot/ephemeral0/nix /unionfs-chroot/rw-nix
+      unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
+    '';
+
+    boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
+    */
+
+  # Since Nova allows VNC access to instances, it's nice to start to
+  # start a few virtual consoles.
+  services.mingetty.ttys = [ "tty1" "tty2" ];
+
+  # Allow root logins only using the SSH key that the user specified
+  # at instance creation time.
+  services.openssh.enable = true;
+  services.openssh.permitRootLogin = "without-password";
+}
diff --git a/nixos/modules/virtualisation/nova.nix b/nixos/modules/virtualisation/nova.nix
new file mode 100644
index 00000000000..05c68e2bbff
--- /dev/null
+++ b/nixos/modules/virtualisation/nova.nix
@@ -0,0 +1,174 @@
+# Module for Nova, a.k.a. OpenStack Compute.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.virtualisation.nova;
+
+  nova = pkgs.nova;
+
+  novaConf = pkgs.writeText "nova.conf"
+    ''
+      --nodaemon
+      --verbose
+      ${cfg.extraConfig}
+    '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    virtualisation.nova.enableSingleNode =
+      mkOption {
+        default = false;
+        description =
+          ''
+            This option enables Nova, also known as OpenStack Compute,
+            a cloud computing system, as a single-machine
+            installation.  That is, all of Nova's components are
+            enabled on this machine, using SQLite as Nova's database.
+            This is useful for evaluating and experimenting with Nova.
+            However, for a real cloud computing environment, you'll
+            want to enable some of Nova's services on other machines,
+            and use a database such as MySQL.
+          '';
+      };
+
+    virtualisation.nova.extraConfig =
+      mkOption {
+        default = "";
+        description =
+          ''
+            Additional text appended to <filename>nova.conf</filename>,
+            the main Nova configuration file.
+          '';
+      };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enableSingleNode {
+
+    environment.systemPackages = [ nova pkgs.euca2ools pkgs.novaclient ];
+
+    environment.etc =
+      [ { source = novaConf;
+          target = "nova/nova.conf";
+        }
+      ];
+
+    # Nova requires libvirtd and RabbitMQ.
+    virtualisation.libvirtd.enable = true;
+    services.rabbitmq.enable = true;
+
+    # `qemu-nbd' required the `nbd' kernel module.
+    boot.kernelModules = [ "nbd" ];
+
+    system.activationScripts.nova =
+      ''
+        mkdir -m 755 -p /var/lib/nova
+        mkdir -m 755 -p /var/lib/nova/networks
+        mkdir -m 700 -p /var/lib/nova/instances
+        mkdir -m 700 -p /var/lib/nova/keys
+
+        # Allow the CA certificate generation script (called by
+        # nova-api) to work.
+        mkdir -m 700 -p /var/lib/nova/CA /var/lib/nova/CA/private
+
+        # Initialise the SQLite database.
+        ${nova}/bin/nova-manage db sync
+      '';
+
+    # `nova-api' receives and executes external client requests from
+    # tools such as euca2ools.  It listens on port 8773 (XML) and 8774
+    # (JSON).
+    jobs.nova_api =
+      { name = "nova-api";
+
+        description = "Nova API service";
+
+        startOn = "ip-up";
+
+        # `openssl' is required to generate the CA.  `openssh' is
+        # required to generate key pairs.
+        path = [ pkgs.openssl pkgs.openssh pkgs.bash ];
+
+        respawn = false;
+
+        exec = "${nova}/bin/nova-api --flagfile=${novaConf} --api_paste_config=${nova}/etc/nova/api-paste.ini";
+      };
+
+    # `nova-objectstore' is a simple image server.  Useful if you're
+    # not running the OpenStack Imaging Service (Swift).  It serves
+    # images placed in /var/lib/nova/images/.
+    jobs.nova_objectstore =
+      { name = "nova-objectstore";
+
+        description = "Nova simple object store service";
+
+        startOn = "ip-up";
+
+        preStart =
+          ''
+            mkdir -m 700 -p /var/lib/nova/images
+          '';
+
+        exec = "${nova}/bin/nova-objectstore --flagfile=${novaConf}";
+      };
+
+    # `nova-scheduler' schedules VM execution requests.
+    jobs.nova_scheduler =
+      { name = "nova-scheduler";
+
+        description = "Nova scheduler service";
+
+        startOn = "ip-up";
+
+        exec = "${nova}/bin/nova-scheduler --flagfile=${novaConf}";
+      };
+
+    # `nova-compute' starts and manages virtual machines.
+    jobs.nova_compute =
+      { name = "nova-compute";
+
+        description = "Nova compute service";
+
+        startOn = "ip-up";
+
+        path =
+          [ pkgs.sudo pkgs.vlan pkgs.nettools pkgs.iptables pkgs.qemu_kvm
+            pkgs.e2fsprogs pkgs.utillinux pkgs.multipath_tools pkgs.iproute
+            pkgs.bridge_utils
+          ];
+
+        exec = "${nova}/bin/nova-compute --flagfile=${novaConf}";
+      };
+
+    # `nova-network' manages networks and allocates IP addresses.
+    jobs.nova_network =
+      { name = "nova-network";
+
+        description = "Nova network service";
+
+        startOn = "ip-up";
+
+        path =
+          [ pkgs.sudo pkgs.vlan pkgs.dnsmasq pkgs.nettools pkgs.iptables
+            pkgs.iproute pkgs.bridge_utils pkgs.radvd
+          ];
+
+        exec = "${nova}/bin/nova-network --flagfile=${novaConf}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/qemu-opts b/nixos/modules/virtualisation/qemu-opts
new file mode 100644
index 00000000000..f06a5136608
--- /dev/null
+++ b/nixos/modules/virtualisation/qemu-opts
@@ -0,0 +1,4 @@
+          -device virtio-serial \
+          -chardev socket,id=charconsole0,path=/tmp/nixos-socket,server,nowait \
+          #-device virtconsole,chardev=charconsole0,id=console0 \
+          -device virtserialport,chardev=chardev=charconsole0,id=serial0
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
new file mode 100644
index 00000000000..5b521a45547
--- /dev/null
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -0,0 +1,421 @@
+# This module creates a virtual machine from the NixOS configuration.
+# Building the `config.system.build.vm' attribute gives you a command
+# that starts a KVM/QEMU VM running the NixOS configuration defined in
+# `config'.  The Nix store is shared read-only with the host, which
+# makes (re)building VMs very efficient.  However, it also means you
+# can't reconfigure the guest inside the guest - you need to rebuild
+# the VM in the host.  On the other hand, the root filesystem is a
+# read/writable disk image persistent across VM reboots.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  vmName =
+    if config.networking.hostName == ""
+    then "noname"
+    else config.networking.hostName;
+
+  cfg = config.virtualisation;
+
+  qemuGraphics = if cfg.graphics then "" else "-nographic";
+  kernelConsole = if cfg.graphics then "" else "console=ttyS0";
+  ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ];
+
+  # Shell script to start the VM.
+  startVM =
+    ''
+      #! ${pkgs.stdenv.shell}
+
+      NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
+
+      if ! test -e "$NIX_DISK_IMAGE"; then
+          ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
+            ${toString config.virtualisation.diskSize}M || exit 1
+      fi
+
+      # Create a directory for exchanging data with the VM.
+      if [ -z "$TMPDIR" -o -z "$USE_TMPDIR" ]; then
+          TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
+      fi
+      cd $TMPDIR
+      mkdir -p $TMPDIR/xchg
+
+      idx=2
+      extraDisks=""
+      ${flip concatMapStrings cfg.emptyDiskImages (size: ''
+        ${pkgs.qemu_kvm}/bin/qemu-img create -f raw "empty$idx" "${toString size}M"
+        extraDisks="$extraDisks -drive index=$idx,file=$(pwd)/empty$idx,if=virtio,werror=report"
+        idx=$((idx + 1))
+      '')}
+
+      # Start QEMU.
+      # "-boot menu=on" is there, because I don't know how to make qemu boot from 2nd hd.
+      exec ${pkgs.qemu_kvm}/bin/qemu-kvm \
+          -name ${vmName} \
+          -m ${toString config.virtualisation.memorySize} \
+          ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
+          -net nic,vlan=0,model=virtio \
+          -net user,vlan=0''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \
+          -virtfs local,path=/nix/store,security_model=none,mount_tag=store \
+          -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
+          -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \
+          ${if cfg.useBootLoader then ''
+            -drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
+            -drive index=1,id=drive2,file=${bootDisk}/disk.img,if=virtio,readonly \
+            -boot menu=on
+          '' else ''
+            -drive file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
+            -kernel ${config.system.build.toplevel}/kernel \
+            -initrd ${config.system.build.toplevel}/initrd \
+            -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS" \
+          ''} \
+          $extraDisks \
+          ${qemuGraphics} \
+          ${toString config.virtualisation.qemu.options} \
+          $QEMU_OPTS
+    '';
+
+
+  regInfo = pkgs.runCommand "reginfo"
+    { exportReferencesGraph =
+        map (x: [("closure-" + baseNameOf x) x]) config.virtualisation.pathsInNixDB;
+      buildInputs = [ pkgs.perl ];
+      preferLocalBuild = true;
+    }
+    ''
+      printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > $out
+    '';
+
+
+  # Generate a hard disk image containing a /boot partition and GRUB
+  # in the MBR.  Used when the `useBootLoader' option is set.
+  bootDisk =
+    pkgs.vmTools.runInLinuxVM (
+      pkgs.runCommand "nixos-boot-disk"
+        { preVM =
+            ''
+              mkdir $out
+              diskImage=$out/disk.img
+              ${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 $diskImage "32M"
+            '';
+          buildInputs = [ pkgs.utillinux ];
+        }
+        ''
+          # Create a single /boot partition.
+          ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
+          ${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
+          . /sys/class/block/vda1/uevent
+          mknod /dev/vda1 b $MAJOR $MINOR
+          . /sys/class/block/vda/uevent
+          ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L boot /dev/vda1
+          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
+
+          # Mount /boot.
+          mkdir /boot
+          mount /dev/vda1 /boot
+
+          # This is needed for GRUB 0.97, which doesn't know about virtio devices.
+          mkdir /boot/grub
+          echo '(hd0) /dev/vda' > /boot/grub/device.map
+
+          # Install GRUB and generate the GRUB boot menu.
+          touch /etc/NIXOS
+          mkdir -p /nix/var/nix/profiles
+          ${config.system.build.toplevel}/bin/switch-to-configuration boot
+
+          umount /boot
+        ''
+    );
+
+in
+
+{
+  imports = [ ../profiles/qemu-guest.nix ];
+
+  options = {
+
+    virtualisation.memorySize =
+      mkOption {
+        default = 384;
+        description =
+          ''
+            Memory size (M) of virtual machine.
+          '';
+      };
+
+    virtualisation.diskSize =
+      mkOption {
+        default = 512;
+        description =
+          ''
+            Disk size (M) of virtual machine.
+          '';
+      };
+
+    virtualisation.diskImage =
+      mkOption {
+        default = "./${vmName}.qcow2";
+        description =
+          ''
+            Path to the disk image containing the root filesystem.
+            The image will be created on startup if it does not
+            exist.
+          '';
+      };
+
+    virtualisation.emptyDiskImages =
+      mkOption {
+        default = [];
+        type = types.listOf types.int;
+        description =
+          ''
+            Additional disk images to provide to the VM, the value is a list of
+            sizes in megabytes the empty disk should be.
+
+            These disks are writeable by the VM and will be thrown away
+            afterwards.
+          '';
+      };
+
+    virtualisation.graphics =
+      mkOption {
+        default = true;
+        description =
+          ''
+            Whether to run QEMU with a graphics window, or access
+            the guest computer serial port through the host tty.
+          '';
+      };
+
+    virtualisation.pathsInNixDB =
+      mkOption {
+        default = [];
+        description =
+          ''
+            The list of paths whose closure is registered in the Nix
+            database in the VM.  All other paths in the host Nix store
+            appear in the guest Nix store as well, but are considered
+            garbage (because they are not registered in the Nix
+            database in the guest).
+          '';
+      };
+
+    virtualisation.vlans =
+      mkOption {
+        default = [ 1 ];
+        example = [ 1 2 ];
+        description =
+          ''
+            Virtual networks to which the VM is connected.  Each
+            number <replaceable>N</replaceable> in this list causes
+            the VM to have a virtual Ethernet interface attached to a
+            separate virtual network on which it will be assigned IP
+            address
+            <literal>192.168.<replaceable>N</replaceable>.<replaceable>M</replaceable></literal>,
+            where <replaceable>M</replaceable> is the index of this VM
+            in the list of VMs.
+          '';
+      };
+
+    virtualisation.writableStore =
+      mkOption {
+        default = false;
+        description =
+          ''
+            If enabled, the Nix store in the VM is made writable by
+            layering a unionfs-fuse/tmpfs filesystem on top of the host's Nix
+            store.
+          '';
+      };
+
+    virtualisation.writableStoreUseTmpfs =
+      mkOption {
+        default = true;
+        description =
+          ''
+            Use a tmpfs for the writable store instead of writing to the VM's
+            own filesystem.
+          '';
+      };
+
+    networking.primaryIPAddress =
+      mkOption {
+        default = "";
+        internal = true;
+        description = "Primary IP address used in /etc/hosts.";
+      };
+
+    virtualisation.qemu.options =
+      mkOption {
+        default = [];
+        example = [ "-vga std" ];
+        description = "Options passed to QEMU.";
+      };
+
+    virtualisation.useBootLoader =
+      mkOption {
+        default = false;
+        description =
+          ''
+            If enabled, the virtual machine will be booted using the
+            regular boot loader (i.e., GRUB 1 or 2).  This allows
+            testing of the boot loader.  If
+            disabled (the default), the VM directly boots the NixOS
+            kernel and initial ramdisk, bypassing the boot loader
+            altogether.
+          '';
+      };
+
+  };
+
+  config = {
+
+    boot.loader.grub.device = mkOverride 50 "/dev/vda";
+
+    boot.initrd.supportedFilesystems = optional cfg.writableStore "unionfs-fuse";
+
+    boot.initrd.extraUtilsCommands =
+      ''
+        # We need mke2fs in the initrd.
+        cp ${pkgs.e2fsprogs}/sbin/mke2fs $out/bin
+      '';
+
+    boot.initrd.postDeviceCommands =
+      ''
+        # If the disk image appears to be empty, run mke2fs to
+        # initialise.
+        FSTYPE=$(blkid -o value -s TYPE /dev/vda || true)
+        if test -z "$FSTYPE"; then
+            mke2fs -t ext4 /dev/vda
+        fi
+      '';
+
+    boot.initrd.postMountCommands =
+      ''
+        # Mark this as a NixOS machinex.
+        mkdir -p $targetRoot/etc
+        echo -n > $targetRoot/etc/NIXOS
+
+        # Fix the permissions on /tmp.
+        chmod 1777 $targetRoot/tmp
+
+        mkdir -p $targetRoot/boot
+        mount -o remount,ro $targetRoot/nix/store
+        ${optionalString cfg.writableStore ''
+          mkdir -p /unionfs-chroot/ro-store
+          mount --rbind $targetRoot/nix/store /unionfs-chroot/ro-store
+
+          mkdir /unionfs-chroot/rw-store
+          ${if cfg.writableStoreUseTmpfs then ''
+          mount -t tmpfs -o "mode=755" none /unionfs-chroot/rw-store
+          '' else ''
+          mkdir $targetRoot/.nix-rw-store
+          mount --bind $targetRoot/.nix-rw-store /unionfs-chroot/rw-store
+          ''}
+
+          unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768,hide_meta_files /rw-store=RW:/ro-store=RO $targetRoot/nix/store
+        ''}
+      '';
+
+    # After booting, register the closure of the paths in
+    # `virtualisation.pathsInNixDB' in the Nix database in the VM.  This
+    # allows Nix operations to work in the VM.  The path to the
+    # registration file is passed through the kernel command line to
+    # allow `system.build.toplevel' to be included.  (If we had a direct
+    # reference to ${regInfo} here, then we would get a cyclic
+    # dependency.)
+    boot.postBootCommands =
+      ''
+        if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
+          ${config.environment.nix}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
+        fi
+      '';
+
+    virtualisation.pathsInNixDB = [ config.system.build.toplevel ];
+
+    virtualisation.qemu.options = [ "-vga std" "-usbdevice tablet" ];
+
+    # Mount the host filesystem via 9P, and bind-mount the Nix store of
+    # the host into our own filesystem.  We use mkOverride to allow this
+    # module to be applied to "normal" NixOS system configuration, where
+    # the regular value for the `fileSystems' attribute should be
+    # disregarded for the purpose of building a VM test image (since
+    # those filesystems don't exist in the VM).
+    fileSystems = mkOverride 10
+      { "/".device = "/dev/vda";
+        "/nix/store" =
+          { device = "store";
+            fsType = "9p";
+            options = "trans=virtio,version=9p2000.L,msize=1048576,cache=loose";
+          };
+        "/tmp/xchg" =
+          { device = "xchg";
+            fsType = "9p";
+            options = "trans=virtio,version=9p2000.L,msize=1048576,cache=loose";
+            neededForBoot = true;
+          };
+        "/tmp/shared" =
+          { device = "shared";
+            fsType = "9p";
+            options = "trans=virtio,version=9p2000.L,msize=1048576";
+            neededForBoot = true;
+          };
+      } // optionalAttrs cfg.useBootLoader
+      { "/boot" =
+          { device = "/dev/disk/by-label/boot";
+            fsType = "ext4";
+            options = "ro";
+            noCheck = true; # fsck fails on a r/o filesystem
+          };
+      };
+
+    swapDevices = mkOverride 50 [ ];
+
+    # Don't run ntpd in the guest.  It should get the correct time from KVM.
+    services.ntp.enable = false;
+
+    system.build.vm = pkgs.runCommand "nixos-vm" { preferLocalBuild = true; }
+      ''
+        ensureDir $out/bin
+        ln -s ${config.system.build.toplevel} $out/system
+        ln -s ${pkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${vmName}-vm
+      '';
+
+    # When building a regular system configuration, override whatever
+    # video driver the host uses.
+    services.xserver.videoDriver = mkOverride 50 null;
+    services.xserver.videoDrivers = mkOverride 50 [ "vesa" ];
+    services.xserver.defaultDepth = mkOverride 50 0;
+    services.xserver.resolutions = mkOverride 50 [ { x = 1024; y = 768; } ];
+    services.xserver.monitorSection =
+      ''
+        # Set a higher refresh rate so that resolutions > 800x600 work.
+        HorizSync 30-140
+        VertRefresh 50-160
+      '';
+
+    # Wireless won't work in the VM.
+    networking.wireless.enable = mkOverride 50 false;
+
+    system.requiredKernelConfig = with config.lib.kernelConfig;
+      [ (isEnabled "VIRTIO_BLK")
+        (isEnabled "VIRTIO_PCI")
+        (isEnabled "VIRTIO_NET")
+        (isEnabled "EXT4_FS")
+        (isYes "BLK_DEV")
+        (isYes "PCI")
+        (isYes "EXPERIMENTAL")
+        (isYes "NETDEVICES")
+        (isYes "NET_CORE")
+        (isYes "INET")
+        (isYes "NETWORK_FILESYSTEMS")
+      ] ++ optional (!cfg.graphics) [
+        (isYes "SERIAL_8250_CONSOLE")
+        (isYes "SERIAL_8250")
+      ];
+
+  };
+}
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
new file mode 100644
index 00000000000..e1a83cdecc2
--- /dev/null
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -0,0 +1,91 @@
+# Module for VirtualBox guests.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.virtualbox;
+  kernel = config.boot.kernelPackages;
+
+in
+
+optionalAttrs (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) # ugly...
+{
+
+  ###### interface
+
+  options = {
+
+    services.virtualbox = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the VirtualBox service and other guest additions.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ kernel.virtualboxGuestAdditions ];
+
+    boot.extraModulePackages = [ kernel.virtualboxGuestAdditions ];
+
+    users.extraGroups.vboxsf.gid = config.ids.gids.vboxsf;
+
+    systemd.services.virtualbox =
+      { description = "VirtualBox Guest Services";
+
+        wantedBy = [ "multi-user.target" ];
+        requires = [ "dev-vboxguest.device" ];
+        after = [ "dev-vboxguest.device" ];
+
+        unitConfig.ConditionVirtualization = "oracle";
+
+        serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/sbin/VBoxService VBoxService --foreground";
+      };
+
+    services.xserver.videoDrivers = mkOverride 50 [ "virtualbox" ];
+
+    services.xserver.config =
+      ''
+        Section "InputDevice"
+          Identifier "VBoxMouse"
+          Driver "vboxmouse"
+        EndSection
+      '';
+
+    services.xserver.serverLayoutSection =
+      ''
+        InputDevice "VBoxMouse"
+      '';
+
+    services.xserver.displayManager.sessionCommands =
+      ''
+        PATH=${makeSearchPath "bin" [ pkgs.gnugrep pkgs.which pkgs.xorg.xorgserver ]}:$PATH \
+          ${kernel.virtualboxGuestAdditions}/bin/VBoxClient-all
+      '';
+
+    services.udev.extraRules =
+      ''
+        # /dev/vboxuser is necessary for VBoxClient to work.  Maybe we
+        # should restrict this to logged-in users.
+        KERNEL=="vboxuser",  OWNER="root", GROUP="root", MODE="0666"
+
+        # Allow systemd dependencies on vboxguest.
+        KERNEL=="vboxguest", TAG+="systemd"
+      '';
+
+    # Make the ACPI Shutdown command to do the right thing.
+    services.acpid.enable = true;
+    services.acpid.powerEventCommands = "poweroff";
+  };
+
+}
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
new file mode 100644
index 00000000000..e1b6def8edb
--- /dev/null
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -0,0 +1,110 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  system.build.virtualBoxImage =
+    pkgs.vmTools.runInLinuxVM (
+      pkgs.runCommand "virtualbox-image"
+        { memSize = 768;
+          preVM =
+            ''
+              mkdir $out
+              diskImage=$out/image
+              ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "10G"
+              mv closure xchg/
+            '';
+          postVM =
+            ''
+              echo "creating VirtualBox disk image..."
+              ${pkgs.vmTools.qemu}/bin/qemu-img convert -f raw -O vdi $diskImage $out/disk.vdi
+              rm $diskImage
+            '';
+          buildInputs = [ pkgs.utillinux pkgs.perl ];
+          exportReferencesGraph =
+            [ "closure" config.system.build.toplevel ];
+        }
+        ''
+          # Create a single / partition.
+          ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
+          ${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
+          . /sys/class/block/vda1/uevent
+          mknod /dev/vda1 b $MAJOR $MINOR
+
+          # Create an empty filesystem and mount it.
+          ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1
+          ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
+          mkdir /mnt
+          mount /dev/vda1 /mnt
+
+          # The initrd expects these directories to exist.
+          mkdir /mnt/dev /mnt/proc /mnt/sys
+          mount --bind /proc /mnt/proc
+          mount --bind /dev /mnt/dev
+          mount --bind /sys /mnt/sys
+
+          # Copy all paths in the closure to the filesystem.
+          storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
+
+          echo "filling Nix store..."
+          mkdir -p /mnt/nix/store
+          set -f
+          cp -prvd $storePaths /mnt/nix/store/
+
+          # Register the paths in the Nix database.
+          printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
+              chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
+
+          # Create the system profile to allow nixos-rebuild to work.
+          chroot /mnt ${config.environment.nix}/bin/nix-env \
+              -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
+
+          # `nixos-rebuild' requires an /etc/NIXOS.
+          mkdir -p /mnt/etc/nixos
+          touch /mnt/etc/NIXOS
+
+          # `switch-to-configuration' requires a /bin/sh
+          mkdir -p /mnt/bin
+          ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
+
+          # Generate the GRUB menu.
+          ln -s vda /dev/sda
+          chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
+
+          umount /mnt/proc /mnt/dev /mnt/sys
+          umount /mnt
+        ''
+    );
+
+  system.build.virtualBoxOVA = pkgs.runCommand "virtualbox-ova"
+    { buildInputs = [ pkgs.linuxPackages.virtualbox ];
+      vmName = "NixOS ${config.system.nixosVersion} (${pkgs.stdenv.system})";
+      fileName = "nixos-${config.system.nixosVersion}-${pkgs.stdenv.system}.ova";
+    }
+    ''
+      echo "creating VirtualBox VM..."
+      export HOME=$PWD
+      VBoxManage createvm --name "$vmName" --register \
+        --ostype ${if pkgs.stdenv.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
+      VBoxManage modifyvm "$vmName" \
+        --memory 1536 --acpi on --vram 10 \
+        --nictype1 virtio --nic1 nat \
+        --audiocontroller ac97 --audio alsa \
+        --rtcuseutc on \
+        --usb on --mouse usbtablet
+      VBoxManage storagectl "$vmName" --name SATA --add sata --sataportcount 4 --bootable on --hostiocache on
+      VBoxManage storageattach "$vmName" --storagectl SATA --port 0 --device 0 --type hdd \
+        --medium ${config.system.build.virtualBoxImage}/disk.vdi
+
+      echo "exporting VirtualBox VM..."
+      mkdir -p $out
+      VBoxManage export "$vmName" --output "$out/$fileName"
+    '';
+
+  fileSystems."/".device = "/dev/disk/by-label/nixos";
+
+  boot.loader.grub.version = 2;
+  boot.loader.grub.device = "/dev/sda";
+
+  services.virtualbox.enable = true;
+}
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
new file mode 100644
index 00000000000..4c24c6a7826
--- /dev/null
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -0,0 +1,179 @@
+# Xen hypervisor (Dom0) support.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.virtualisation.xen;
+
+  xen = pkgs.xen;
+
+  xendConfig = pkgs.writeText "xend-config.sxp"
+    ''
+      (loglevel DEBUG)
+      (network-script network-bridge)
+      (vif-script vif-bridge)
+    '';
+
+in
+
+{
+  ###### interface
+
+  options = {
+
+    virtualisation.xen.enable =
+      mkOption {
+        default = false;
+        description =
+          ''
+            Setting this option enables the Xen hypervisor, a
+            virtualisation technology that allows multiple virtual
+            machines, known as <emphasis>domains</emphasis>, to run
+            concurrently on the physical machine.  NixOS runs as the
+            privileged <emphasis>Domain 0</emphasis>.  This option
+            requires a reboot to take effect.
+          '';
+      };
+
+    virtualisation.xen.bootParams =
+      mkOption {
+        default = "";
+        description =
+          ''
+            Parameters passed to the Xen hypervisor at boot time.
+          '';
+      };
+
+    virtualisation.xen.domain0MemorySize =
+      mkOption {
+        default = 0;
+        example = 512;
+        description =
+          ''
+            Amount of memory (in MiB) allocated to Domain 0 on boot.
+            If set to 0, all memory is assigned to Domain 0.
+          '';
+      };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ xen ];
+
+    # Domain 0 requires a pvops-enabled kernel.
+    boot.kernelPackages = pkgs.linuxPackages_3_2_xen;
+
+    boot.kernelModules =
+      [ "xen_evtchn" "xen_gntdev" "xen_blkback" "xen_netback" "xen_pciback"
+        "blktap" "tun"
+      ];
+
+    # The radeonfb kernel module causes the screen to go black as soon
+    # as it's loaded, so don't load it.
+    boot.blacklistedKernelModules = [ "radeonfb" ];
+
+    # Increase the number of loopback devices from the default (8),
+    # which is way too small because every VM virtual disk requires a
+    # loopback device.
+    boot.extraModprobeConfig =
+      ''
+        options loop max_loop=64
+      '';
+
+    virtualisation.xen.bootParams =
+      [ "loglvl=all" "guest_loglvl=all" ] ++
+      optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";
+
+    system.extraSystemBuilderCmds =
+      ''
+        ln -s ${xen}/boot/xen.gz $out/xen.gz
+        echo "${toString cfg.bootParams}" > $out/xen-params
+      '';
+
+    # Mount the /proc/xen pseudo-filesystem.
+    system.activationScripts.xen =
+      ''
+        if [ -d /proc/xen ]; then
+            ${pkgs.sysvtools}/bin/mountpoint -q /proc/xen || \
+                ${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen
+        fi
+      '';
+
+    jobs.xend =
+      { description = "Xen control daemon";
+
+        startOn = "stopped udevtrigger";
+
+        path =
+          [ pkgs.bridge_utils pkgs.gawk pkgs.iproute pkgs.nettools
+            pkgs.utillinux pkgs.bash xen pkgs.pciutils pkgs.procps
+          ];
+
+        environment.XENCONSOLED_TRACE = "hv";
+
+        preStart =
+          ''
+            mkdir -p /var/log/xen/console -m 0700
+
+            ${xen}/sbin/xend start
+
+            # Wait until Xend is running.
+            for ((i = 0; i < 60; i++)); do echo "waiting for xend..."; ${xen}/sbin/xend status && break; done
+
+            ${xen}/sbin/xend status || exit 1
+          '';
+
+        postStop = "${xen}/sbin/xend stop";
+      };
+
+    jobs.xendomains =
+      { description = "Automatically starts, saves and restores Xen domains on startup/shutdown";
+
+        startOn = "started xend";
+
+        stopOn = "starting shutdown and stopping xend";
+
+        restartIfChanged = false;
+        
+        path = [ pkgs.xen ];
+
+        environment.XENDOM_CONFIG = "${xen}/etc/sysconfig/xendomains";
+
+        preStart =
+          ''
+            mkdir -p /var/lock/subsys -m 755
+            ${xen}/etc/init.d/xendomains start
+          '';
+
+        postStop = "${xen}/etc/init.d/xendomains stop";
+      };
+
+    # To prevent a race between dhcpcd and xend's bridge setup script
+    # (which renames eth* to peth* and recreates eth* as a virtual
+    # device), start dhcpcd after xend.
+    jobs.dhcpcd.startOn = mkOverride 50 "started xend";
+
+    environment.etc =
+      [ { source = xendConfig;
+          target = "xen/xend-config.sxp";
+        }
+        { source = "${xen}/etc/xen/scripts";
+          target = "xen/scripts";
+        }
+      ];
+
+    # Xen provides udev rules.
+    services.udev.packages = [ xen ];
+
+    services.udev.path = [ pkgs.bridge_utils pkgs.iproute ];
+
+  };
+
+}
diff --git a/nixos/modules/virtualisation/xen-domU.nix b/nixos/modules/virtualisation/xen-domU.nix
new file mode 100644
index 00000000000..48358966934
--- /dev/null
+++ b/nixos/modules/virtualisation/xen-domU.nix
@@ -0,0 +1,19 @@
+# Common configuration for Xen DomU NixOS virtual machines.
+
+{ config, pkgs, ... }:
+
+{
+  # We're being booted using pv-grub, which means that we need to
+  # generate a GRUB 1 menu without actually installing GRUB.
+  boot.loader.grub.version = 1;
+  boot.loader.grub.device = "nodev";
+  boot.loader.grub.extraPerEntryConfig = "root (hd0)";
+
+  boot.initrd.kernelModules = [ "xen-blkfront" ];
+
+  # Send syslog messages to the Xen console.
+  services.syslogd.tty = "hvc0";
+
+  # Don't run ntpd, since we should get the correct time from Dom0.
+  services.ntp.enable = false;
+}