summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/test-driver/Machine.pm2
-rw-r--r--nixos/modules/misc/ids.nix8
-rw-r--r--nixos/modules/module-list.nix7
-rw-r--r--nixos/modules/programs/zsh/zsh.nix3
-rw-r--r--nixos/modules/rename.nix1
-rw-r--r--nixos/modules/security/duosec.nix198
-rw-r--r--nixos/modules/services/backup/tarsnap.nix11
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix118
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/slave.nix67
-rw-r--r--nixos/modules/services/databases/couchdb.nix33
-rw-r--r--nixos/modules/services/databases/mysql.nix5
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/misc/gpsd.nix30
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix2
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/graphite.nix94
-rw-r--r--nixos/modules/services/monitoring/statsd.nix4
-rw-r--r--nixos/modules/services/network-filesystems/openafs-client/default.nix35
-rw-r--r--nixos/modules/services/networking/firewall.nix15
-rw-r--r--nixos/modules/services/networking/kippo.nix3
-rw-r--r--nixos/modules/services/networking/notbit.nix93
-rw-r--r--nixos/modules/services/search/solr.nix114
-rw-r--r--nixos/modules/services/ttys/agetty.nix95
-rw-r--r--nixos/modules/services/ttys/kmscon.nix11
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix11
-rw-r--r--nixos/modules/services/web-servers/phpfpm.nix77
-rw-r--r--nixos/modules/services/web-servers/winstone.nix129
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/xbmc.nix31
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl2
-rw-r--r--nixos/modules/system/boot/kernel.nix3
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh13
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix7
-rw-r--r--nixos/modules/system/boot/systemd.nix126
-rw-r--r--nixos/modules/system/etc/etc.nix20
-rw-r--r--nixos/modules/system/etc/make-etc.sh4
-rw-r--r--nixos/modules/system/etc/setup-etc.pl8
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix221
-rw-r--r--nixos/modules/tasks/network-interfaces.nix53
-rw-r--r--nixos/tests/default.nix1
-rw-r--r--nixos/tests/jenkins.nix35
45 files changed, 1397 insertions, 335 deletions
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index 8bef3d67b8d9..99810f87750d 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -497,7 +497,7 @@ sub waitForX {
         retry sub {
             my ($status, $out) = $self->execute("journalctl -bu systemd-logind | grep Linked");
             return 0 if $status != 0;
-            my ($status, $out) = $self->execute("xwininfo -root > /dev/null 2>&1");
+            ($status, $out) = $self->execute("xwininfo -root > /dev/null 2>&1");
             return 1 if $status == 0;
         }
     });
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index e50819d6d001..970b9caa2f9d 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -116,6 +116,10 @@
       dictd = 105;
       couchdb = 106;
       searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
+      notbit = 111;
 
       # When adding a uid, make sure it doesn't match an existing gid.
 
@@ -210,6 +214,10 @@
       dictd = 105;
       couchdb = 106;
       searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
+      notbit = 111;
 
       # When adding a gid, make sure it doesn't match an existing uid.
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 3a5eee1e3c69..1c2fca1f88b5 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -61,6 +61,7 @@
   ./security/apparmor.nix
   ./security/apparmor-suid.nix
   ./security/ca.nix
+  ./security/duosec.nix
   ./security/pam.nix
   ./security/pam_usb.nix
   ./security/polkit.nix
@@ -81,6 +82,8 @@
   ./services/backup/rsnapshot.nix
   ./services/backup/sitecopy-backup.nix
   ./services/backup/tarsnap.nix
+  ./services/continuous-integration/jenkins/default.nix
+  ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
   ./services/databases/couchdb.nix
@@ -177,6 +180,7 @@
   ./services/networking/minidlna.nix
   ./services/networking/nat.nix
   ./services/networking/networkmanager.nix
+  ./services/networking/notbit.nix
   ./services/networking/ntopng.nix
   ./services/networking/ntpd.nix
   ./services/networking/oidentd.nix
@@ -206,6 +210,7 @@
   ./services/scheduling/cron.nix
   ./services/scheduling/fcron.nix
   ./services/search/elasticsearch.nix
+  ./services/search/solr.nix
   ./services/security/clamav.nix
   ./services/security/haveged.nix
   ./services/security/fprot.nix
@@ -228,8 +233,10 @@
   ./services/web-servers/lighttpd/cgit.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/nginx/default.nix
+  ./services/web-servers/phpfpm.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/varnish/default.nix
+  ./services/web-servers/winstone.nix
   ./services/web-servers/zope2.nix
   ./services/x11/desktop-managers/default.nix
   ./services/x11/display-managers/auto.nix
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index cff751934d7d..69a56ff69a0d 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -116,8 +116,9 @@ in
         # This file is read for all shells.
 
         # Only execute this file once per shell.
+        # But don't clobber the environment of interactive non-login children!
         if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
-        __ETC_ZSHENV_SOURCED=1
+        export __ETC_ZSHENV_SOURCED=1
 
         ${cfg.shellInit}
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index a26f930eda1d..72093aab5cd7 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -103,6 +103,7 @@ in zipModules ([]
 ++ obsolete [ "services" "sshd" "gatewayPorts" ] [ "services" "openssh" "gatewayPorts" ]
 ++ obsolete [ "services" "sshd" "permitRootLogin" ] [ "services" "openssh" "permitRootLogin" ]
 ++ obsolete [ "services" "xserver" "startSSHAgent" ] [ "services" "xserver" "startOpenSSHAgent" ]
+++ obsolete [ "services" "xserver" "windowManager" "xbmc" ] [ "services" "xserver" "desktopManager" "xbmc" ]
 
 # KDE
 ++ deprecated [ "kde" "extraPackages" ] [ "environment" "kdePackages" ]
diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix
new file mode 100644
index 000000000000..989bd13d101e
--- /dev/null
+++ b/nixos/modules/security/duosec.nix
@@ -0,0 +1,198 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.security.duosec;
+
+  boolToStr = b: if b then "yes" else "no";
+
+  configFile = ''
+    [duo]
+    ikey=${cfg.ikey}
+    skey=${cfg.skey}
+    host=${cfg.host}
+    ${optionalString (cfg.group != "") ("group="+cfg.group)}
+    failmode=${cfg.failmode}
+    pushinfo=${boolToStr cfg.pushinfo}
+    autopush=${boolToStr cfg.autopush}
+    motd=${boolToStr cfg.motd}
+    prompts=${toString cfg.prompts}
+    accept_env_factor=${boolToStr cfg.acceptEnvFactor}
+    fallback_local_ip=${boolToStr cfg.fallbackLocalIP}
+  '';
+
+  loginCfgFile = optional cfg.ssh.enable
+    { source = pkgs.writeText "login_duo.conf" configFile;
+      mode   = "0600";
+      uid    = config.ids.uids.sshd;
+      target = "duo/login_duo.conf";
+    };
+
+  pamCfgFile = optional cfg.pam.enable
+    { source = pkgs.writeText "pam_duo.conf" configFile;
+      mode   = "0600";
+      uid    = config.ids.uids.sshd;
+      target = "duo/pam_duo.conf";
+    };
+in
+{
+  options = {
+    security.duosec = {
+      ssh.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, protect SSH logins with Duo Security.";
+      };
+
+      pam.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "If enabled, protect logins with Duo Security using PAM support.";
+      };
+
+      ikey = mkOption {
+        type = types.str;
+        description = "Integration key.";
+      };
+
+      skey = mkOption {
+        type = types.str;
+        description = "Secret key.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        description = "Duo API hostname.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "";
+        description = "Use Duo authentication for users only in this group.";
+      };
+
+      failmode = mkOption {
+        type = types.str;
+        default = "safe";
+        description = ''
+          On service or configuration errors that prevent Duo
+          authentication, fail "safe" (allow access) or "secure" (deny
+          access). The default is "safe".
+        '';
+      };
+
+      pushinfo = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Include information such as the command to be executed in
+          the Duo Push message.
+        '';
+      };
+
+      autopush = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If <literal>true</literal>, Duo Unix will automatically send
+          a push login request to the user’s phone, falling back on a
+          phone call if push is unavailable. If
+          <literal>false</literal>, the user will be prompted to
+          choose an authentication method. When configured with
+          <literal>autopush = yes</literal>, we recommend setting
+          <literal>prompts = 1</literal>.
+        '';
+      };
+
+      motd = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Print the contents of <literal>/etc/motd</literal> to screen
+          after a succesful login.
+        '';
+      };
+
+      prompts = mkOption {
+        type = types.int;
+        default = 3;
+        description = ''
+          If a user fails to authenticate with a second factor, Duo
+          Unix will prompt the user to authenticate again. This option
+          sets the maximum number of prompts that Duo Unix will
+          display before denying access. Must be 1, 2, or 3. Default
+          is 3.
+
+          For example, when <literal>prompts = 1</literal>, the user
+          will have to successfully authenticate on the first prompt,
+          whereas if <literal>prompts = 2</literal>, if the user
+          enters incorrect information at the initial prompt, he/she
+          will be prompted to authenticate again.
+
+          When configured with <literal>autopush = true</literal>, we
+          recommend setting <literal>prompts = 1</literal>.
+        '';
+      };
+
+      acceptEnvFactor = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Look for factor selection or passcode in the
+          <literal>$DUO_PASSCODE</literal> environment variable before
+          prompting the user for input.
+
+          When $DUO_PASSCODE is non-empty, it will override
+          autopush. The SSH client will need SendEnv DUO_PASSCODE in
+          its configuration, and the SSH server will similarily need
+          AcceptEnv DUO_PASSCODE.
+        '';
+      };
+
+      fallbackLocalIP = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Duo Unix reports the IP address of the authorizing user, for
+          the purposes of authorization and whitelisting. If Duo Unix
+          cannot detect the IP address of the client, setting
+          <literal>fallbackLocalIP = yes</literal> will cause Duo Unix
+          to send the IP address of the server it is running on.
+
+          If you are using IP whitelisting, enabling this option could
+          cause unauthorized logins if the local IP is listed in the
+          whitelist.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
+    assertions =
+      [ { assertion = cfg.failmode == "safe" || cfg.failmode == "secure";
+          message   = "Invalid value for failmode (must be safe or secure).";
+        }
+        { assertion = cfg.prompts == 1 || cfg.prompts == 2 || cfg.prompts == 3;
+          message   = "Invalid value for prompts (must be 1, 2, or 3).";
+        }
+        { assertion = !cfg.pam.enable;
+          message   = "PAM support is currently not implemented.";
+        }
+      ];
+
+     environment.systemPackages = [ pkgs.duo-unix ];
+     security.setuidPrograms    = [ "login_duo" ];
+     environment.etc = loginCfgFile ++ pamCfgFile;
+
+     /* If PAM *and* SSH are enabled, then don't do anything special.
+     If PAM isn't used, set the default SSH-only options. */
+     services.openssh.extraConfig = mkIf (cfg.ssh.enable || cfg.pam.enable) (
+     if cfg.pam.enable then "UseDNS no" else ''
+       # Duo Security configuration
+       ForceCommand ${config.security.wrapperDir}/login_duo
+       PermitTunnel no
+       AllowTcpForwarding no
+     '');
+  };
+}
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
index 03fbd29a191d..ac8f008069a6 100644
--- a/nixos/modules/services/backup/tarsnap.nix
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -33,8 +33,9 @@ in
           service called <literal>tarsnap-backup</literal> which is
           periodically run by cron, or you may run it on-demand.
 
-          See <link xlink:href='http://www.tarsnap.com/gettingstarted.html'>Getting Started</link> 
-          Tarsnap page.
+          See the Tarsnap <link
+          xlink:href='http://www.tarsnap.com/gettingstarted.html'>Getting
+          Started</link> page.
         '';
       };
 
@@ -46,7 +47,7 @@ in
           full name will be
           <literal>label-$(date+"%Y%m%d%H%M%S")</literal>. For
           example, by default your backups will look similar to
-          <literal>nixos-20140301021501</literal>.
+          <literal>nixos-20140301011501</literal>.
         '';
       };
 
@@ -110,8 +111,8 @@ in
         default = "15 01 * * *";
         description = ''
           This option defines (in the format used by cron) when
-          tarsnap is run for backups.  The default is to update at
-          01:15 at night every day.
+          tarsnap is run for backups. The default is to backup the
+          specified paths at 01:15 at night every day.
         '';
       };
 
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
new file mode 100644
index 000000000000..c3dc59a9fbd0
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  cfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkins = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the jenkins continuous integration server.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins server should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins server should execute under.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+
+      port = mkOption {
+        default = 8080;
+        type = types.uniq types.int;
+        description = ''
+          Specifies port number on which the jenkins HTTP interface listens. The default is 8080
+        '';
+      };
+
+      packages = mkOption {
+        default = [ pkgs.stdenv pkgs.git pkgs.jdk pkgs.openssh pkgs.nix ];
+        type = types.listOf types.package;
+        description = ''
+          Packages to add to PATH for the jenkins process.
+        '';
+      };
+
+      environment = mkOption {
+        default = { NIX_REMOTE = "daemon"; };
+        type = with types; attrsOf string;
+        description = ''
+          Additional environment variables to be passed to the jenkins process.
+          The environment will always include JENKINS_HOME.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+
+    systemd.services.jenkins = {
+      description = "Jenkins Continuous Integration Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        JENKINS_HOME = cfg.home;
+      } // cfg.environment;
+
+      path = cfg.packages;
+
+      script = ''
+        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpPort=${toString cfg.port}
+      '';
+
+      postStart = ''
+        until ${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port} ; do
+          sleep 10
+        done
+        while true ; do
+          index=`${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port}`
+          if [[ !("$index" =~ 'Please wait while Jenkins is restarting' ||
+                  "$index" =~ 'Please wait while Jenkins is getting ready to work') ]]; then
+            exit 0
+          fi
+          sleep 30
+        done
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/slave.nix b/nixos/modules/services/continuous-integration/jenkins/slave.nix
new file mode 100644
index 000000000000..1d31ab830f6c
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/slave.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  cfg = config.services.jenkinsSlave;
+  masterCfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkinsSlave = {
+      # todo:
+      # * assure the profile of the jenkins user has a JRE and any specified packages. This would
+      # enable ssh slaves.
+      # * Optionally configure the node as a jenkins ad-hoc slave. This would imply configuration
+      # properties for the master node.
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If true the system will be configured to work as a jenkins slave.
+          If the system is also configured to work as a jenkins master then this has no effect.
+          In progress: Currently only assures the jenkins user is configured.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins slave agent should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins slave agent should execute under.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.enable && !masterCfg.enable) {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix
index 01631e9879a2..b48d3a64767f 100644
--- a/nixos/modules/services/databases/couchdb.nix
+++ b/nixos/modules/services/databases/couchdb.nix
@@ -1,9 +1,7 @@
 { config, pkgs, ... }:
-
-with pkgs.lib;
+  with pkgs.lib;
 
 let
-
   cfg = config.services.couchdb;
   configFile = pkgs.writeText "couchdb.ini"
     ''
@@ -19,10 +17,8 @@ let
       [log]
       file = ${cfg.logFile}
     '';
-  configExtraFile = pkgs.writeText "couchdb-extra.ini" cfg.extraConfig;
 
-in
-{
+in {
 
   ###### interface
 
@@ -64,14 +60,6 @@ in
         '';
       };
 
-      pidFile = mkOption {
-        type = types.path;
-        default = "/var/run/couchdb/couchdb.pid";
-        description = ''
-          pid file.
-        '';
-      };
-
       # couchdb options: http://docs.couchdb.org/en/latest/config/index.html
 
       databaseDir = mkOption {
@@ -153,26 +141,23 @@ in
 
       preStart =
         ''
-        mkdir -p `dirname ${cfg.pidFile}`;
         mkdir -p `dirname ${cfg.uriFile}`;
         mkdir -p `dirname ${cfg.logFile}`;
-        touch ${cfg.logFile};
         mkdir -p ${cfg.databaseDir};
         mkdir -p ${cfg.viewIndexDir};
-        chown ${cfg.user}:${cfg.group} `dirname ${cfg.pidFile}`
-        chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`
-        chown ${cfg.user}:${cfg.group} ${cfg.logFile}
-        chown ${cfg.user}:${cfg.group} ${cfg.databaseDir}
-        chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir}
+
+        if [ "$(id -u)" = 0 ]; then
+          chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`
+          chown ${cfg.user}:${cfg.group} ${cfg.databaseDir}
+          chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir}
+        fi
         '';
 
       serviceConfig = {
         PermissionsStartOnly = true;
         User = cfg.user;
         Group = cfg.group;
-        Type = "forking";
-        ExecStart = "${cfg.package}/bin/couchdb -b -o /dev/null -e /dev/null -p ${cfg.pidFile} -a ${configFile} -a ${configExtraFile}";
-        ExecStop = "${cfg.package}/bin/couchdb -d";
+        ExecStart = "${cfg.package}/bin/couchdb -a ${configFile} -a ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig}";
       };
     };
 
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 62fbc2a1415b..44fb0062f928 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -15,7 +15,7 @@ let
   pidFile = "${cfg.pidDir}/mysqld.pid";
 
   mysqldOptions =
-    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
+    "--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${mysql} " +
     "--pid-file=${pidFile}";
 
   myCnf = pkgs.writeText "my.cnf"
@@ -250,9 +250,6 @@ in
               rm /tmp/mysql_init
             fi
           ''; # */
-
-        serviceConfig.ExecStop =
-          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
       };
 
   };
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index cc4230d4d6a7..a83b1a110fed 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -190,7 +190,7 @@ in
                 mkdir -m 0700 -p ${cfg.dataDir}
                 if [ "$(id -u)" = 0 ]; then
                   chown -R postgres ${cfg.dataDir}
-                  su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root'
+                  su -s ${pkgs.stdenv.shell} postgres -c initdb
                 else
                   # For non-root operation.
                   initdb
diff --git a/nixos/modules/services/misc/gpsd.nix b/nixos/modules/services/misc/gpsd.nix
index bc1d1f4575a8..99b733d399e2 100644
--- a/nixos/modules/services/misc/gpsd.nix
+++ b/nixos/modules/services/misc/gpsd.nix
@@ -19,6 +19,7 @@ in
     services.gpsd = {
 
       enable = mkOption {
+        type = types.bool;
         default = false;
         description = ''
           Whether to enable `gpsd', a GPS service daemon.
@@ -26,6 +27,7 @@ in
       };
 
       device = mkOption {
+        type = types.str;
         default = "/dev/ttyUSB0";
         description = ''
           A device may be a local serial device for GPS input, or a URL of the form:
@@ -35,6 +37,7 @@ in
       };
 
       readonly = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to enable the broken-device-safety, otherwise
@@ -51,6 +54,7 @@ in
       };
 
       port = mkOption {
+        type = types.uniq types.int;
         default = 2947;
         description = ''
           The port where to listen for TCP connections.
@@ -58,6 +62,7 @@ in
       };
 
       debugLevel = mkOption {
+        type = types.uniq types.int;
         default = 0;
         description = ''
           The debugging level.
@@ -85,19 +90,20 @@ in
         inherit gid;
       };
 
-    jobs.gpsd =
-      { description = "GPSD daemon";
-
-        startOn = "ip-up";
-
-        exec =
-          ''
-            ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}"  \
-              -S "${toString cfg.port}"                             \
-              ${if cfg.readonly then "-b" else ""}                  \
-              "${cfg.device}"
-          '';
+    systemd.services.gpsd = {
+      description = "GPSD daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = ''
+          ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}"  \
+            -S "${toString cfg.port}"                             \
+            ${if cfg.readonly then "-b" else ""}                  \
+            "${cfg.device}"
+        '';
       };
+    };
 
   };
 
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 9a5b170d5e66..1a8b85db3290 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -63,7 +63,7 @@ in
 
     services.nixosManual.showManual = mkOption {
       type = types.bool;
-      default = true;
+      default = false;
       description = ''
         Whether to show the NixOS manual on one of the virtual
         consoles.
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 58ec8a49694b..38fd53653d62 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -168,11 +168,11 @@ in
     # shuts off power.) Copied from here:
     # http://forums.opensuse.org/english/get-technical-help-here/applications/479499-apcupsd-systemd-killpower-issues.html
     systemd.services.apcupsd-killpower = {
+      description = "APC UPS Kill Power";
       after = [ "shutdown.target" ]; # append umount.target?
       before = [ "final.target" ];
       wantedBy = [ "shutdown.target" ];
       unitConfig = {
-        Description = "APC UPS Kill Power";
         ConditionPathExists = "/run/apcupsd/powerfail";
         DefaultDependencies = "no";
       };
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index be57b8c5c03f..3e2e7e9df6ef 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -4,17 +4,31 @@ with pkgs.lib;
 
 let
   cfg = config.services.graphite;
-  writeTextOrNull = f: t: if t == null then null else pkgs.writeText f t;
+  writeTextOrNull = f: t: if t == null then null else pkgs.writeTextDir f t;
+
+  dataDir = cfg.dataDir;
+
+  configDir = pkgs.buildEnv {
+    name = "graphite-config";
+    paths = lists.filter (el: el != null) [
+      (writeTextOrNull "carbon.conf" cfg.carbon.config)
+      (writeTextOrNull "storage-agregation.conf" cfg.carbon.storageAggregation)
+      (writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas)
+      (writeTextOrNull "blacklist.conf" cfg.carbon.blacklist)
+      (writeTextOrNull "whitelist.conf" cfg.carbon.whitelist)
+      (writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules)
+      (writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules)
+      (writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules)
+    ];
+  };
 
-  dataDir = "/var/db/graphite";
   carbonOpts = name: with config.ids; ''
-    --nodaemon --syslog --prefix=${name} --pidfile /var/run/${name}.pid \
-    --uid ${toString uids.graphite} --gid ${toString uids.graphite} ${name}
+    --nodaemon --syslog --prefix=${name} --pidfile ${dataDir}/${name}.pid ${name}
   '';
   carbonEnv = {
     PYTHONPATH = "${pkgs.python27Packages.carbon}/lib/python2.7/site-packages";
     GRAPHITE_ROOT = dataDir;
-    GRAPHITE_CONF_DIR = "/etc/graphite/";
+    GRAPHITE_CONF_DIR = configDir;
     GRAPHITE_STORAGE_DIR = dataDir;
   };
 
@@ -23,6 +37,14 @@ in {
   ###### interface
 
   options.services.graphite = {
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/db/graphite";
+      description = ''
+        Data directory for graphite.
+      '';
+    };
+
     web = {
       enable = mkOption {
         description = "Whether to enable graphite web frontend";
@@ -38,8 +60,8 @@ in {
 
       port = mkOption {
         description = "Graphite web frontend port";
-        default = "8080";
-        type = types.str;
+        default = 8080;
+        type = types.int;
       };
     };
 
@@ -152,31 +174,17 @@ in {
   ###### implementation
 
   config = mkIf (cfg.carbon.enableAggregator || cfg.carbon.enableCache || cfg.carbon.enableRelay || cfg.web.enable) {
-    environment.etc = lists.filter (el: el.source != null) [
-      { source = writeTextOrNull "carbon.conf" cfg.carbon.config;
-        target = "graphite/carbon.conf"; }
-      { source = writeTextOrNull "storage-agregation.conf" cfg.carbon.storageAggregation;
-        target = "graphite/storage-agregation.conf"; }
-      { source = writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas;
-        target = "graphite/storage-schemas.conf"; }
-      { source = writeTextOrNull "blacklist.conf" cfg.carbon.blacklist;
-        target = "graphite/blacklist.conf"; }
-      { source = writeTextOrNull "whitelist.conf" cfg.carbon.whitelist;
-        target = "graphite/whitelist.conf"; }
-      { source = writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules;
-        target = "graphite/rewrite-rules.conf"; }
-      { source = writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules;
-        target = "graphite/relay-rules.conf"; }
-      { source = writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules;
-        target = "graphite/aggregation-rules.conf"; }
-    ];
-
-    systemd.services.carbonCache = mkIf cfg.carbon.enableCache {
+    systemd.services.carbonCache = {
+      enable = cfg.carbon.enableCache;
       description = "Graphite Data Storage Backend";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-cache"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-cache"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon
         cfg.carbon.config
@@ -185,33 +193,45 @@ in {
         cfg.carbon.rewriteRules
       ];
       preStart = ''
-        mkdir -p ${dataDir}/whisper
+        mkdir -m 0700 -p ${cfg.dataDir}/whisper
+        if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
       '';
     };
 
-    systemd.services.carbonAggregator = mkIf cfg.carbon.enableAggregator {
+    systemd.services.carbonAggregator = {
+      enable = cfg.carbon.enableAggregator;
       description = "Carbon Data Aggregator";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-aggregator"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-aggregator"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.aggregationRules
       ];
     };
 
-    systemd.services.carbonRelay = mkIf cfg.carbon.enableRelay {
+    systemd.services.carbonRelay = {
+      enable = cfg.carbon.enableRelay;
       description = "Carbon Data Relay";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
       environment = carbonEnv;
-      serviceConfig.ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-relay"}";
+      serviceConfig = {
+        ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-relay"}";
+        User = "graphite";
+        Group = "graphite";
+      };
       restartTriggers = [
         pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.relayRules
       ];
     };
 
-    systemd.services.graphiteWeb = mkIf cfg.web.enable {
+    systemd.services.graphiteWeb = {
+      enable = cfg.web.enable;
       description = "Graphite Web Interface";
       wantedBy = [ "multi-user.target" ];
       after = [ "network-interfaces.target" ];
@@ -224,14 +244,15 @@ in {
       serviceConfig = {
         ExecStart = ''
           ${pkgs.python27Packages.waitress}/bin/waitress-serve \
-          --host=${cfg.web.host} --port=${cfg.web.port} \
+          --host=${cfg.web.host} --port=${toString cfg.web.port} \
           --call django.core.handlers.wsgi:WSGIHandler'';
         User = "graphite";
         Group = "graphite";
       };
       preStart = ''
         if ! test -e ${dataDir}/db-created; then
-          mkdir -p ${dataDir}/{whisper/,log/webapp/}
+          mkdir -m 0700 -p ${dataDir}/{whisper/,log/webapp/}
+          if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
 
           # populate database
           ${pkgs.python27Packages.graphite_web}/bin/manage-graphite.py syncdb --noinput
@@ -259,7 +280,6 @@ in {
       uid = config.ids.uids.graphite;
       description = "Graphite daemon user";
       home = dataDir;
-      createHome = true;
     };
     users.extraGroups.graphite.gid = config.ids.gids.graphite;
   };
diff --git a/nixos/modules/services/monitoring/statsd.nix b/nixos/modules/services/monitoring/statsd.nix
index 979debefdd9f..54b7e79f82eb 100644
--- a/nixos/modules/services/monitoring/statsd.nix
+++ b/nixos/modules/services/monitoring/statsd.nix
@@ -64,13 +64,13 @@ in
 
     graphiteHost = mkOption {
       description = "Hostname or IP of Graphite server";
-      default = "127.0.0.1";
+      default = config.services.graphite.web.host;
       type = types.str;
     };
 
     graphitePort = mkOption {
       description = "Port of Graphite server";
-      default = 2003;
+      default = config.services.graphite.web.port;
       type = types.uniq types.int;
     };
 
diff --git a/nixos/modules/services/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix
index 4a888b64bd34..b34ebc3663e3 100644
--- a/nixos/modules/services/network-filesystems/openafs-client/default.nix
+++ b/nixos/modules/services/network-filesystems/openafs-client/default.nix
@@ -46,6 +46,16 @@ in
         description = "Cache directory.";
       };
 
+      crypt = mkOption {
+        default = false;
+        description = "Whether to enable (weak) protocol encryption.";
+      };
+
+      sparse = mkOption {
+        default = false;
+        description = "Minimal cell list in /afs.";
+      };
+
     };
   };
 
@@ -70,18 +80,23 @@ in
         startOn = "started network-interfaces";
         stopOn = "stopping network-interfaces";
 
-	preStart = ''
-	  mkdir -m 0755 /afs || true
-	  mkdir -m 0755 -p ${cfg.cacheDirectory} || true
+        preStart = ''
+          mkdir -p -m 0755 /afs
+          mkdir -m 0700 -p ${cfg.cacheDirectory}
           ${pkgs.module_init_tools}/sbin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true
-          ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} -dynroot -fakestat
-	'';
-
-	postStop = ''
-	  umount /afs
+          ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} -fakestat -afsdb
+          ${openafsPkgs}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
+        '';
+
+        # Doing this in preStop, because after these commands AFS is basically
+        # stopped, so systemd has nothing to do, just noticing it.  If done in
+        # postStop, then we get a hang + kernel oops, because AFS can't be
+        # stopped simply by sending signals to processes.
+        preStop = ''
+          ${pkgs.utillinux}/bin/umount /afs
           ${openafsPkgs}/sbin/afsd -shutdown
-	  rmmod libafs
-	'';
+          ${pkgs.module_init_tools}/sbin/rmmod libafs
+        '';
 
       };
 
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index babde3e942b3..07e05fa6d051 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -171,6 +171,17 @@ in
         '';
     };
 
+    networking.firewall.pingLimit = mkOption {
+      default = null;
+      type = types.nullOr (types.separatedString " ");
+      description =
+        ''
+          If pings are allowed, this allows setting rate limits
+          on them. If non-null, this option should be in the form
+          of flags like "-limit 1/minute -limit-burst 5"
+        '';
+    };
+
     networking.firewall.checkReversePath = mkOption {
       default = kernelHasRPFilter;
       type = types.bool;
@@ -375,7 +386,9 @@ in
 
             # Optionally respond to ICMPv4 pings.
             ${optionalString cfg.allowPing ''
-              iptables -A nixos-fw -p icmp --icmp-type echo-request -j nixos-fw-accept
+              iptables -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
+                "-m limit ${cfg.pingLimit} "
+              }-j nixos-fw-accept
             ''}
 
             # Accept all ICMPv6 messages except redirects and node
diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix
index 76dd66013ba7..164c38940f77 100644
--- a/nixos/modules/services/networking/kippo.nix
+++ b/nixos/modules/services/networking/kippo.nix
@@ -76,8 +76,9 @@ rec {
     users.extraUsers = singleton {
       name = "kippo";
       description = "kippo web server privilege separation user";
+      uid = 108; # why does config.ids.uids.kippo give an error?
     };
-    users.extraGroups = singleton { name = "kippo"; };
+    users.extraGroups = singleton { name = "kippo";gid=108; };
 
     systemd.services.kippo = with pkgs; {
       description = "Kippo Web Server";
diff --git a/nixos/modules/services/networking/notbit.nix b/nixos/modules/services/networking/notbit.nix
new file mode 100644
index 000000000000..83dafd083791
--- /dev/null
+++ b/nixos/modules/services/networking/notbit.nix
@@ -0,0 +1,93 @@
+{ config, pkgs, ... }:
+
+let
+  cfg = config.services.notbit;
+  varDir = "/var/lib/notbit";
+  
+  sendmail = pkgs.stdenv.mkDerivation {
+    name = "notbit-wrapper";
+    buildInputs = [ pkgs.makeWrapper ];
+    propagatedBuildInputs = [ pkgs.notbit ];
+    buildCommand = ''
+      mkdir -p $out/bin
+      makeWrapper ${pkgs.notbit}/bin/notbit-sendmail $out/bin/notbit-system-sendmail \
+        --set XDG_RUNTIME_DIR ${varDir}
+    '';
+  };
+in
+
+with pkgs.lib;
+{
+
+  ### configuration
+
+  options = {
+
+    services.notbit = {
+
+      enable = mkOption {
+        type = types.uniq types.bool;
+        default = false;
+        description = ''
+          Enables the notbit daemon and provides a sendmail binary named `notbit-system-sendmail` for sending mail over the system instance of notbit. Users must be in the notbit group in order to send mail over the system notbit instance. Currently mail recipt is not supported.
+        '';
+      };
+
+      port = mkOption {
+        type = types.uniq types.int;
+        default = 8443;
+        description = "The port which the daemon listens for other bitmessage clients";
+      };
+
+      nice = mkOption {
+        type = types.uniq types.int;
+        default = 10;
+        description = "Set the nice level for the notbit daemon";
+      };
+
+    };
+
+  };
+
+  ### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.notbit sendmail ];
+
+    systemd.services.notbit = {
+      description = "Notbit daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.notbit ];
+      environment = { XDG_RUNTIME_DIR = varDir; };
+
+      postStart = ''
+        [ ! -f "${varDir}/addr" ] && notbit-keygen > ${varDir}/addr
+        chmod 0640 ${varDir}/{addr,notbit/notbit-ipc.lock}
+        chmod 0750 ${varDir}/notbit/{,notbit-ipc}
+      '';
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.notbit}/bin/notbit -d -p ${toString cfg.port}";
+        User = "notbit";
+        Group = "notbit";
+        UMask = "0077";
+        WorkingDirectory = varDir;
+        Nice = cfg.nice;
+      };
+    };
+
+    users.extraUsers.notbit = {
+      group = "notbit";
+      description = "Notbit daemon user";
+      home = varDir;
+      createHome = true;
+      uid = config.ids.uids.notbit;
+    };
+
+    users.extraGroups.notbit.gid = config.ids.gids.notbit;
+  };
+
+}
diff --git a/nixos/modules/services/search/solr.nix b/nixos/modules/services/search/solr.nix
new file mode 100644
index 000000000000..eab18c4229b7
--- /dev/null
+++ b/nixos/modules/services/search/solr.nix
@@ -0,0 +1,114 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.solr;
+
+in {
+
+  options = {
+    services.solr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables the solr service.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running solr.
+        '';
+      };
+
+      solrPackage = mkOption {
+        type = types.package;
+        default = pkgs.solr;
+        description = ''
+          Which solr derivation to use for running solr.
+        '';
+      };
+
+      log4jConfiguration = mkOption {
+        type = types.lines;
+        default = ''
+          log4j.rootLogger=INFO, stdout
+          log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+          log4j.appender.stdout.Target=System.out
+          log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+          log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+        '';
+        description = ''
+          Contents of the <literal>log4j.properties</literal> used. By default,
+          everything is logged to stdout (picked up by systemd) with level INFO.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run the solr process and.
+          the working directories.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      solrHome = mkOption {
+        type = types.str;
+        description = ''
+          The solr home directory. It is your own responsibility to
+          make sure this directory contains a working solr configuration,
+          and is writeable by the the user running the solr service.
+          Failing to do so, the solr will not start properly.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          solr.
+        '';
+      };
+
+      extraWinstoneOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone, which is
+          the servlet container hosting solr.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.winstone.solr = {
+      serviceName = "solr";
+      inherit (cfg) user group javaPackage;
+      warFile = "${cfg.solrPackage}/lib/solr.war";
+      extraOptions = [
+        "--commonLibFolder=${cfg.solrPackage}/lib/ext"
+      ] ++ cfg.extraWinstoneOptions;
+      extraJavaOptions = [
+        "-Dsolr.solr.home=${cfg.solrHome}"
+        "-Dlog4j.configuration=file://${pkgs.writeText "log4j.properties" cfg.log4jConfiguration}"
+      ] ++ cfg.extraJavaOptions;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index 211008fa7e41..ca4fbeb0add3 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -48,82 +48,25 @@ with pkgs.lib;
 
   config = {
 
-    # FIXME: these are mostly copy/pasted from the systemd sources,
-    # which some small modifications, which is annoying.
-
-    # Generate a separate job for each tty.
-    systemd.units."getty@.service".text =
-      ''
-        [Unit]
-        Description=Getty on %I
-        Documentation=man:agetty(8)
-        After=systemd-user-sessions.service plymouth-quit-wait.service
-
-        # If additional gettys are spawned during boot then we should make
-        # sure that this is synchronized before getty.target, even though
-        # getty.target didn't actually pull it in.
-        Before=getty.target
-        IgnoreOnIsolate=yes
-
-        ConditionPathExists=/dev/tty0
-
-        [Service]
-        Environment=TERM=linux
-        Environment=LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive
-        ExecStart=@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        TTYVTDisallocate=yes # set to no to prevent clearing the screen
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-
-        X-RestartIfChanged=false
-      '';
-    
-    systemd.units."serial-getty@.service".text = let
-        speeds = with pkgs.lib; concatStringsSep "," (map toString config.services.mingetty.serialSpeed);
-      in ''
-        [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 ${speeds}
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-        
-        X-RestartIfChanged=false
-      '';
+    systemd.services."getty@" =
+      { baseUnit = pkgs.runCommand "getty.service" {}
+          ''
+            sed '/ExecStart/ d' < ${config.systemd.package}/example/systemd/system/getty@.service > $out
+          '';
+        serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400";
+        restartIfChanged = false;
+      };
+
+    systemd.services."serial-getty@" =
+      { baseUnit = pkgs.runCommand "serial-getty.service" {}
+          ''
+            sed '/ExecStart/ d' < ${config.systemd.package}/example/systemd/system/serial-getty@.service > $out
+          '';
+        serviceConfig.ExecStart =
+          let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed);
+          in "@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I ${speeds}";
+        restartIfChanged = false;
+      };
 
     environment.etc = singleton
       { # Friendly greeting on the virtual consoles.
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index eb68a3d95d8f..70555e5d8825 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -44,6 +44,7 @@ in {
       After=systemd-user-sessions.service
       After=plymouth-quit-wait.service
       After=systemd-logind.service
+      After=systemd-vconsole-setup.service
       Requires=systemd-logind.service
       Before=getty.target
       Conflicts=getty@%i.service
@@ -62,11 +63,13 @@ in {
       X-RestartIfChanged=false
     '';
 
-    systemd.units."autovt@.service".linkTarget = "${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service";
-
-    systemd.services."systemd-vconsole-setup".restartIfChanged = false;
+    systemd.units."autovt@.service".unit = pkgs.runCommand "unit" { }
+        ''
+          mkdir -p $out
+          ln -s ${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service $out/autovt@.service
+        '';
 
-    systemd.units."kmsconvt@tty1.service".extraConfig.wait-for-vconsole-setup = "After=systemd-vconsole-setup.service";
+    systemd.services.systemd-vconsole-setup.restartIfChanged = false;
 
     services.kmscon.extraConfig = mkIf cfg.hwRender ''
       drm
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 1a39fe43bbee..621536133554 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -9,6 +9,11 @@ let
     user ${cfg.user} ${cfg.group};
     daemon off;
     ${cfg.config}
+    ${optionalString (cfg.httpConfig != "") ''
+    http {
+      ${cfg.httpConfig}
+    }
+    ''}
     ${cfg.appendConfig}
   '';
 in
@@ -51,6 +56,12 @@ in
         '';
       };
 
+      httpConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Configuration lines to be appended inside of the http {} block.";
+      };
+
       stateDir = mkOption {
         default = "/var/spool/nginx";
         description = "
diff --git a/nixos/modules/services/web-servers/phpfpm.nix b/nixos/modules/services/web-servers/phpfpm.nix
new file mode 100644
index 000000000000..76ec41244627
--- /dev/null
+++ b/nixos/modules/services/web-servers/phpfpm.nix
@@ -0,0 +1,77 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.phpfpm;
+
+  stateDir = "/run/phpfpm";
+
+  pidFile = "${stateDir}/phpfpm.pid";
+
+  cfgFile = pkgs.writeText "phpfpm.conf" ''
+    [global]
+    pid = ${pidFile}
+    error_log = syslog
+    daemonize = yes
+    ${cfg.extraConfig}
+
+    ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
+  '';
+
+in {
+
+  options = {
+    services.phpfpm = {
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration that should be put in the global section of
+          the PHP FPM configuration file. Do not specify the options
+          <literal>pid</literal>, <literal>error_log</literal> or
+          <literal>daemonize</literal> here, since they are generated by
+          NixOS.
+        '';
+      };
+
+      poolConfigs = mkOption {
+        type = types.attrsOf types.lines;
+        default = {};
+        example = {
+          mypool = ''
+            listen = /run/phpfpm/mypool
+            user = nobody
+            pm = dynamic
+            pm.max_children = 75
+            pm.start_servers = 10
+            pm.min_spare_servers = 5
+            pm.max_spare_servers = 20
+            pm.max_requests = 500
+          '';
+        };
+        description = ''
+          A mapping between PHP FPM pool names and their configurations.
+          See the documentation on <literal>php-fpm.conf</literal> for
+          details on configuration directives. If no pools are defined,
+          the phpfpm service is disabled.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.poolConfigs != {}) {
+
+    systemd.services.phpfpm = {
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p "${stateDir}"
+      '';
+      serviceConfig = {
+        ExecStart = "${pkgs.php54}/sbin/php-fpm -y ${cfgFile}";
+        PIDFile = pidFile;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix
new file mode 100644
index 000000000000..33c7e7301182
--- /dev/null
+++ b/nixos/modules/services/web-servers/winstone.nix
@@ -0,0 +1,129 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.winstone;
+
+  winstoneOpts = { name, ... }: {
+    options = {
+      name = mkOption {
+        default = name;
+        internal = true;
+      };
+
+      serviceName = mkOption {
+        type = types.str;
+        description = ''
+          The name of the systemd service. By default, it is
+          derived from the winstone instance name.
+        '';
+      };
+
+      warFile = mkOption {
+        type = types.str;
+        description = ''
+          The WAR file that Winstone should serve.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running Winstone.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run this Winstone process and
+          own the working directory.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      workDir = mkOption {
+        type = types.str;
+        description = ''
+          The working directory for this Winstone instance. Will
+          contain extracted webapps etc. The directory will be
+          created if it doesn't exist.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          Winstone.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone process.
+        '';
+      };
+    };
+
+    config = {
+      workDir = mkDefault "/run/winstone/${name}";
+      serviceName = mkDefault "winstone-${name}";
+    };
+  };
+
+  mkService = cfg: let
+    opts = concatStringsSep " " (cfg.extraOptions ++ [
+      "--warfile ${cfg.warFile}"
+    ]);
+
+    javaOpts = concatStringsSep " " (cfg.extraJavaOptions ++ [
+      "-Djava.io.tmpdir=${cfg.workDir}"
+      "-jar ${pkgs.winstone}/lib/winstone.jar"
+    ]);
+  in {
+    wantedBy = [ "multi-user.target" ];
+    description = "winstone service for ${cfg.name}";
+    preStart = ''
+      mkdir -p "${cfg.workDir}"
+      chown ${cfg.user}:${cfg.group} "${cfg.workDir}"
+    '';
+    serviceConfig = {
+      ExecStart = "${cfg.javaPackage}/bin/java ${javaOpts} ${opts}";
+      User = cfg.user;
+      PermissionsStartOnly = true;
+    };
+  };
+
+in {
+
+  options = {
+    services.winstone = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ winstoneOpts ];
+      description = ''
+        Defines independent Winstone services, each serving one WAR-file.
+      '';
+    };
+  };
+
+  config = mkIf (cfg != {}) {
+
+    systemd.services = mapAttrs' (n: c: nameValuePair c.serviceName (mkService c)) cfg;
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index dde5b333f329..753babf6a225 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -17,7 +17,7 @@ in
   # Note: the order in which desktop manager modules are imported here
   # determines the default: later modules (if enabled) are preferred.
   # E.g., if KDE is enabled, it supersedes xterm.
-  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ./gnome3.nix ];
+  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ./gnome3.nix ./xbmc.nix ];
 
   options = {
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
index 456975015a22..85be61bfc1e9 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome3.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -43,7 +43,7 @@ in {
         '';
       };
 
-    environment.variables.GIO_EXTRA_MODULES = "${gnome3.dconf}/lib/gio/modules";
+    environment.variables.GIO_EXTRA_MODULES = [ "${gnome3.dconf}/lib/gio/modules" ];
     environment.systemPackages =
       [ gnome3.evince
         gnome3.eog
diff --git a/nixos/modules/services/x11/desktop-managers/xbmc.nix b/nixos/modules/services/x11/desktop-managers/xbmc.nix
new file mode 100644
index 000000000000..51278c5fadb6
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/xbmc.nix
@@ -0,0 +1,31 @@
+{ pkgs, config, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.xserver.desktopManager.xbmc;
+in
+
+{
+  options = {
+    services.xserver.desktopManager.xbmc = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the xbmc multimedia center.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.desktopManager.session = [{
+      name = "xbmc";
+      start = ''
+        ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
+        waitPID=$!
+      '';
+    }];
+    
+    environment.systemPackages = [ pkgs.xbmc ];
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 3c67571ffd5b..9ce7744b0652 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -81,7 +81,7 @@ in
     environment.pathsToLink =
       [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
 
-    environment.variables.GIO_EXTRA_MODULES = "${pkgs.xfce.gvfs}/lib/gio/modules";
+    environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
 
     # Enable helpful DBus services.
     services.udisks2.enable = true;
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 4d52e398b477..1c10333462c8 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -16,7 +16,6 @@ in
       ./wmii.nix
       ./xmonad.nix
       ./i3.nix
-      ./xbmc.nix
       ./herbstluftwm.nix
     ];
 
diff --git a/nixos/modules/services/x11/window-managers/xbmc.nix b/nixos/modules/services/x11/window-managers/xbmc.nix
deleted file mode 100644
index 46494202b404..000000000000
--- a/nixos/modules/services/x11/window-managers/xbmc.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{pkgs, config, ...}:
-
-let
-  inherit (pkgs.lib) mkOption mkIf;
-  cfg = config.services.xserver.windowManager.xbmc;
-in
-
-{
-  options = {
-    services.xserver.windowManager.xbmc = {
-      enable = mkOption {
-        default = false;
-        example = true;
-        description = "Enable the xbmc multimedia center.";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    services.xserver.windowManager = {
-      session = [{
-        name = "xbmc";
-        start = "
-          ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
-          waitPID=$!
-        ";
-      }];
-    };
-    environment.systemPackages = [ pkgs.xbmc ];
-  };
-}
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index cb5b49692fe1..e0649448c834 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -128,7 +128,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
     $baseName =~ s/\.[a-z]*$//;
 
     if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
-        if (! -e $newUnitFile) {
+        if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
             push @unitsToStop, $unit;
         }
 
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 327f3b7e1128..4cfd8a0b6187 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -203,6 +203,9 @@ in
 
         # To wait for SCSI devices to appear.
         "scsi_wait_scan"
+
+        # Needed by the stage 2 init script.
+        "rtc_cmos"
       ];
 
     boot.initrd.kernelModules =
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index b749172a3ff3..ee042992b178 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -131,13 +131,12 @@ if ! mountpoint -q /run; then
     mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
 fi
 
-# Create a ramfs on /run/keys to hold secrets that shouldn't
-# be written to disk (generally used for nixops, harmless
-# elsehwere)
+# Create a ramfs on /run/keys to hold secrets that shouldn't be
+# written to disk (generally used for NixOps, harmless elsewhere).
 if ! mountpoint -q /run/keys; then
     rm -rf /run/keys
     mkdir -m 0750 /run/keys
-    chown root:keys /run/keys
+    chown 0:96 /run/keys
     mount -t ramfs none /run/keys
 fi
 
@@ -162,6 +161,12 @@ echo "running activation script..."
 $systemConfig/activate
 
 
+# Restore the system time from the hardware clock.  We do this after
+# running the activation script to be sure that /etc/localtime points
+# at the current time zone.
+hwclock --hctosys
+
+
 # Record the boot configuration.
 ln -sfn "$systemConfig" /run/booted-system
 
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index c0518599f17a..113990814efa 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -41,6 +41,12 @@ in rec {
       '';
     };
 
+    baseUnit = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "Path to an upstream unit file on which the NixOS unit configuration will be based.";
+    };
+
     description = mkOption {
       default = "";
       type = types.str;
@@ -135,6 +141,7 @@ in rec {
 
     restartTriggers = mkOption {
       default = [];
+      type = types.listOf types.unspecified;
       description = ''
         An arbitrary list of items such as derivations.  If any item
         in the list changes between reconfigurations, the service will
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 2d1bb5ae9802..49502b3e6851 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -11,20 +11,18 @@ let
   systemd = cfg.package;
 
   makeUnit = name: unit:
-    pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
-      ((if !unit.enable then  ''
-        mkdir -p $out
-        ln -s /dev/null $out/${name}
-      '' else if unit.linkTarget != null then ''
-        mkdir -p $out
-        ln -s ${unit.linkTarget} $out/${name}
-      '' else if unit.text != null then ''
-        mkdir -p $out
-        echo -n "$text" > $out/${name}
-      '' else "") + optionalString (unit.extraConfig != {}) ''
-        mkdir -p $out/${name}.d
-        ${concatStringsSep "\n" (mapAttrsToList (n: v: "echo -n \"${v}\" > $out/${name}.d/${n}") unit.extraConfig)}
-      '');
+    if unit.enable then
+      pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
+        ''
+          mkdir -p $out
+          echo -n "$text" > $out/${name}
+        ''
+    else
+      pkgs.runCommand "unit" { preferLocalBuild = true; }
+        ''
+          mkdir -p $out
+          ln -s /dev/null $out/${name}
+        '';
 
   upstreamUnits =
     [ # Targets.
@@ -132,6 +130,11 @@ let
     ++ optionals cfg.enableEmergencyMode [
       "emergency.target"
       "emergency.service"
+    ]
+
+    ++ optionals config.services.journald.enableHttpGateway [
+      "systemd-journal-gatewayd.socket"
+      "systemd-journal-gatewayd.service"
     ];
 
   upstreamWants =
@@ -151,15 +154,23 @@ let
   unitConfig = { name, config, ... }: {
     config = {
       unitConfig =
-        { Requires = concatStringsSep " " config.requires;
-          Wants = concatStringsSep " " config.wants;
-          After = concatStringsSep " " config.after;
-          Before = concatStringsSep " " config.before;
-          BindsTo = concatStringsSep " " config.bindsTo;
-          PartOf = concatStringsSep " " config.partOf;
-          Conflicts = concatStringsSep " " config.conflicts;
-          "X-Restart-Triggers" = toString config.restartTriggers;
-        } // optionalAttrs (config.description != "") {
+        optionalAttrs (config.requires != [])
+          { Requires = toString config.requires; }
+        // optionalAttrs (config.wants != [])
+          { Wants = toString config.wants; }
+        // optionalAttrs (config.after != [])
+          { After = toString config.after; }
+        // optionalAttrs (config.before != [])
+          { Before = toString config.before; }
+        // optionalAttrs (config.bindsTo != [])
+          { BindsTo = toString config.bindsTo; }
+        // optionalAttrs (config.partOf != [])
+          { PartOf = toString config.partOf; }
+        // optionalAttrs (config.conflicts != [])
+          { Conflicts = toString config.conflicts; }
+        // optionalAttrs (config.restartTriggers != [])
+          { X-Restart-Triggers = toString config.restartTriggers; }
+        // optionalAttrs (config.description != "") {
           Description = config.description;
         };
     };
@@ -167,7 +178,7 @@ let
 
   serviceConfig = { name, config, ... }: {
     config = mkMerge
-      [ { # Default path for systemd services.  Should be quite minimal.
+      [ (mkIf (config.baseUnit == null) { # Default path for systemd services.  Should be quite minimal.
           path =
             [ pkgs.coreutils
               pkgs.findutils
@@ -176,7 +187,7 @@ let
               systemd
             ];
           environment.PATH = config.path;
-        }
+        })
         (mkIf (config.preStart != "")
           { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
               #! ${pkgs.stdenv.shell} -e
@@ -244,6 +255,14 @@ let
         (if isList value then value else [value]))
         as));
 
+  commonUnitText = def:
+    optionalString (def.baseUnit != null) ''
+      .include ${def.baseUnit}
+    '' + ''
+      [Unit]
+      ${attrsToSection def.unitConfig}
+    '';
+
   targetToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
       text =
@@ -255,11 +274,8 @@ let
 
   serviceToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Service]
           ${let env = cfg.globalEnvironment // def.environment;
             in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
@@ -271,11 +287,8 @@ let
 
   socketToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Socket]
           ${attrsToSection def.socketConfig}
           ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
@@ -284,11 +297,8 @@ let
 
   timerToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Timer]
           ${attrsToSection def.timerConfig}
         '';
@@ -296,11 +306,8 @@ let
 
   mountToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Mount]
           ${attrsToSection def.mountConfig}
         '';
@@ -308,11 +315,8 @@ let
 
   automountToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Automount]
           ${attrsToSection def.automountConfig}
         '';
@@ -372,6 +376,10 @@ let
 
       ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
             ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
+
+      ${ optionalString config.services.journald.enableHttpGateway ''
+      ln -s ../systemd-journal-gatewayd.service $out/multi-user-target.wants/
+      ''}
     ''; # */
 
 in
@@ -423,25 +431,9 @@ in
               internal = true;
               description = "The generated unit.";
             };
-            linkTarget = mkOption {
-              default = null;
-              description = "The file to symlink this target to.";
-              type = types.nullOr types.path;
-            };
-            extraConfig = mkOption {
-              default = {};
-              example = { "foo@1.conf" = "X-RestartIfChanged=false"; };
-              type = types.attrsOf types.lines;
-              description = ''
-                Extra files to be appended to the configuration for the unit.
-                This can be used to override configuration for a unit provided
-                by systemd or another package, or to override only a single instance
-                of a template unit.
-              '';
-            };
           };
           config = {
-            unit = makeUnit name config;
+            unit = mkDefault (makeUnit name config);
           };
         };
     };
@@ -567,6 +559,14 @@ in
       '';
     };
 
+    services.journald.enableHttpGateway = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Enable journal http gateway
+      '';
+    };
+
     services.logind.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -676,6 +676,8 @@ in
       };
 
     users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
+    users.extraUsers.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
+    users.extraGroups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
 
     # Generate timer units for all services that have a ‘startAt’ value.
     systemd.timers =
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index a8f0a59b6fa9..e9c03df2ba34 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -19,6 +19,8 @@ let
     sources = map (x: x.source) etc';
     targets = map (x: x.target) etc';
     modes = map (x: x.mode) etc';
+    uids  = map (x: x.uid) etc';
+    gids  = map (x: x.gid) etc';
   };
 
 in
@@ -87,6 +89,24 @@ in
               '';
             };
 
+            uid = mkOption {
+              default = 0;
+              type = types.int;
+              description = ''
+                UID of created file. Only takes affect when the file is
+                copied (that is, the mode is not 'symlink').
+                '';
+            };
+
+            gid = mkOption {
+              default = 0;
+              type = types.int;
+              description = ''
+                GID of created file. Only takes affect when the file is
+                copied (that is, the mode is not 'symlink').
+              '';
+            };
+
           };
 
           config = {
diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh
index 7cf68db9ddce..60d4ba1301a3 100644
--- a/nixos/modules/system/etc/make-etc.sh
+++ b/nixos/modules/system/etc/make-etc.sh
@@ -6,6 +6,8 @@ set -f
 sources_=($sources)
 targets_=($targets)
 modes_=($modes)
+uids_=($uids)
+gids_=($gids)
 set +f
 
 for ((i = 0; i < ${#targets_[@]}; i++)); do
@@ -35,6 +37,8 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
         
         if test "${modes_[$i]}" != symlink; then
             echo "${modes_[$i]}" > $out/etc/$target.mode
+            echo "${uids_[$i]}" > $out/etc/$target.uid
+            echo "${gids_[$i]}" > $out/etc/$target.gid
         fi
         
     fi
diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl
index 4b79dbaab89e..8ba9a370b27a 100644
--- a/nixos/modules/system/etc/setup-etc.pl
+++ b/nixos/modules/system/etc/setup-etc.pl
@@ -60,7 +60,15 @@ sub link {
         if ($mode eq "direct-symlink") {
             atomicSymlink readlink("$static/$fn"), $target or warn;
         } else {
+            open UID, "<$_.uid";
+            my $uid = <UID>; chomp $uid;
+            close UID;
+            open GID, "<$_.gid";
+            my $gid = <GID>; chomp $gid;
+            close GID;
+
             copy "$static/$fn", "$target.tmp" or warn;
+            chown int($uid), int($gid), "$target.tmp" or warn;
             chmod oct($mode), "$target.tmp" or warn;
             rename "$target.tmp", $target or warn;
         }
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 7c3c662eeac9..32e3da6a273c 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -11,18 +11,30 @@ with pkgs.lib;
 let
 
   cfgSpl = config.boot.spl;
+  cfgSnapshots = config.services.zfs.autoSnapshot;
+
   inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems;
   inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems;
+
+  enableAutoSnapshots = cfgSnapshots.enable;
+  enableZfs = inInitrd || inSystem || enableAutoSnapshots;
+
   kernel = config.boot.kernelPackages;
 
+  autosnapPkg = pkgs.zfstools.override {
+    zfs = config.boot.kernelPackages.zfs;
+  };
+
+  zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
+
 in
 
 {
 
   ###### interface
-  
-  options = { 
-    boot.spl.hostid = mkOption { 
+
+  options = {
+    boot.spl.hostid = mkOption {
       default = "";
       example = "0xdeadbeef";
       description = ''
@@ -34,62 +46,177 @@ in
         manually import pools.
       '';
     };
-  };
 
-  ###### implementation
+    services.zfs.autoSnapshot = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable the (OpenSolaris-compatible) ZFS auto-snapshotting service.
+          Note that you must set the <literal>com.sun:auto-snapshot</literal>
+          property to <literal>true</literal> on all datasets which you wish
+          to auto-snapshot.
 
-  config = mkIf ( inInitrd || inSystem ) {
+          You can override a child dataset to use, or not use auto-snapshotting
+          by setting its flag with the given interval:
+          <literal>zfs set com.sun:auto-snapshot:weekly=false DATASET</literal>
+        '';
+      };
 
-    boot = { 
-      kernelModules = [ "spl" "zfs" ] ;
-      extraModulePackages = [ kernel.zfs kernel.spl ];
-      extraModprobeConfig = mkIf (cfgSpl.hostid != "") ''
-        options spl spl_hostid=${cfgSpl.hostid}
-      '';
-    };
+      frequent = mkOption {
+        default = 4;
+        type = types.int;
+        description = ''
+          Number of frequent (15-minute) auto-snapshots that you wish to keep.
+        '';
+      };
+
+      hourly = mkOption {
+        default = 24;
+        type = types.int;
+        description = ''
+          Number of hourly auto-snapshots that you wish to keep.
+        '';
+      };
+
+      daily = mkOption {
+        default = 7;
+        type = types.int;
+        description = ''
+          Number of daily auto-snapshots that you wish to keep.
+        '';
+      };
 
-    boot.initrd = mkIf inInitrd { 
-      kernelModules = [ "spl" "zfs" ] ;
-      extraUtilsCommands =
-        ''
-          cp -v ${kernel.zfs}/sbin/zfs $out/bin
-          cp -v ${kernel.zfs}/sbin/zdb $out/bin
-          cp -v ${kernel.zfs}/sbin/zpool $out/bin
-          cp -pdv ${kernel.zfs}/lib/lib*.so* $out/lib
-          cp -pdv ${pkgs.zlib}/lib/lib*.so* $out/lib
+      weekly = mkOption {
+        default = 4;
+        type = types.int;
+        description = ''
+          Number of weekly auto-snapshots that you wish to keep.
         '';
-      postDeviceCommands =
-        ''
-          zpool import -f -a -d /dev
+      };
+
+      monthly = mkOption {
+        default = 12;
+        type = types.int;
+        description = ''
+          Number of monthly auto-snapshots that you wish to keep.
         '';
+      };
     };
+  };
 
-    systemd.services."zpool-import" = {
-      description = "Import zpools";
-      after = [ "systemd-udev-settle.service" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
+  ###### implementation
+
+  config = mkMerge [
+    (mkIf enableZfs {
+      boot = {
+        kernelModules = [ "spl" "zfs" ] ;
+        extraModulePackages = [ kernel.zfs kernel.spl ];
+        extraModprobeConfig = mkIf (cfgSpl.hostid != "") ''
+          options spl spl_hostid=${cfgSpl.hostid}
+        '';
+      };
+
+      boot.initrd = mkIf inInitrd {
+        kernelModules = [ "spl" "zfs" ] ;
+        extraUtilsCommands =
+          ''
+            cp -v ${kernel.zfs}/sbin/zfs $out/bin
+            cp -v ${kernel.zfs}/sbin/zdb $out/bin
+            cp -v ${kernel.zfs}/sbin/zpool $out/bin
+            cp -pdv ${kernel.zfs}/lib/lib*.so* $out/lib
+            cp -pdv ${pkgs.zlib}/lib/lib*.so* $out/lib
+          '';
+        postDeviceCommands =
+          ''
+            zpool import -f -a
+          '';
+      };
+
+      systemd.services."zpool-import" = {
+        description = "Import zpools";
+        after = [ "systemd-udev-settle.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${kernel.zfs}/sbin/zpool import -f -a";
+        };
         restartIfChanged = false;
-        ExecStart = "${kernel.zfs}/sbin/zpool import -f -a -d /dev";
       };
-    };
 
-    systemd.services."zfs-mount" = {
-      description = "Mount ZFS Volumes";
-      after = [ "zpool-import.service" ];
-      wantedBy = [ "local-fs.target" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
+      systemd.services."zfs-mount" = {
+        description = "Mount ZFS Volumes";
+        after = [ "zpool-import.service" ];
+        wantedBy = [ "local-fs.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          ExecStart = "${kernel.zfs}/sbin/zfs mount -a";
+          ExecStop = "${kernel.zfs}/sbin/zfs umount -a";
+        };
         restartIfChanged = false;
-        ExecStart = "${kernel.zfs}/sbin/zfs mount -a";
-        ExecStop = "${kernel.zfs}/sbin/zfs umount -a";
       };
-    };
 
-    system.fsPackages = [ kernel.zfs ];                  # XXX: needed? zfs doesn't have (need) a fsck
-    environment.systemPackages = [ kernel.zfs ];
-    services.udev.packages = [ kernel.zfs ];             # to hook zvol naming, etc. 
-  };
+      system.fsPackages = [ kernel.zfs ];                  # XXX: needed? zfs doesn't have (need) a fsck
+      environment.systemPackages = [ kernel.zfs ];
+      services.udev.packages = [ kernel.zfs ];             # to hook zvol naming, etc.
+    })
+
+    (mkIf enableAutoSnapshots {
+      systemd.services."zfs-snapshot-frequent" = {
+        description = "ZFS auto-snapshotting every 15 mins";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} frequent ${toString cfgSnapshots.frequent}";
+        };
+        restartIfChanged = false;
+        startAt = "*:15,30,45";
+      };
+
+      systemd.services."zfs-snapshot-hourly" = {
+        description = "ZFS auto-snapshotting every hour";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} hourly ${toString cfgSnapshots.hourly}";
+        };
+        restartIfChanged = false;
+        startAt = "hourly";
+      };
+
+      systemd.services."zfs-snapshot-daily" = {
+        description = "ZFS auto-snapshotting every day";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} daily ${toString cfgSnapshots.daily}";
+        };
+        restartIfChanged = false;
+        startAt = "daily";
+      };
+
+      systemd.services."zfs-snapshot-weekly" = {
+        description = "ZFS auto-snapshotting every week";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} weekly ${toString cfgSnapshots.weekly}";
+        };
+        restartIfChanged = false;
+        startAt = "weekly";
+      };
+
+      systemd.services."zfs-snapshot-monthly" = {
+        description = "ZFS auto-snapshotting every month";
+        after = [ "zpool-import.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${zfsAutoSnap} monthly ${toString cfgSnapshots.monthly}";
+        };
+        restartIfChanged = false;
+        startAt = "monthly";
+      };
+    })
+  ];
 }
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 9619f0f5ebe7..548685377a90 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -50,6 +50,26 @@ let
         '';
       };
 
+      ipv6Address = mkOption {
+        default = null;
+        example = "2001:1470:fffd:2098::e006";
+        type = types.nullOr types.string;
+        description = ''
+          IPv6 address of the interface.  Leave empty to configure the
+          interface using NDP.
+        '';
+      };
+
+      ipv6prefixLength = mkOption {
+        default = 64;
+        example = 64;
+        type = types.int;
+        description = ''
+          Subnet mask of the interface, specified as the number of
+          bits in the prefix (<literal>64</literal>).
+        '';
+      };
+
       macAddress = mkOption {
         default = null;
         example = "00:11:22:33:44:55";
@@ -401,9 +421,11 @@ in
                 EOF
 
                 # Disable or enable IPv6.
-                if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
-                  echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
-                fi
+                ${optionalString (!config.boot.isContainer) ''
+                  if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
+                    echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
+                  fi
+                ''}
 
                 # Set the default gateway.
                 ${optionalString (cfg.defaultGateway != "") ''
@@ -435,6 +457,7 @@ in
           (let mask =
                 if i.prefixLength != null then toString i.prefixLength else
                 if i.subnetMask != "" then i.subnetMask else "32";
+               staticIPv6 = cfg.enableIPv6 && i.ipv6Address != null;
           in
           { description = "Configuration of ${i.name}";
             wantedBy = [ "network-interfaces.target" ];
@@ -468,11 +491,31 @@ in
                     echo "configuring interface..."
                     ip -4 addr flush dev "${i.name}"
                     ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (staticIPv6)
+                ''
+                  # Only do a flush/add if it's necessary.  This is
+                  # useful when the Nix store is accessed via this
+                  # interface (e.g. in a QEMU VM test).
+                  if ! ip -6 -o a show dev "${i.name}" | grep "${i.ipv6Address}/${toString i.ipv6prefixLength}"; then
+                    echo "configuring interface..."
+                    ip -6 addr flush dev "${i.name}"
+                    ip -6 addr add "${i.ipv6Address}/${toString i.ipv6prefixLength}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (i.ipAddress != null || staticIPv6)
+                ''
+                  if [ restart_network_setup = true ]; then
                     # Ensure that the default gateway remains set.
                     # (Flushing this interface may have removed it.)
                     ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  else
-                    echo "skipping configuring interface"
                   fi
                   ${config.systemd.package}/bin/systemctl start ip-up.target
                 ''
diff --git a/nixos/tests/default.nix b/nixos/tests/default.nix
index b37a0d5fa0c9..5b68862a2cd1 100644
--- a/nixos/tests/default.nix
+++ b/nixos/tests/default.nix
@@ -14,6 +14,7 @@ with import ../lib/testing.nix { inherit system minimal; };
   efi-installer = makeTests (import ./efi-installer.nix);
   gnome3 = makeTest (import ./gnome3.nix);
   ipv6 = makeTest (import ./ipv6.nix);
+  jenkins = makeTest (import ./jenkins.nix);
   kde4 = makeTest (import ./kde4.nix);
   #kexec = makeTest (import ./kexec.nix);
   login = makeTest (import ./login.nix {});
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
new file mode 100644
index 000000000000..e6524ec56538
--- /dev/null
+++ b/nixos/tests/jenkins.nix
@@ -0,0 +1,35 @@
+# verifies:
+#   1. jenkins service starts on master node
+#   2. jenkins user can be extended on both master and slave
+#   3. jenkins service not started on slave node
+{ pkgs, ... }:
+{
+  nodes = {
+    master = { pkgs, config, ... }: {
+        services.jenkins.enable = true;
+
+        # should have no effect
+        services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+    slave = { pkgs, config, ... }: {
+        services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+  };
+
+  testScript = ''
+    startAll;
+
+    $master->waitForUnit("jenkins");
+    print $master->execute("sudo -u jenkins groups");
+    $master->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    print $slave->execute("sudo -u jenkins groups");
+    $slave->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    $slave->mustFail("systemctl status jenkins.service");
+  '';
+}