summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorRobin Gloster <mail@glob.in>2016-08-12 09:46:53 +0000
committerRobin Gloster <mail@glob.in>2016-08-12 09:46:53 +0000
commitb7787d932ec9cbd82ea6bc7c69d8df159b606fdc (patch)
treec4b6af2e6b49732ce5c6982cb8512ce9b7f1f34d /nixos/modules
parentbc025e83bd6c44df38851ef23da53359a0e62841 (diff)
parent532b2222965377e77ed884c463ee2751fb51dba3 (diff)
downloadnixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar.gz
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar.bz2
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar.lz
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar.xz
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.tar.zst
nixlib-b7787d932ec9cbd82ea6bc7c69d8df159b606fdc.zip
Merge remote-tracking branch 'upstream/master' into hardened-stdenv
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/system-path.nix1
-rw-r--r--nixos/modules/installer/tools/nixos-install.sh35
-rw-r--r--nixos/modules/installer/tools/nixos-rebuild.sh4
-rw-r--r--nixos/modules/installer/tools/tools.nix2
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/misc/version.nix4
-rw-r--r--nixos/modules/module-list.nix9
-rw-r--r--nixos/modules/programs/unity3d.nix25
-rw-r--r--nixos/modules/programs/zsh/zsh.nix2
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/chromium-suid-sandbox.nix32
-rw-r--r--nixos/modules/services/continuous-integration/gocd-agent/default.nix37
-rw-r--r--nixos/modules/services/databases/cassandra.nix449
-rw-r--r--nixos/modules/services/games/factorio.nix48
-rw-r--r--nixos/modules/services/games/terraria.nix139
-rw-r--r--nixos/modules/services/misc/gitlab.nix155
-rw-r--r--nixos/modules/services/misc/gitlab.xml63
-rw-r--r--nixos/modules/services/networking/offlineimap.nix2
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix1
-rw-r--r--nixos/modules/services/networking/syncthing.nix55
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix14
-rw-r--r--nixos/modules/services/web-servers/phpfpm/default.nix (renamed from nixos/modules/services/web-servers/phpfpm.nix)22
-rw-r--r--nixos/modules/services/web-servers/phpfpm/pool-options.nix35
-rw-r--r--nixos/modules/services/web-servers/varnish/default.nix5
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix29
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix7
-rw-r--r--nixos/modules/services/x11/xserver.nix5
-rw-r--r--nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh123
-rw-r--r--nixos/modules/system/boot/stage-1.nix14
30 files changed, 1078 insertions, 246 deletions
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index 3054439da655..9708b5d9fe33 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -34,7 +34,6 @@ let
       config.programs.ssh.package
       pkgs.perl
       pkgs.procps
-      pkgs.rsync
       pkgs.strace
       pkgs.su
       pkgs.time
diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh
index 7962be137875..0247925f4144 100644
--- a/nixos/modules/installer/tools/nixos-install.sh
+++ b/nixos/modules/installer/tools/nixos-install.sh
@@ -24,6 +24,7 @@ fi
 # Parse the command line for the -I flag
 extraBuildFlags=()
 chrootCommand=(/run/current-system/sw/bin/bash)
+bootLoader=1
 
 while [ "$#" -gt 0 ]; do
     i="$1"; shift 1
@@ -40,6 +41,18 @@ while [ "$#" -gt 0 ]; do
         --root)
             mountPoint="$1"; shift 1
             ;;
+        --closure)
+            closure="$1"; shift 1
+            ;;
+        --no-channel-copy)
+            noChannelCopy=1
+            ;;
+        --no-root-passwd)
+            noRootPasswd=1
+            ;;
+        --no-bootloader)
+            bootLoader=0
+            ;;
         --show-trace)
             extraBuildFlags+=("$i")
             ;;
@@ -111,7 +124,7 @@ if test -z "$NIXOS_CONFIG"; then
     NIXOS_CONFIG=/etc/nixos/configuration.nix
 fi
 
-if ! test -e "$mountPoint/$NIXOS_CONFIG"; then
+if [ ! -e "$mountPoint/$NIXOS_CONFIG" ] && [ -z "$closure" ]; then
     echo "configuration file $mountPoint/$NIXOS_CONFIG doesn't exist"
     exit 1
 fi
@@ -162,7 +175,7 @@ if ! NIX_DB_DIR=$mountPoint/nix/var/nix/db nix-store --check-validity @nix@ 2> /
     for i in $(@perl@/bin/perl @pathsFromGraph@ @nixClosure@); do
         echo "  $i"
         chattr -R -i $mountPoint/$i 2> /dev/null || true # clear immutable bit
-        rsync -a $i $mountPoint/nix/store/
+        @rsync@/bin/rsync -a $i $mountPoint/nix/store/
     done
 
     # Register the paths in the Nix closure as valid.  This is necessary
@@ -200,16 +213,22 @@ for i in /nix/var/nix/manifests/*.nixmanifest; do
 done
 
 
-# Get the absolute path to the NixOS/Nixpkgs sources.
-nixpkgs="$(readlink -f $(nix-instantiate --find-file nixpkgs))"
+if [ -z "$closure" ]; then
+    # Get the absolute path to the NixOS/Nixpkgs sources.
+    nixpkgs="$(readlink -f $(nix-instantiate --find-file nixpkgs))"
 
+    nixEnvAction="-f <nixpkgs/nixos> --set -A system"
+else
+    nixpkgs=""
+    nixEnvAction="--set $closure"
+fi
 
 # 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=/tmp/root/$nixpkgs:nixos-config=$NIXOS_CONFIG" NIXOS_CONFIG= \
     chroot $mountPoint @nix@/bin/nix-env \
-    "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/system -f '<nixpkgs/nixos>' --set -A system
+    "${extraBuildFlags[@]}" -p /nix/var/nix/profiles/system $nixEnvAction
 
 
 # Copy the NixOS/Nixpkgs sources to the target as the initial contents
@@ -218,7 +237,7 @@ 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
+if [ -z "$noChannelCopy" ] && [ -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
@@ -244,7 +263,7 @@ touch $mountPoint/etc/NIXOS
 # a menu default pointing at the kernel/initrd/etc of the new
 # configuration.
 echo "finalising the installation..."
-NIXOS_INSTALL_GRUB=1 chroot $mountPoint \
+NIXOS_INSTALL_GRUB="$bootLoader" chroot $mountPoint \
     /nix/var/nix/profiles/system/bin/switch-to-configuration boot
 
 
@@ -253,7 +272,7 @@ chroot $mountPoint /nix/var/nix/profiles/system/activate
 
 
 # Ask the user to set a root password.
-if [ "$(chroot $mountPoint /run/current-system/sw/bin/sh -l -c "nix-instantiate --eval '<nixpkgs/nixos>' -A config.users.mutableUsers")" = true ] && [ -t 0 ] ; then
+if [ -z "$noRootPasswd" ] && [ "$(chroot $mountPoint /run/current-system/sw/bin/sh -l -c "nix-instantiate --eval '<nixpkgs/nixos>' -A config.users.mutableUsers")" = true ] && [ -t 0 ] ; then
     echo "setting root password..."
     chroot $mountPoint /var/setuid-wrappers/passwd
 fi
diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh
index 5ecdcdb3cdb5..e26a9f6cf635 100644
--- a/nixos/modules/installer/tools/nixos-rebuild.sh
+++ b/nixos/modules/installer/tools/nixos-rebuild.sh
@@ -214,9 +214,9 @@ fi
 
 # Re-execute nixos-rebuild from the Nixpkgs tree.
 if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" ]; then
-    if p=$(nix-instantiate --find-file nixpkgs/nixos/modules/installer/tools/nixos-rebuild.sh "${extraBuildFlags[@]}"); then
+    if p=$(nix-build --no-out-link --expr 'with import <nixpkgs/nixos> {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then
         export _NIXOS_REBUILD_REEXEC=1
-        exec $SHELL -e $p "${origArgs[@]}"
+        exec $p/bin/nixos-rebuild "${origArgs[@]}"
         exit 1
     fi
 fi
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index d8622b510522..a55c03bd9528 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -21,7 +21,7 @@ let
     name = "nixos-install";
     src = ./nixos-install.sh;
 
-    inherit (pkgs) perl pathsFromGraph;
+    inherit (pkgs) perl pathsFromGraph rsync;
     nix = config.nix.package.out;
     cacert = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 3ba279f597da..83b47728639c 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -273,6 +273,7 @@
       smokeping = 250;
       gocd-agent = 251;
       gocd-server = 252;
+      terraria = 253;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -516,6 +517,7 @@
       smokeping = 250;
       gocd-agent = 251;
       gocd-server = 252;
+      terraria = 253;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 6af310a9d877..2ecdbdbf3925 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -63,7 +63,9 @@ in
     nixosRevision = mkOption {
       internal = true;
       type = types.str;
-      default = if pathExists revisionFile then fileContents revisionFile else "master";
+      default = if pathIsDirectory gitRepo then commitIdFromGitRepo gitRepo
+                else if pathExists revisionFile then fileContents revisionFile
+                else "master";
       description = "The Git revision from which this NixOS configuration was built.";
     };
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 9f1ca0f69232..044838161f05 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -62,8 +62,7 @@
   ./programs/bash/bash.nix
   ./programs/blcr.nix
   ./programs/cdemu.nix
-  # see https://github.com/NixOS/nixos-channel-scripts/issues/4
-  #./programs/command-not-found/command-not-found.nix
+  ./programs/command-not-found/command-not-found.nix
   ./programs/dconf.nix
   ./programs/environment.nix
   ./programs/freetds.nix
@@ -80,7 +79,6 @@
   ./programs/ssh.nix
   ./programs/ssmtp.nix
   ./programs/tmux.nix
-  ./programs/unity3d.nix
   ./programs/venus.nix
   ./programs/wvdial.nix
   ./programs/xfs_quota.nix
@@ -92,6 +90,7 @@
   ./security/apparmor-suid.nix
   ./security/audit.nix
   ./security/ca.nix
+  ./security/chromium-suid-sandbox.nix
   ./security/duosec.nix
   ./security/grsecurity.nix
   ./security/hidepid.nix
@@ -137,6 +136,7 @@
   ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
+  ./services/databases/cassandra.nix
   ./services/databases/couchdb.nix
   ./services/databases/firebird.nix
   ./services/databases/hbase.nix
@@ -173,6 +173,7 @@
   ./services/games/ghost-one.nix
   ./services/games/minecraft-server.nix
   ./services/games/minetest-server.nix
+  ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
   ./services/hardware/amd-hybrid-graphics.nix
@@ -473,7 +474,7 @@
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/lighttpd/inginious.nix
   ./services/web-servers/nginx/default.nix
-  ./services/web-servers/phpfpm.nix
+  ./services/web-servers/phpfpm/default.nix
   ./services/web-servers/shellinabox.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/uwsgi.nix
diff --git a/nixos/modules/programs/unity3d.nix b/nixos/modules/programs/unity3d.nix
deleted file mode 100644
index 3c0ea26d9d56..000000000000
--- a/nixos/modules/programs/unity3d.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let cfg = config.programs.unity3d;
-in {
-
-  options = {
-    programs.unity3d.enable = mkEnableOption "Unity3D, a game development tool";
-  };
-
-  config = mkIf cfg.enable {
-    security.setuidOwners = [{
-      program = "unity-chrome-sandbox";
-      source = "${pkgs.unity3d.sandbox}/bin/unity-chrome-sandbox";
-      owner = "root";
-      #group = "root";
-      setuid = true;
-      #setgid = true;
-    }];
-
-    environment.systemPackages = [ pkgs.unity3d ];
-  };
-
-}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index b51104c16fa9..1b8b7a79593e 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -116,6 +116,8 @@ in
         done
 
         ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""}
+
+        HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
       '';
 
     };
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 634f91a275d3..3f8a770cbce7 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -134,6 +134,9 @@ with lib;
     (mkRemovedOptionModule [ "security" "grsecurity" "config" "verboseVersion" ])
     (mkRemovedOptionModule [ "security" "grsecurity" "config" "kernelExtraConfig" ])
 
+    # Unity3D
+    (mkRenamedOptionModule [ "programs" "unity3d" "enable" ] [ "security" "chromiumSuidSandbox" "enable" ])
+
     # Options that are obsolete and have no replacement.
     (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ])
     (mkRemovedOptionModule [ "programs" "bash" "enable" ])
diff --git a/nixos/modules/security/chromium-suid-sandbox.nix b/nixos/modules/security/chromium-suid-sandbox.nix
new file mode 100644
index 000000000000..88fbe518c2de
--- /dev/null
+++ b/nixos/modules/security/chromium-suid-sandbox.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg     = config.security.chromiumSuidSandbox;
+  sandbox = pkgs.chromium.sandbox;
+in
+{
+  options.security.chromiumSuidSandbox.enable = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      Whether to install the Chromium SUID sandbox which is an executable that
+      Chromium may use in order to achieve sandboxing.
+
+      If you get the error "The SUID sandbox helper binary was found, but is not
+      configured correctly.", turning this on might help.
+
+      Also, if the URL chrome://sandbox tells you that "You are not adequately
+      sandboxed!", turning this on might resolve the issue.
+
+      Finally, if you have <option>security.grsecurity</option> enabled and you
+      use Chromium, you probably need this.
+    '';
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ sandbox ];
+    security.setuidPrograms    = [ sandbox.passthru.sandboxExecutableName ];
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/gocd-agent/default.nix b/nixos/modules/services/continuous-integration/gocd-agent/default.nix
index 36f6527ee47b..21f319f7fcf6 100644
--- a/nixos/modules/services/continuous-integration/gocd-agent/default.nix
+++ b/nixos/modules/services/continuous-integration/gocd-agent/default.nix
@@ -36,7 +36,7 @@ in {
       };
 
       packages = mkOption {
-        default = [ pkgs.stdenv pkgs.jre config.programs.ssh.package pkgs.nix ];
+        default = [ pkgs.stdenv pkgs.jre pkgs.git config.programs.ssh.package pkgs.nix ];
         type = types.listOf types.package;
         description = ''
           Packages to add to PATH for the Go.CD agent process.
@@ -57,18 +57,10 @@ in {
       };
 
       goServer = mkOption {
-        default = "127.0.0.1";
+        default = "https://127.0.0.1:8154/go";
         type = types.str;
         description = ''
-          Address of GoCD Server to attach the Go.CD Agent to.
-        '';
-      };
-
-      goServerPort = mkOption {
-        default = 8153;
-        type = types.int;
-        description = ''
-          Port that Go.CD Server is Listening on.
+          URL of the GoCD Server to attach the Go.CD Agent to.
         '';
       };
 
@@ -80,26 +72,26 @@ in {
         '';
       };
 
-      heapSize = mkOption {
+      initialJavaHeapSize = mkOption {
         default = "128m";
         type = types.str;
         description = ''
-          Specifies the java heap memory size for the Go.CD agent java process.
+          Specifies the initial java heap memory size for the Go.CD agent java process.
         '';
       };
 
-      maxMemory = mkOption {
+      maxJavaHeapMemory = mkOption {
         default = "256m";
         type = types.str;
         description = ''
-          Specifies the java maximum memory size for the Go.CD agent java process.
+          Specifies the java maximum heap memory size for the Go.CD agent java process.
         '';
       };
 
       startupOptions = mkOption {
         default = [
-          "-Xms${cfg.heapSize}"
-          "-Xmx${cfg.maxMemory}"
+          "-Xms${cfg.initialJavaHeapSize}"
+          "-Xmx${cfg.maxJavaHeapMemory}"
           "-Djava.io.tmpdir=/tmp"
           "-Dcruise.console.publish.interval=10"
           "-Djava.security.egd=file:/dev/./urandom"
@@ -112,8 +104,8 @@ in {
 
       extraOptions = mkOption {
         default = [ ];
-        example = [ 
-          "-X debug" 
+        example = [
+          "-X debug"
           "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006"
           "-verbose:gc"
           "-Xloggc:go-agent-gc.log"
@@ -170,7 +162,7 @@ in {
               config.environment.sessionVariables;
         in
           selectedSessionVars //
-            { 
+            {
               NIX_REMOTE = "daemon";
               AGENT_WORK_DIR = cfg.workDir;
               AGENT_STARTUP_ARGS = ''${concatStringsSep " "  cfg.startupOptions}'';
@@ -199,13 +191,14 @@ in {
         ${pkgs.jre}/bin/java ${concatStringsSep " " cfg.startupOptions} \
                         ${concatStringsSep " " cfg.extraOptions} \
                               -jar ${pkgs.gocd-agent}/go-agent/agent-bootstrapper.jar \
-                              ${cfg.goServer} \
-                              ${toString cfg.goServerPort}
+                              -serverUrl ${cfg.goServer}
       '';
 
       serviceConfig = {
         User = cfg.user;
         WorkingDirectory = cfg.workDir;
+        RestartSec = 30;
+        Restart = "on-failure";
       };
     };
   };
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
new file mode 100644
index 000000000000..c98af617587d
--- /dev/null
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -0,0 +1,449 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cassandra;
+  cassandraPackage = cfg.package.override {
+    jre = cfg.jre;
+  };
+  cassandraUser = {
+    name = cfg.user;
+    home = "/var/lib/cassandra";
+    description = "Cassandra role user";
+  };
+
+  cassandraRackDcProperties = ''
+    dc=${cfg.dc}
+    rack=${cfg.rack}
+  '';
+
+  cassandraConf = ''
+    cluster_name: ${cfg.clusterName}
+    num_tokens: 256
+    auto_bootstrap: ${if cfg.autoBootstrap then "true" else "false"}
+    hinted_handoff_enabled: ${if cfg.hintedHandOff then "true" else "false"}
+    hinted_handoff_throttle_in_kb: ${builtins.toString cfg.hintedHandOffThrottle}
+    max_hints_delivery_threads: 2
+    max_hint_window_in_ms: 10800000 # 3 hours
+    authenticator: ${cfg.authenticator}
+    authorizer: ${cfg.authorizer}
+    permissions_validity_in_ms: 2000
+    partitioner: org.apache.cassandra.dht.Murmur3Partitioner
+    data_file_directories:
+    ${builtins.concatStringsSep "\n" (map (v: "  - "+v) cfg.dataDirs)}
+    commitlog_directory: ${cfg.commitLogDirectory}
+    disk_failure_policy: stop
+    key_cache_size_in_mb:
+    key_cache_save_period: 14400
+    row_cache_size_in_mb: 0
+    row_cache_save_period: 0
+    saved_caches_directory: ${cfg.savedCachesDirectory}
+    commitlog_sync: ${cfg.commitLogSync}
+    commitlog_sync_period_in_ms: ${builtins.toString cfg.commitLogSyncPeriod}
+    commitlog_segment_size_in_mb: 32
+    seed_provider:
+      - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+        parameters:
+          - seeds: "${builtins.concatStringsSep "," cfg.seeds}"
+    concurrent_reads: ${builtins.toString cfg.concurrentReads}
+    concurrent_writes: ${builtins.toString cfg.concurrentWrites}
+    memtable_flush_queue_size: 4
+    trickle_fsync: false
+    trickle_fsync_interval_in_kb: 10240
+    storage_port: 7000
+    ssl_storage_port: 7001
+    listen_address: ${cfg.listenAddress}
+    start_native_transport: true
+    native_transport_port: 9042
+    start_rpc: true
+    rpc_address: ${cfg.rpcAddress}
+    rpc_port: 9160
+    rpc_keepalive: true
+    rpc_server_type: sync
+    thrift_framed_transport_size_in_mb: 15
+    incremental_backups: ${if cfg.incrementalBackups then "true" else "false"}
+    snapshot_before_compaction: false
+    auto_snapshot: true
+    column_index_size_in_kb: 64
+    in_memory_compaction_limit_in_mb: 64
+    multithreaded_compaction: false
+    compaction_throughput_mb_per_sec: 16
+    compaction_preheat_key_cache: true
+    read_request_timeout_in_ms: 10000
+    range_request_timeout_in_ms: 10000
+    write_request_timeout_in_ms: 10000
+    cas_contention_timeout_in_ms: 1000
+    truncate_request_timeout_in_ms: 60000
+    request_timeout_in_ms: 10000
+    cross_node_timeout: false
+    endpoint_snitch: ${cfg.snitch}
+    dynamic_snitch_update_interval_in_ms: 100
+    dynamic_snitch_reset_interval_in_ms: 600000
+    dynamic_snitch_badness_threshold: 0.1
+    request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+    server_encryption_options:
+      internode_encryption: ${cfg.internodeEncryption}
+      keystore: ${cfg.keyStorePath}
+      keystore_password: ${cfg.keyStorePassword}
+      truststore: ${cfg.trustStorePath}
+      truststore_password: ${cfg.trustStorePassword}
+    client_encryption_options:
+      enabled: ${if cfg.clientEncryption then "true" else "false"}
+      keystore: ${cfg.keyStorePath}
+      keystore_password: ${cfg.keyStorePassword}
+    internode_compression: all
+    inter_dc_tcp_nodelay: false
+    preheat_kernel_page_cache: false
+    streaming_socket_timeout_in_ms: ${toString cfg.streamingSocketTimoutInMS}
+  '';
+
+  cassandraLog = ''
+    log4j.rootLogger=${cfg.logLevel},stdout
+    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %d{HH:mm:ss,SSS} %m%n
+  '';
+
+  cassandraConfFile = pkgs.writeText "cassandra.yaml" cassandraConf;
+  cassandraLogFile = pkgs.writeText "log4j-server.properties" cassandraLog;
+  cassandraRackFile = pkgs.writeText "cassandra-rackdc.properties" cassandraRackDcProperties;
+
+  cassandraEnvironment = {
+    CASSANDRA_HOME = cassandraPackage;
+    JAVA_HOME = cfg.jre;
+    CASSANDRA_CONF = "/etc/cassandra";
+  };
+
+in {
+
+  ###### interface
+
+  options.services.cassandra = {
+    enable = mkOption {
+      description = "Whether to enable cassandra.";
+      default = false;
+      type = types.bool;
+    };
+    package = mkOption {
+      description = "Cassandra package to use.";
+      default = pkgs.cassandra;
+      defaultText = "pkgs.cassandra";
+      type = types.package;
+    };
+    jre = mkOption {
+      description = "JRE package to run cassandra service.";
+      default = pkgs.jre;
+      defaultText = "pkgs.jre";
+      type = types.package;
+    };
+    user = mkOption {
+      description = "User that runs cassandra service.";
+      default = "cassandra";
+      type = types.string;
+    };
+    group = mkOption {
+      description = "Group that runs cassandra service.";
+      default = "cassandra";
+      type = types.string;
+    };
+    envFile = mkOption {
+      description = "path to cassandra-env.sh";
+      default = "${cassandraPackage}/conf/cassandra-env.sh";
+      defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
+      type = types.path;
+    };
+    clusterName = mkOption {
+      description = "set cluster name";
+      default = "cassandra";
+      example = "prod-cluster0";
+      type = types.string;
+    };
+    commitLogDirectory = mkOption {
+      description = "directory for commit logs";
+      default = "/var/lib/cassandra/commit_log";
+      type = types.string;
+    };
+    savedCachesDirectory = mkOption {
+      description = "directory for saved caches";
+      default = "/var/lib/cassandra/saved_caches";
+      type = types.string;
+    };
+    hintedHandOff = mkOption {
+      description = "enable hinted handoff";
+      default = true;
+      type = types.bool;
+    };
+    hintedHandOffThrottle = mkOption {
+      description = "hinted hand off throttle rate in kb";
+      default = 1024;
+      type = types.int;
+    };
+    commitLogSync = mkOption {
+      description = "commitlog sync method";
+      default = "periodic";
+      type = types.str;
+      example = "batch";
+    };
+    commitLogSyncPeriod = mkOption {
+      description = "commitlog sync period in ms ";
+      default = 10000;
+      type = types.int;
+    };
+    envScript = mkOption {
+      default = "${cassandraPackage}/conf/cassandra-env.sh";
+      defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
+      type = types.path;
+      description = "Supply your own cassandra-env.sh rather than using the default";
+    };
+    extraParams = mkOption {
+      description = "add additional lines to cassandra-env.sh";
+      default = [];
+      example = [''JVM_OPTS="$JVM_OPTS -Dcassandra.available_processors=1"''];
+      type = types.listOf types.str;
+    };
+    dataDirs = mkOption {
+      type = types.listOf types.path;
+      default = [ "/var/lib/cassandra/data" ];
+      description = "Data directories for cassandra";
+    };
+    logLevel = mkOption {
+      type = types.str;
+      default = "INFO";
+      description = "default logging level for log4j";
+    };
+    internodeEncryption = mkOption {
+      description = "enable internode encryption";
+      default = "none";
+      example = "all";
+      type = types.str;
+    };
+    clientEncryption = mkOption {
+      description = "enable client encryption";
+      default = false;
+      type = types.bool;
+    };
+    trustStorePath = mkOption {
+      description = "path to truststore";
+      default = ".conf/truststore";
+      type = types.str;
+    };
+    keyStorePath = mkOption {
+      description = "path to keystore";
+      default = ".conf/keystore";
+      type = types.str;
+    };
+    keyStorePassword = mkOption {
+      description = "password to keystore";
+      default = "cassandra";
+      type = types.str;
+    };
+    trustStorePassword = mkOption {
+      description = "password to truststore";
+      default = "cassandra";
+      type = types.str;
+    };
+    seeds = mkOption {
+      description = "password to truststore";
+      default = [ "127.0.0.1" ];
+      type = types.listOf types.str;
+    };
+    concurrentWrites = mkOption {
+      description = "number of concurrent writes allowed";
+      default = 32;
+      type = types.int;
+    };
+    concurrentReads = mkOption {
+      description = "number of concurrent reads allowed";
+      default = 32;
+      type = types.int;
+    };
+    listenAddress = mkOption {
+      description = "listen address";
+      default = "localhost";
+      type = types.str;
+    };
+    rpcAddress = mkOption {
+      description = "rpc listener address";
+      default = "localhost";
+      type = types.str;
+    };
+    incrementalBackups = mkOption {
+      description = "enable incremental backups";
+      default = false;
+      type = types.bool;
+    };
+    snitch = mkOption {
+      description = "snitch to use for topology discovery";
+      default = "GossipingPropertyFileSnitch";
+      example = "Ec2Snitch";
+      type = types.str;
+    };
+    dc = mkOption {
+      description = "datacenter for use in topology configuration";
+      default = "DC1";
+      example = "DC1";
+      type = types.str;
+    };
+    rack = mkOption {
+      description = "rack for use in topology configuration";
+      default = "RAC1";
+      example = "RAC1";
+      type = types.str;
+    };
+    authorizer = mkOption {
+      description = "
+        Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
+      ";
+      default = "AllowAllAuthorizer";
+      example = "CassandraAuthorizer";
+      type = types.str;
+    };
+    authenticator = mkOption {
+      description = "
+        Authentication backend, implementing IAuthenticator; used to identify users
+      ";
+      default = "AllowAllAuthenticator";
+      example = "PasswordAuthenticator";
+      type = types.str;
+    };
+    autoBootstrap = mkOption {
+      description = "It makes new (non-seed) nodes automatically migrate the right data to themselves.";
+      default = true;
+      example = true;
+      type = types.bool;
+    };
+    streamingSocketTimoutInMS = mkOption {
+      description = "Enable or disable socket timeout for streaming operations";
+      default = 3600000; #CASSANDRA-8611
+      example = 120;
+      type = types.int;
+    };
+    repairStartAt = mkOption {
+      default = "Sun";
+      type = types.string;
+      description = ''
+      Defines realtime (i.e. wallclock) timers with calendar event
+      expressions. For more details re: systemd OnCalendar at
+      https://www.freedesktop.org/software/systemd/man/systemd.time.html#Displaying%20Time%20Spans
+      '';
+      example = ["weekly" "daily" "08:05:40" "mon,fri *-1/2-1,3 *:30:45"];
+    };
+    repairRandomizedDelayInSec = mkOption {
+      default = 0;
+      type = types.int;
+      description = ''Delay the timer by a randomly selected, evenly distributed
+      amount of time between 0 and the specified time value. re: systemd timer
+      RandomizedDelaySec for more details
+      '';
+    };
+    repairPostStop = mkOption {
+      default = null;
+      type = types.nullOr types.string;
+      description = ''
+      Run a script when repair is over. One can use it to send statsd events, email, etc.
+      '';
+    };
+    repairPostStart = mkOption {
+      default = null;
+      type = types.nullOr types.string;
+      description = ''
+      Run a script when repair starts. One can use it to send statsd events, email, etc.
+      It has same semantics as systemd ExecStopPost; So, if it fails, unit is consisdered
+      failed.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc."cassandra/cassandra-rackdc.properties" = {
+      source = cassandraRackFile;
+    };
+    environment.etc."cassandra/cassandra.yaml" = {
+      source = cassandraConfFile;
+    };
+    environment.etc."cassandra/log4j-server.properties" = {
+      source = cassandraLogFile;
+    };
+    environment.etc."cassandra/cassandra-env.sh" = {
+      text = ''
+        ${builtins.readFile cfg.envFile}
+        ${concatStringsSep "\n" cfg.extraParams}
+      '';
+    };
+    systemd.services.cassandra = {
+      description = "Cassandra Daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-interfaces.target" ];
+      environment = cassandraEnvironment;
+      restartTriggers = [ cassandraConfFile cassandraLogFile cassandraRackFile ];
+      serviceConfig = {
+
+        User = cfg.user;
+        PermissionsStartOnly = true;
+        LimitAS = "infinity";
+        LimitNOFILE = "100000";
+        LimitNPROC = "32768";
+        LimitMEMLOCK = "infinity";
+
+      };
+      script = ''
+         ${cassandraPackage}/bin/cassandra -f
+        '';
+      path = [
+        cfg.jre
+        cassandraPackage
+        pkgs.coreutils
+      ];
+      preStart = ''
+        mkdir -m 0700 -p /etc/cassandra/triggers
+        mkdir -m 0700 -p /var/lib/cassandra /var/log/cassandra
+        chown ${cfg.user} /var/lib/cassandra /var/log/cassandra /etc/cassandra/triggers
+      '';
+      postStart = ''
+        sleep 2
+        while ! nodetool status >/dev/null 2>&1; do
+          sleep 2
+        done
+        nodetool status
+      '';
+    };
+
+    environment.systemPackages = [ cassandraPackage ];
+
+    networking.firewall.allowedTCPPorts = [
+      7000
+      7001
+      9042
+      9160
+    ];
+
+    users.extraUsers.cassandra =
+      if config.ids.uids ? "cassandra"
+      then { uid = config.ids.uids.cassandra; } // cassandraUser
+      else cassandraUser ;
+
+    boot.kernel.sysctl."vm.swappiness" = pkgs.lib.mkOptionDefault 0;
+
+    systemd.timers."cassandra-repair" = {
+      timerConfig = {
+        OnCalendar = "${toString cfg.repairStartAt}";
+        RandomizedDelaySec = cfg.repairRandomizedDelayInSec;
+      };
+    };
+
+    systemd.services."cassandra-repair" = {
+      description = "Cassandra repair daemon";
+      environment = cassandraEnvironment;
+      script = "${cassandraPackage}/bin/nodetool repair -pr";
+      postStop = mkIf (cfg.repairPostStop != null) cfg.repairPostStop;
+      postStart = mkIf (cfg.repairPostStart != null) cfg.repairPostStart;
+      serviceConfig = {
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 90834c5b2605..0369752997a7 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -4,14 +4,17 @@ with lib;
 
 let
   cfg = config.services.factorio;
+  factorio = pkgs.factorio-headless;
   name = "Factorio";
   stateDir = "/var/lib/factorio";
+  mkSavePath = name: "${stateDir}/saves/${name}.zip";
   configFile = pkgs.writeText "factorio.conf" ''
     use-system-read-write-data-directories=true
     [path]
-    read-data=${pkgs.factorio-headless}/share/factorio/data
+    read-data=${factorio}/share/factorio/data
     write-data=${stateDir}
   '';
+  modDir = pkgs.factorio-mkModDirDrv cfg.mods;
 in
 {
   options = {
@@ -32,7 +35,8 @@ in
         description = ''
           The name of the savegame that will be used by the server.
 
-          When not present in ${stateDir}/saves, it will be generated before starting the service.
+          When not present in ${stateDir}/saves, a new map with default
+          settings will be generated before starting the service.
         '';
       };
       # TODO Add more individual settings as nixos-options?
@@ -51,6 +55,26 @@ in
           customizations.
         '';
       };
+      mods = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        description = ''
+          Mods the server should install and activate.
+
+          The derivations in this list must "build" the mod by simply copying
+          the .zip, named correctly, into the output directory. Eventually,
+          there will be a way to pull in the most up-to-date list of
+          derivations via nixos-channel. Until then, this is for experts only.
+        '';
+      };
+      autosave-interval = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 2;
+        description = ''
+          The time, in minutes, between autosaves.
+        '';
+      };
     };
   };
 
@@ -74,12 +98,14 @@ in
       wantedBy      = [ "multi-user.target" ];
       after         = [ "network.target" ];
 
-      preStart = ''
-          test -e ${stateDir}/saves/${cfg.saveName}.zip || \
-            ${pkgs.factorio-headless}/bin/factorio         \
-              --config=${cfg.configFile}                   \
-              --create=${stateDir}/saves/${cfg.saveName}.zip
-      '';
+      preStart = toString [
+        "test -e ${stateDir}/saves/${cfg.saveName}.zip"
+        "||"
+        "${factorio}/bin/factorio"
+          "--config=${cfg.configFile}"
+          "--create=${mkSavePath cfg.saveName}"
+          (optionalString (cfg.mods != []) "--mod-directory=${modDir}")
+      ];
 
       serviceConfig = {
         User = "factorio";
@@ -90,10 +116,12 @@ in
         PrivateTmp = true;
         UMask = "0007";
         ExecStart = toString [
-          "${pkgs.factorio-headless}/bin/factorio"
+          "${factorio}/bin/factorio"
           "--config=${cfg.configFile}"
           "--port=${toString cfg.port}"
-          "--start-server=${stateDir}/saves/${cfg.saveName}.zip"
+          "--start-server=${mkSavePath cfg.saveName}"
+          (optionalString (cfg.mods != []) "--mod-directory=${modDir}")
+          (optionalString (cfg.autosave-interval != null) "--autosave-interval ${toString cfg.autosave-interval}")
         ];
       };
     };
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
new file mode 100644
index 000000000000..57ac37bb1bd3
--- /dev/null
+++ b/nixos/modules/services/games/terraria.nix
@@ -0,0 +1,139 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg   = config.services.terraria;
+  worldSizeMap = { "small" = 1; "medium" = 2; "large" = 3; };
+  valFlag = name: val: optionalString (val != null) "-${name} \"${escape ["\\" "\""] (toString val)}\"";
+  boolFlag = name: val: optionalString val "-${name}";
+  flags = [ 
+    (valFlag "port" cfg.port)
+    (valFlag "maxPlayers" cfg.maxPlayers)
+    (valFlag "password" cfg.password)
+    (valFlag "motd" cfg.messageOfTheDay)
+    (valFlag "world" cfg.worldPath)
+    (valFlag "autocreate" (builtins.getAttr cfg.autoCreatedWorldSize worldSizeMap))
+    (valFlag "banlist" cfg.banListPath)
+    (boolFlag "secure" cfg.secure)
+    (boolFlag "noupnp" cfg.noUPnP)
+  ];
+in
+{
+  options = {
+    services.terraria = {
+      enable = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = ''
+          If enabled, starts a Terraria server. The server can be connected to via <literal>tmux -S /var/lib/terraria/terraria.sock attach</literal>
+          for administration by users who are a part of the <literal>terraria</literal> group (use <literal>C-b d</literal> shortcut to detach again).
+        '';
+      };
+
+      port = mkOption {
+        type        = types.int;
+        default     = 7777;
+        description = ''
+          Specifies the port to listen on.
+        '';
+      };
+
+      maxPlayers = mkOption {
+        type        = types.int;
+        default     = 255;
+        description = ''
+          Sets the max number of players (between 1 and 255).
+        '';
+      };
+
+      password = mkOption {
+        type        = types.nullOr types.str;
+        default     = null;
+        description = ''
+          Sets the server password. Leave <literal>null</literal> for no password.
+        '';
+      };
+
+      messageOfTheDay = mkOption {
+        type        = types.nullOr types.str;
+        default     = null;
+        description = ''
+          Set the server message of the day text.
+        '';
+      };
+
+      worldPath = mkOption {
+        type        = types.path;
+        default     = null;
+        description = ''
+          The path to the world file (<literal>.wld</literal>) which should be loaded.
+          If no world exists at this path, one will be created with the size
+          specified by <literal>autoCreatedWorldSize</literal>.
+        '';
+      };
+
+      autoCreatedWorldSize = mkOption {
+        type        = types.enum [ "small" "medium" "large" ];
+        default     = "medium";
+        description = ''
+          Specifies the size of the auto-created world if <literal>worldPath</literal> does not
+          point to an existing world.
+        '';
+      };
+
+      banListPath = mkOption {
+        type        = types.nullOr types.path;
+        default     = null;
+        description = ''
+          The path to the ban list.
+        '';
+      };
+
+      secure = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = "Adds additional cheat protection to the server.";
+      };
+
+      noUPnP = mkOption {
+        type        = types.bool;
+        default     = false;
+        description = "Disables automatic Universal Plug and Play.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraUsers.terraria = {
+      description = "Terraria server service user";
+      home        = "/var/lib/terraria";
+      createHome  = true;
+      uid         = config.ids.uids.terraria;
+    };
+
+    users.extraGroups.terraria = {
+      gid = config.ids.gids.terraria;
+      members = [ "terraria" ];
+    };
+
+    systemd.services.terraria = {
+      description   = "Terraria Server Service";
+      wantedBy      = [ "multi-user.target" ];
+      after         = [ "network.target" ];
+
+      serviceConfig = {
+        User    = "terraria";
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
+        ExecStop = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter";
+      };
+
+      postStart = ''
+        ${pkgs.coreutils}/bin/chmod 660 /var/lib/terraria/terraria.sock
+        ${pkgs.coreutils}/bin/chgrp terraria /var/lib/terraria/terraria.sock
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 267442bd1f8b..ac2e94c25c33 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -57,42 +57,23 @@ let
           issues = true;
           merge_requests = true;
           wiki = true;
-          snippets = false;
+          snippets = true;
           builds = true;
+          container_registry = true;
         };
       };
-      artifacts = {
-        enabled = true;
-      };
-      lfs = {
-        enabled = true;
-      };
-      gravatar = {
-        enabled = true;
-      };
-      cron_jobs = {
-        stuck_ci_builds_worker = {
-          cron = "0 0 * * *";
-        };
-      };
-      gitlab_ci = {
-        builds_path = "${cfg.statePath}/builds";
-      };
-      ldap = {
-        enabled = false;
-      };
-      omniauth = {
-        enabled = false;
-      };
-      shared = {
-        path = "${cfg.statePath}/shared";
-      };
-      backup = {
-        path = "${cfg.backupPath}";
-      };
+      repositories.storages.default = "${cfg.statePath}/repositories";
+      artifacts.enabled = true;
+      lfs.enabled = true;
+      gravatar.enabled = true;
+      cron_jobs = { };
+      gitlab_ci.builds_path = "${cfg.statePath}/builds";
+      ldap.enabled = false;
+      omniauth.enabled = false;
+      shared.path = "${cfg.statePath}/shared";
+      backup.path = "${cfg.backupPath}";
       gitlab_shell = {
         path = "${cfg.packages.gitlab-shell}";
-        repos_path = "${cfg.statePath}/repositories";
         hooks_path = "${cfg.statePath}/shell/hooks";
         secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
         upload_pack = true;
@@ -125,21 +106,42 @@ let
 
   unicornConfig = builtins.readFile ./defaultUnicornConfig.rb;
 
-  gitlab-runner = pkgs.stdenv.mkDerivation rec {
-    name = "gitlab-runner";
-    buildInputs = [ cfg.packages.gitlab bundler pkgs.makeWrapper ];
+  gitlab-rake = pkgs.stdenv.mkDerivation rec {
+    name = "gitlab-rake";
+    buildInputs = [ cfg.packages.gitlab cfg.packages.gitlab.env pkgs.makeWrapper ];
     phases = "installPhase fixupPhase";
     buildPhase = "";
     installPhase = ''
       mkdir -p $out/bin
-      makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner \
-          ${concatStrings (mapAttrsToList (name: value: "--set ${name} '\"${value}\"' ") gitlabEnv)} \
-          --set GITLAB_CONFIG_PATH '"${cfg.statePath}/config"' \
-          --set PATH '"${pkgs.nodejs}/bin:${pkgs.gzip}/bin:${config.services.postgresql.package}/bin:$PATH"' \
-          --set RAKEOPT '"-f ${cfg.packages.gitlab}/share/gitlab/Rakefile"'
-    '';
+      makeWrapper ${cfg.packages.gitlab.env}/bin/bundle $out/bin/gitlab-bundle \
+          ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
+          --set GITLAB_CONFIG_PATH '${cfg.statePath}/config' \
+          --set PATH '${pkgs.nodejs}/bin:${pkgs.gzip}/bin:${config.services.postgresql.package}/bin:$PATH' \
+          --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
+          --run 'cd ${cfg.packages.gitlab}/share/gitlab'
+      makeWrapper $out/bin/gitlab-bundle $out/bin/gitlab-rake \
+          --add-flags "exec rake"
+     '';
   };
 
+  smtpSettings = pkgs.writeText "gitlab-smtp-settings.rb" ''
+    if Rails.env.production?
+      Rails.application.config.action_mailer.delivery_method = :smtp
+
+      ActionMailer::Base.delivery_method = :smtp
+      ActionMailer::Base.smtp_settings = {
+        address: "${cfg.smtp.address}",
+        port: ${toString cfg.smtp.port},
+        ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''}
+        ${optionalString (cfg.smtp.password != null) ''password: "${cfg.smtp.password}",''}
+        domain: "${cfg.smtp.domain}",
+        ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"}
+        enable_starttls_auto: ${toString cfg.smtp.enableStartTLSAuto},
+        openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}'
+      }
+    end
+  '';
+
 in {
 
   options = {
@@ -255,6 +257,62 @@ in {
         '';
       };
 
+      smtp = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Enable gitlab mail delivery over SMTP.";
+        };
+
+        address = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Address of the SMTP server for Gitlab.";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 465;
+          description = "Port of the SMTP server for Gitlab.";
+        };
+
+        username = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = "Username of the SMTP server for Gitlab.";
+        };
+
+        password = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = "Password of the SMTP server for Gitlab.";
+        };
+
+        domain = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "HELO domain to use for outgoing mail.";
+        };
+
+        authentication = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = "Authentitcation type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
+        };
+
+        enableStartTLSAuto = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Whether to try to use StartTLS.";
+        };
+
+        opensslVerifyMode = mkOption {
+          type = types.str;
+          default = "peer";
+          description = "How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
+        };
+      };
+
       extraConfig = mkOption {
         type = types.attrs;
         default = {};
@@ -275,7 +333,7 @@ in {
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.git gitlab-runner cfg.packages.gitlab-shell ];
+    environment.systemPackages = [ pkgs.git gitlab-rake cfg.packages.gitlab-shell ];
 
     assertions = [
       { assertion = cfg.databasePassword != "";
@@ -308,6 +366,7 @@ in {
     systemd.services.gitlab-sidekiq = {
       after = [ "network.target" "redis.service" ];
       wantedBy = [ "multi-user.target" ];
+      partOf = [ "gitlab.service" ];
       environment = gitlabEnv;
       path = with pkgs; [
         config.services.postgresql.package
@@ -322,7 +381,7 @@ in {
         Group = cfg.group;
         TimeoutSec = "300";
         WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
-        ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailers -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
+        ExecStart="${cfg.packages.gitlab.env}/bin/bundle exec \"sidekiq -q post_receive -q mailers -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
       };
     };
 
@@ -397,6 +456,9 @@ in {
         chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}/
 
         cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
+        ${optionalString cfg.smtp.enable ''
+          ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
+        ''}
         ln -sf ${cfg.statePath}/config /run/gitlab/config
         cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
 
@@ -420,14 +482,14 @@ in {
             touch "${cfg.statePath}/db-created"
 
             # The gitlab:setup task is horribly broken somehow, these two tasks will do the same for setting up the initial database
-            ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production
-            ${gitlab-runner}/bin/gitlab-runner exec rake db:seed_fu RAILS_ENV=production \
+            ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
+            ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \
               GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}";
           fi
         fi
 
         # Always do the db migrations just to be sure the database is up-to-date
-        ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production
+        ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
 
         # Change permissions in the last step because some of the
         # intermediary scripts like to create directories as root.
@@ -441,8 +503,9 @@ in {
         User = cfg.user;
         Group = cfg.group;
         TimeoutSec = "300";
+        Restart = "on-failure";
         WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
-        ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\"";
+        ExecStart = "${cfg.packages.gitlab.env}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\"";
       };
 
     };
diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml
index b630fe421130..a8147b3a74f9 100644
--- a/nixos/modules/services/misc/gitlab.xml
+++ b/nixos/modules/services/misc/gitlab.xml
@@ -14,29 +14,22 @@
 <literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to configure a
 webserver to proxy HTTP requests to the socket.</para>
 
-<para>For instance, this could be used for Nginx:
+<para>For instance, the following configuration could be used to use nginx as
+    frontend proxy:
 
 <programlisting>
-services.nginx.httpConfig = ''
-  server {
-    server_name git.example.com;
-    listen 443 ssl spdy;
-    listen [::]:443 ssl spdy;
-
-    ssl_certificate /var/lib/acme/git.example.com/fullchain.pem;
-    ssl_certificate_key /var/lib/acme/git.example.com/key.pem;
-
-    location / {
-      proxy_http_version 1.1;
-      proxy_set_header    Host                $http_host;
-      proxy_set_header    X-Real-IP           $remote_addr;
-      proxy_set_header    X-Forwarded-Ssl     on;
-      proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
-      proxy_set_header    X-Forwarded-Proto   $scheme;
-
-      proxy_pass http://unix:/run/gitlab/gitlab-workhorse.socket;
-    }
-  }
+    services.nginx = {
+      enable = true;
+      recommendedGzipSettings = true;
+      recommendedOptimisation = true;
+      recommendedProxySettings = true;
+      recommendedTlsSettings = true;
+      virtualHosts."git.example.com" = {
+        enableACME = true;
+        forceSSL = true;
+        locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
+      };
+    };
 '';
 </programlisting>
 </para>
@@ -49,10 +42,10 @@ services.nginx.httpConfig = ''
 both services. In the case of PostgreSQL, a database and a role will be created.
 </para>
 
-<para>The default state dir is /var/gitlab/state. This is where all data like
-the repositories and uploads will be stored.</para>
+<para>The default state dir is <literal>/var/gitlab/state</literal>. This is where
+all data like the repositories and uploads will be stored.</para>
 
-<para>A basic configuration could look like this:
+<para>A basic configuration with some custom settings could look like this:
 
 <programlisting>
 services.gitlab = {
@@ -64,8 +57,16 @@ services.gitlab = {
   port = 443;
   user = "git";
   group = "git";
+  smtp = {
+    enable = true;
+    address = "localhost";
+    port = 25;
+  };
   extraConfig = {
     gitlab = {
+      email_from = "gitlab-no-reply@example.com";
+      email_display_name = "Example GitLab";
+      email_reply_to = "gitlab-no-reply@example.com";
       default_projects_features = { builds = false; };
     };
   };
@@ -80,21 +81,21 @@ options for the <literal>services.gitlab</literal> module.</para>
 
 <section><title>Maintenance</title>
 
-<para>You can run all Gitlab related commands like rake tasks with
-<literal>gitlab-runner</literal> which will be available on the system
-when gitlab is enabled. You will have to run the commands as the user that
-you configured to run gitlab.</para>
+<para>You can run Gitlab's rake tasks with <literal>gitlab-rake</literal>
+which will be available on the system when gitlab is enabled. You will
+have to run the command as the user that you configured to run gitlab
+with.</para>
 
-<para>For instance, to backup a Gitlab instance:
+<para>For example, to backup a Gitlab instance:
 
 <programlisting>
-$ sudo -u git -H gitlab-runner exec rake gitlab:backup:create
+$ sudo -u git -H gitlab-rake gitlab:backup:create
 </programlisting>
 
 A list of all availabe rake tasks can be obtained by running:
 
 <programlisting>
-$ sudo -u git -H gitlab-runner exec rake -T
+$ sudo -u git -H gitlab-rake -T
 </programlisting>
 </para>
 
diff --git a/nixos/modules/services/networking/offlineimap.nix b/nixos/modules/services/networking/offlineimap.nix
index 31ce9280f319..daf6196d3706 100644
--- a/nixos/modules/services/networking/offlineimap.nix
+++ b/nixos/modules/services/networking/offlineimap.nix
@@ -54,7 +54,7 @@ in {
       description = "Offlineimap: a software to dispose your mailbox(es) as a local Maildir(s)";
       serviceConfig = {
         Type      = "oneshot";
-        ExecStart = "${cfg.package}/bin/offlineimap -u basic -o -1";
+        ExecStart = "${cfg.package}/bin/offlineimap -u syslog -o -1";
         TimeoutStartSec = cfg.timeoutStartSec;
       };
       path = cfg.path;
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index a464733a6a03..f900ef494abf 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -263,6 +263,7 @@ in
 
             serviceConfig =
               { ExecStart =
+                  (optionalString cfg.startWhenNeeded "-") +
                   "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") +
                   "-f ${pkgs.writeText "sshd_config" cfg.extraConfig}";
                 KillMode = "process";
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index b44b03dc0bf3..8a430734319b 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -23,6 +23,18 @@ let
     RestartForceExitStatus="3 4";
   };
 
+  iNotifyHeader = {
+    description = "Syncthing Inotify File Watcher service";
+    after = [ "network.target" "syncthing.service" ];
+    requires = [ "syncthing.service" ];
+  };
+
+  iNotifyService = {
+    SuccessExitStatus = "2";
+    RestartForceExitStatus = "3";
+    Restart = "on-failure";
+  };
+
 in
 
 {
@@ -39,6 +51,12 @@ in
         available on http://127.0.0.1:8384/.
       '';
 
+      useInotify = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Provide syncthing-inotify as a service.";
+      };
+
       systemService = mkOption {
         type = types.bool;
         default = true;
@@ -112,27 +130,40 @@ in
         config.ids.gids.syncthing;
     };
 
-    environment.systemPackages = [ cfg.package ];
-
-    systemd.services = mkIf cfg.systemService {
-      syncthing = header // {
+    systemd.services = {
+      syncthing = mkIf cfg.systemService (header // {
+          wants = mkIf cfg.useInotify [ "syncthing-inotify.service" ];
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = service // {
+            User = cfg.user;
+            Group = cfg.group;
+            PermissionsStartOnly = true;
+            ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}";
+          };
+      });
+
+      syncthing-inotify = mkIf (cfg.systemService && cfg.useInotify) (iNotifyHeader // {
         wantedBy = [ "multi-user.target" ];
-        serviceConfig = service // {
+        serviceConfig = iNotifyService // {
           User = cfg.user;
-          Group = cfg.group;
-          PermissionsStartOnly = true;
-          ExecStart = "${cfg.package}/bin/syncthing -no-browser -home=${cfg.dataDir}";
+          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -home=${cfg.dataDir} -logflags=0";
         };
-      };
+      });
     };
 
-    systemd.user.services.syncthing =
-      header // {
-        wantedBy = [ "default.target" ];
+    systemd.user.services = {
+      syncthing = header // {
         serviceConfig = service // {
           ExecStart = "${cfg.package}/bin/syncthing -no-browser";
         };
       };
 
+      syncthing-inotify = mkIf cfg.useInotify (iNotifyHeader // {
+        serviceConfig = iNotifyService // {
+          ExecStart = "${pkgs.syncthing-inotify.bin}/bin/syncthing-inotify -logflags=0";
+        };
+      });
+    };
+
   };
 }
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 8385d8e60266..af7753470de6 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -18,7 +18,9 @@ let
 
     ${cfg.config}
 
-    ${optionalString (cfg.httpConfig == "") ''
+    ${optionalString (cfg.httpConfig == "" && cfg.config == "") ''
+    events {}
+
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
@@ -96,6 +98,7 @@ let
     }''}
 
     ${optionalString (cfg.httpConfig != "") ''
+    events {}
     http {
       include ${cfg.package}/conf/mime.types;
       include ${cfg.package}/conf/fastcgi.conf;
@@ -233,9 +236,12 @@ in
       };
 
       config = mkOption {
-        default = "events {}";
+        default = "";
         description = "
           Verbatim nginx.conf configuration.
+          This is mutually exclusive with the structured configuration
+          via virtualHosts and the recommendedXyzSettings configuration
+          options. See appendConfig for appending to the generated http block.
         ";
       };
 
@@ -268,8 +274,8 @@ in
         default = "";
         description = "
           Configuration lines to be appended to the generated http block.
-          This is mutually exclusive with using httpConfig for specifying the whole
-          http block verbatim.
+          This is mutually exclusive with using config and httpConfig for 
+          specifying the whole http block verbatim.
         ";
       };
 
diff --git a/nixos/modules/services/web-servers/phpfpm.nix b/nixos/modules/services/web-servers/phpfpm/default.nix
index 2658d7117e37..819d0c251bf3 100644
--- a/nixos/modules/services/web-servers/phpfpm.nix
+++ b/nixos/modules/services/web-servers/phpfpm/default.nix
@@ -9,6 +9,12 @@ let
 
   pidFile = "${stateDir}/phpfpm.pid";
 
+  mkPool = n: p: ''
+    [${n}]
+    listen = ${p.listen}
+    ${p.extraConfig}
+  '';
+
   cfgFile = pkgs.writeText "phpfpm.conf" ''
     [global]
     pid = ${pidFile}
@@ -16,6 +22,8 @@ let
     daemonize = yes
     ${cfg.extraConfig}
 
+    ${concatStringsSep "\n" (mapAttrsToList mkPool cfg.pools)}
+
     ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
   '';
 
@@ -62,8 +70,8 @@ in {
       };
 
       poolConfigs = mkOption {
-        type = types.attrsOf types.lines;
         default = {};
+        type = types.attrsOf types.lines;
         example = literalExample ''
           { mypool = '''
               listen = /run/phpfpm/mypool
@@ -84,10 +92,20 @@ in {
           the phpfpm service is disabled.
         '';
       };
+
+      pools = mkOption {
+        type = types.attrsOf (types.submodule (import ./pool-options.nix {
+          inherit lib;
+        }));
+        default = {};
+        description = ''
+          If no pools are defined, the phpfpm service is disabled.
+        '';
+      };
     };
   };
 
-  config = mkIf (cfg.poolConfigs != {}) {
+  config = mkIf (cfg.pools != {}) {
 
     systemd.services.phpfpm = {
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/web-servers/phpfpm/pool-options.nix b/nixos/modules/services/web-servers/phpfpm/pool-options.nix
new file mode 100644
index 000000000000..cc688c2c48a2
--- /dev/null
+++ b/nixos/modules/services/web-servers/phpfpm/pool-options.nix
@@ -0,0 +1,35 @@
+{ lib }:
+
+with lib; {
+
+  options = {
+
+    listen = mkOption {
+      type = types.str;
+      example = "/path/to/unix/socket";
+      description = ''
+        The address on which to accept FastCGI requests.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      example = ''
+        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 = ''
+        Extra lines that go into the pool configuration.
+        See the documentation on <literal>php-fpm.conf</literal> for
+        details on configuration directives.
+      '';
+    };
+  };
+}
+
diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix
index 364f6c68faca..61df43ec2352 100644
--- a/nixos/modules/services/web-servers/varnish/default.nix
+++ b/nixos/modules/services/web-servers/varnish/default.nix
@@ -28,7 +28,7 @@ with lib;
       };
 
       stateDir = mkOption {
-        default = "/var/spool/varnish";
+        default = "/var/spool/varnish/${config.networking.hostName}";
         description = "
           Directory holding all state for Varnish to run.
         ";
@@ -46,6 +46,9 @@ with lib;
         mkdir -p ${cfg.stateDir}
         chown -R varnish:varnish ${cfg.stateDir}
       '';
+      postStop = ''
+        rm -rf ${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";
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 14c7131e611c..b358550ba41d 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -25,16 +25,21 @@ in {
 
       accelProfile = mkOption {
         type = types.enum [ "flat" "adaptive" ];
-        default = "flat";
-        example = "adaptive";
+        default = "adaptive";
+        example = "flat";
         description =
           ''
-            Sets  the pointer acceleration profile to the given profile. Permitted values are adaptive, flat.
-            Not all devices support this option or all profiles. If a profile is unsupported, the default profile
-            for this is used. For a description on the profiles and their behavior, see the libinput documentation.
+            Sets  the pointer acceleration profile to the given profile.
+            Permitted values are adaptive, flat.
+            Not all devices support this option or all profiles.
+            If a profile is unsupported, the default profile for this is used.
+            <literal>flat</literal>: Pointer motion is accelerated by a constant
+            (device-specific) factor, depending on the current speed.
+            <literal>adaptive</literal>: Pointer acceleration depends on the input speed.
+            This is the default profile for most devices.
           '';
-      };    
-      
+      };
+
       accelSpeed = mkOption {
         type = types.nullOr types.string;
         default = null;
@@ -216,7 +221,7 @@ in {
           Option "LeftHanded" "${xorgBool cfg.leftHanded}"
           Option "MiddleEmulation" "${xorgBool cfg.middleEmulation}"
           Option "NaturalScrolling" "${xorgBool cfg.naturalScrolling}"
-          ${optionalString (cfg.scrollButton != null) ''Option "ScrollButton" "${cfg.scrollButton}"''}
+          ${optionalString (cfg.scrollButton != null) ''Option "ScrollButton" "${toString cfg.scrollButton}"''}
           Option "ScrollMethod" "${cfg.scrollMethod}"
           Option "HorizontalScrolling" "${xorgBool cfg.horizontalScrolling}"
           Option "SendEventsMode" "${cfg.sendEventsMode}"
@@ -227,6 +232,14 @@ in {
         EndSection
       '';
 
+    assertions = [
+      # already present in synaptics.nix
+      /* {
+        assertion = !config.services.xserver.synaptics.enable;
+        message = "Synaptics and libinput are incompatible, you cannot enable both (in services.xserver).";
+      } */
+    ];
+
   };
 
 }
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index e74b19c8e710..5c068e89dd71 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -205,6 +205,13 @@ in {
         EndSection
       '';
 
+    assertions = [
+      {
+        assertion = !config.services.xserver.libinput.enable;
+        message = "Synaptics and libinput are incompatible, you cannot enable both (in services.xserver).";
+      }
+    ];
+
   };
 
 }
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index e7df2e6b4690..b03f70385b1f 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -14,6 +14,9 @@ let
   # Map video driver names to driver packages. FIXME: move into card-specific modules.
   knownVideoDrivers = {
     virtualbox = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
+
+    # modesetting does not have a xf86videomodesetting package as it is included in xorgserver
+    modesetting = {};
   };
 
   fontsForXServer =
@@ -443,7 +446,7 @@ in
            then { modules = [xorg.${"xf86video" + name}]; }
            else null)
           knownVideoDrivers;
-      in optional (driver != null) ({ inherit name; driverName = name; } // driver));
+      in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
 
     assertions =
       [ { assertion = config.security.polkit.enable;
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 8dad09c89207..f2755b49f88d 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -443,7 +443,7 @@ in
         copy_bin_and_libs pbkdf2-sha512
 
         mkdir -p $out/etc/ssl
-        cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
+        cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl
 
         cat > $out/bin/openssl-wrap <<EOF
         #!$out/bin/sh
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 82995d5bab11..fbb32901f64e 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -185,39 +185,6 @@ 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
-    if test -n "@resumeDevice@"; then
-        resumeDev="@resumeDevice@"
-        resumeInfo="$(udevadm info -q property "$resumeDev" )"
-    else
-        for sd in @resumeDevices@; do
-            # Try to detect resume device. According to Ubuntu bug:
-            # https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
-            # when there are multiple swap devices, we can't know where the hibernate
-            # image will reside. We can check all of them for swsuspend blkid.
-            resumeInfo="$(test -e "$sd" && udevadm info -q property "$sd")"
-            if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
-                resumeDev="$sd"
-                break
-            fi
-        done
-    fi
-    if test -e "$resumeDev"; then
-        resumeMajor="$(echo "$resumeInfo" | sed -n 's/^MAJOR=//p')"
-        resumeMinor="$(echo "$resumeInfo" | sed -n 's/^MINOR=//p')"
-        echo "$resumeMajor:$resumeMinor" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
-    fi
-fi
-
-
 # Return true if the machine is on AC power, or if we can't determine
 # whether it's on AC power.
 onACPower() {
@@ -348,6 +315,68 @@ mountFS() {
 }
 
 
+# Function for waiting a device to appear.
+waitDevice() {
+    local device="$1"
+
+    # 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 test ! -e $device; then
+        echo -n "waiting for device $device to appear..."
+        try=20
+        while [ $try -gt 0 ]; do
+            sleep 1
+            # also re-try lvm activation now that new block devices might have appeared
+            lvm vgchange -ay
+            # and tell udev to create nodes for the new LVs
+            udevadm trigger --action=add
+            if test -e $device; then break; fi
+            echo -n "."
+            try=$((try - 1))
+        done
+        echo
+        [ $try -ne 0 ]
+    fi
+}
+
+
+# Try to resume - all modules are loaded now.
+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
+    if test -n "@resumeDevice@" && waitDevice "@resumeDevice@"; then
+        resumeDev="@resumeDevice@"
+        resumeInfo="$(udevadm info -q property "$resumeDev" )"
+    else
+        for sd in @resumeDevices@; do
+            # Try to detect resume device. According to Ubuntu bug:
+            # https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
+            # when there are multiple swap devices, we can't know where the hibernate
+            # image will reside. We can check all of them for swsuspend blkid.
+            if waitDevice "$sd"; then
+                resumeInfo="$(udevadm info -q property "$sd")"
+                if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
+                    resumeDev="$sd"
+                    break
+                fi
+            fi
+        done
+    fi
+    if test -n "$resumeDev"; then
+        resumeMajor="$(echo "$resumeInfo" | sed -n 's/^MAJOR=//p')"
+        resumeMinor="$(echo "$resumeInfo" | sed -n 's/^MINOR=//p')"
+        echo "$resumeMajor:$resumeMinor" > /sys/power/resume 2> /dev/null || echo "failed to resume..."
+    fi
+fi
+
+
 # Try to find and mount the root device.
 mkdir -p $targetRoot
 
@@ -380,29 +409,11 @@ while read -u 3 mountPoint; do
             ;;
     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..."
-        try=20
-        while [ $try -gt 0 ]; do
-            sleep 1
-            # also re-try lvm activation now that new block devices might have appeared
-            lvm vgchange -ay
-            # and tell udev to create nodes for the new LVs
-            udevadm trigger --action=add
-            if test -e $device; then break; fi
-            echo -n "."
-            try=$((try - 1))
-        done
-        echo
-        if [ $try -eq 0 ]; then
-          echo "Timed out waiting for device $device, trying to mount anyway."
-        fi
+    if test -z "$pseudoDevice" && ! waitDevice "$device"; then
+        # 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.
+        echo "Timed out waiting for device $device, trying to mount anyway."
     fi
 
     # Wait once more for the udev queue to empty, just in case it's
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 21a49d45789e..70429e9c0a22 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -87,15 +87,11 @@ let
         LDD="$(ldd $BIN)" || continue
         LIBS="$(echo "$LDD" | awk '{print $3}' | sed '/^$/d')"
         for LIB in $LIBS; do
-          [ ! -f "$out/lib/$(basename $LIB)" ] && cp -pdv $LIB $out/lib
-          while [ "$(readlink $LIB)" != "" ]; do
-            LINK="$(readlink $LIB)"
-            if [ "${LINK:0:1}" != "/" ]; then
-              LINK="$(dirname $LIB)/$LINK"
-            fi
-            LIB="$LINK"
-            [ ! -f "$out/lib/$(basename $LIB)" ] && cp -pdv $LIB $out/lib
-          done
+          TGT="$out/lib/$(basename $LIB)"
+          if [ ! -f "$TGT" ]; then
+            SRC="$(readlink -e $LIB)"
+            cp -pdv "$SRC" "$TGT"
+          fi
         done
       done