summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-1903.xml18
-rw-r--r--nixos/modules/misc/ids.nix2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix214
-rw-r--r--nixos/modules/services/monitoring/kapacitor.nix154
-rw-r--r--nixos/modules/services/networking/chrony.nix2
-rw-r--r--nixos/modules/services/networking/consul.nix7
-rw-r--r--nixos/modules/services/networking/ntpd.nix2
-rw-r--r--nixos/modules/services/networking/syncthing.nix14
-rw-r--r--nixos/modules/services/networking/zerotierone.nix3
-rw-r--r--nixos/modules/system/activation/activation-script.nix1
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix2
-rw-r--r--nixos/modules/virtualisation/docker-preloader.nix135
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix1
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix5
-rw-r--r--nixos/release.nix3
-rw-r--r--nixos/tests/chromium.nix10
-rw-r--r--nixos/tests/docker-preloader.nix27
-rw-r--r--nixos/tests/gitlab.nix18
20 files changed, 482 insertions, 139 deletions
diff --git a/nixos/doc/manual/release-notes/rl-1903.xml b/nixos/doc/manual/release-notes/rl-1903.xml
index 1d9b6ecc0e2e..fb5d475f693a 100644
--- a/nixos/doc/manual/release-notes/rl-1903.xml
+++ b/nixos/doc/manual/release-notes/rl-1903.xml
@@ -97,6 +97,16 @@
        start org.nixos.nix-daemon</command>.
       </para>
      </listitem>
+     <listitem>
+      <para>
+        The Syncthing state and configuration data has been moved from
+        <varname>services.syncthing.dataDir</varname> to the newly defined
+        <varname>services.syncthing.configDir</varname>, which default to
+        <literal>/var/lib/syncthing/.config/syncthing</literal>.
+        This change makes possible to share synced directories using ACLs
+        without Syncthing resetting the permission on every start.
+      </para>
+     </listitem>
     </itemizedlist>
    </listitem>
    <listitem>
@@ -154,6 +164,14 @@
    </listitem>
    <listitem>
     <para>
+     Package <literal>consul-ui</literal> and passthrough <literal>consul.ui</literal> have been removed.
+     The package <literal>consul</literal> now uses upstream releases that vendor the UI into the binary.
+     See <link xlink:href="https://github.com/NixOS/nixpkgs/pull/48714#issuecomment-433454834">#48714</link>
+     for details.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
       Slurm introduces the new option
       <literal>services.slurm.stateSaveLocation</literal>,
       which is now set to <literal>/var/spool/slurm</literal> by default
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index a32e4fe3f7c8..16737efb1856 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -332,6 +332,7 @@
       lirc = 305;
       lidarr = 306;
       slurm = 307;
+      kapacitor = 308;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -624,6 +625,7 @@
       lirc = 305;
       lidarr = 306;
       slurm = 307;
+      kapacitor = 308;
 
       # 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/module-list.nix b/nixos/modules/module-list.nix
index 660644eade8d..37e90232da2a 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -433,6 +433,7 @@
   ./services/monitoring/hdaps.nix
   ./services/monitoring/heapster.nix
   ./services/monitoring/incron.nix
+  ./services/monitoring/kapacitor.nix
   ./services/monitoring/longview.nix
   ./services/monitoring/monit.nix
   ./services/monitoring/munin.nix
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 6edb1503c233..f592be0e768b 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -271,5 +271,5 @@ in
   };
 
   meta.doc = ./postgresql.xml;
-
+  meta.maintainers = with lib.maintainers; [ thoughtpolice ];
 }
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 8ea831afb7c1..ce1cb6ad37f2 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -14,15 +14,16 @@ let
   pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
   pgSuperUser = config.services.postgresql.superUser;
 
-  databaseYml = ''
-    production:
-      adapter: postgresql
-      database: ${cfg.databaseName}
-      host: ${cfg.databaseHost}
-      password: ${cfg.databasePassword}
-      username: ${cfg.databaseUsername}
-      encoding: utf8
-  '';
+  databaseConfig = {
+    production = {
+      adapter = "postgresql";
+      database = cfg.databaseName;
+      host = cfg.databaseHost;
+      password = cfg.databasePassword;
+      username = cfg.databaseUsername;
+      encoding = "utf8";
+    };
+  };
 
   gitalyToml = pkgs.writeText "gitaly.toml" ''
     socket_path = "${lib.escape ["\""] gitalySocket}"
@@ -45,35 +46,31 @@ let
     '') gitlabConfig.production.repositories.storages))}
   '';
 
-  gitlabShellYml = ''
-    user: ${cfg.user}
-    gitlab_url: "http+unix://${pathUrlQuote gitlabSocket}"
-    http_settings:
-      self_signed_cert: false
-    repos_path: "${cfg.statePath}/repositories"
-    secret_file: "${cfg.statePath}/config/gitlab_shell_secret"
-    log_file: "${cfg.statePath}/log/gitlab-shell.log"
-    custom_hooks_dir: "${cfg.statePath}/custom_hooks"
-    redis:
-      bin: ${pkgs.redis}/bin/redis-cli
-      host: 127.0.0.1
-      port: 6379
-      database: 0
-      namespace: resque:gitlab
-  '';
+  gitlabShellConfig = {
+    user = cfg.user;
+    gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}";
+    http_settings.self_signed_cert = false;
+    repos_path = "${cfg.statePath}/repositories";
+    secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
+    log_file = "${cfg.statePath}/log/gitlab-shell.log";
+    custom_hooks_dir = "${cfg.statePath}/custom_hooks";
+    redis = {
+      bin = "${pkgs.redis}/bin/redis-cli";
+      host = "127.0.0.1";
+      port = 6379;
+      database = 0;
+      namespace = "resque:gitlab";
+    };
+  };
 
-  redisYml = ''
-    production:
-      url: redis://localhost:6379/
-  '';
+  redisConfig.production.url = "redis://localhost:6379/";
 
-  secretsYml = ''
-    production:
-      secret_key_base: ${cfg.secrets.secret}
-      otp_key_base: ${cfg.secrets.otp}
-      db_key_base: ${cfg.secrets.db}
-      openid_connect_signing_key: ${builtins.toJSON cfg.secrets.jws}
-  '';
+  secretsConfig.production = {
+    secret_key_base = cfg.secrets.secret;
+    otp_key_base = cfg.secrets.otp;
+    db_key_base = cfg.secrets.db;
+    openid_connect_signing_key = cfg.secrets.jws;
+  };
 
   gitlabConfig = {
     # These are the default settings from config/gitlab.example.yml
@@ -115,12 +112,8 @@ let
         upload_pack = true;
         receive_pack = true;
       };
-      workhorse = {
-        secret_file = "${cfg.statePath}/.gitlab_workhorse_secret";
-      };
-      git = {
-        bin_path = "git";
-      };
+      workhorse.secret_file = "${cfg.statePath}/.gitlab_workhorse_secret";
+      git.bin_path = "git";
       monitoring = {
         ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
         sidekiq_exporter = {
@@ -138,7 +131,7 @@ let
     HOME = "${cfg.statePath}/home";
     UNICORN_PATH = "${cfg.statePath}/";
     GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
-    GITLAB_STATE_PATH = "${cfg.statePath}";
+    GITLAB_STATE_PATH = cfg.statePath;
     GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
     SCHEMA = "${cfg.statePath}/db/schema.rb";
     GITLAB_LOG_PATH = "${cfg.statePath}/log";
@@ -146,13 +139,11 @@ let
     GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
     GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret";
     GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks";
-    GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "gitlab-redis.yml" redisYml;
+    GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig);
     prometheus_multiproc_dir = "/run/gitlab";
     RAILS_ENV = "production";
   };
 
-  unicornConfig = builtins.readFile ./defaultUnicornConfig.rb;
-
   gitlab-rake = pkgs.stdenv.mkDerivation rec {
     name = "gitlab-rake";
     buildInputs = [ pkgs.makeWrapper ];
@@ -162,7 +153,6 @@ let
       mkdir -p $out/bin
       makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \
           ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
-          --set GITLAB_CONFIG_PATH '${cfg.statePath}/config' \
           --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package pkgs.coreutils pkgs.procps ]}:$PATH' \
           --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
           --run 'cd ${cfg.packages.gitlab}/share/gitlab'
@@ -306,7 +296,6 @@ in {
 
       initialRootPassword = mkOption {
         type = types.str;
-        default = "UseNixOS!";
         description = ''
           Initial password of the root account if this is a new install.
         '';
@@ -461,10 +450,30 @@ in {
       }
     ];
 
+    systemd.tmpfiles.rules = [
+      "d /run/gitlab 0755 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.backupPath} 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -"
+      "d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
+      "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
+    ];
+
     systemd.services.gitlab-sidekiq = {
-      after = [ "network.target" "redis.service" ];
+      after = [ "network.target" "redis.service" "gitlab.service" ];
       wantedBy = [ "multi-user.target" ];
-      partOf = [ "gitlab.service" ];
       environment = gitlabEnv;
       path = with pkgs; [
         config.services.postgresql.package
@@ -486,10 +495,8 @@ in {
     };
 
     systemd.services.gitaly = {
-      after = [ "network.target" "gitlab.service" ];
+      after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      environment.HOME = gitlabEnv.HOME;
-      environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
       path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv cfg.packages.gitaly.rubyEnv.wrappedRuby ];
       serviceConfig = {
         Type = "simple";
@@ -505,8 +512,6 @@ in {
     systemd.services.gitlab-workhorse = {
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      environment.HOME = gitlabEnv.HOME;
-      environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
       path = with pkgs; [
         gitAndTools.git
         gnutar
@@ -514,10 +519,6 @@ in {
         openssh
         gitlab-workhorse
       ];
-      preStart = ''
-        mkdir -p /run/gitlab
-        chown ${cfg.user}:${cfg.group} /run/gitlab
-      '';
       serviceConfig = {
         PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
@@ -538,7 +539,7 @@ in {
     };
 
     systemd.services.gitlab = {
-      after = [ "network.target" "postgresql.service" "redis.service" ];
+      after = [ "gitlab-workhorse.service" "gitaly.service" "network.target" "postgresql.service" "redis.service" ];
       requires = [ "gitlab-sidekiq.service" ];
       wantedBy = [ "multi-user.target" ];
       environment = gitlabEnv;
@@ -551,102 +552,75 @@ in {
         gnupg
       ];
       preStart = ''
-        mkdir -p ${cfg.backupPath}
-        mkdir -p ${cfg.statePath}/builds
-        mkdir -p ${cfg.statePath}/repositories
-        mkdir -p ${gitlabConfig.production.shared.path}/artifacts
-        mkdir -p ${gitlabConfig.production.shared.path}/lfs-objects
-        mkdir -p ${gitlabConfig.production.shared.path}/pages
-        mkdir -p ${cfg.statePath}/log
-        mkdir -p ${cfg.statePath}/tmp/pids
-        mkdir -p ${cfg.statePath}/tmp/sockets
-        mkdir -p ${cfg.statePath}/shell
-        mkdir -p ${cfg.statePath}/db
-        mkdir -p ${cfg.statePath}/uploads
-        mkdir -p ${cfg.statePath}/custom_hooks/pre-receive.d
-        mkdir -p ${cfg.statePath}/custom_hooks/post-receive.d
-        mkdir -p ${cfg.statePath}/custom_hooks/update.d
-
-        rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
-        mkdir -p ${cfg.statePath}/config
-
         ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/config/gitlab_shell_secret
 
-        mkdir -p /run/gitlab
-        mkdir -p ${cfg.statePath}/log
-        [ -d /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log
-        [ -d /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
-        [ -d /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
-        ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml
-        chown -R ${cfg.user}:${cfg.group} /run/gitlab
-
-        # Prepare home directory
-        mkdir -p ${gitlabEnv.HOME}/.ssh
-        touch ${gitlabEnv.HOME}/.ssh/authorized_keys
-        chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
-
         cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
         cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
-        ${optionalString cfg.smtp.enable ''
-          ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
-        ''}
-        ln -sf ${cfg.statePath}/config /run/gitlab/config
         if [ -e ${cfg.statePath}/lib ]; then
           rm ${cfg.statePath}/lib
         fi
-        ln -sf ${pkgs.gitlab}/share/gitlab/lib ${cfg.statePath}/lib
+
+        ln -sf ${cfg.packages.gitlab}/share/gitlab/lib ${cfg.statePath}/lib
+        [ -L /run/gitlab/config ] || ln -sf ${cfg.statePath}/config /run/gitlab/config
+        [ -L /run/gitlab/log ] || ln -sf ${cfg.statePath}/log /run/gitlab/log
+        [ -L /run/gitlab/tmp ] || ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp
+        [ -L /run/gitlab/uploads ] || ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
+        ${optionalString cfg.smtp.enable ''
+          ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
+        ''}
         cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
 
         # JSON is a subset of YAML
-        ln -fs ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
-        ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.statePath}/config/database.yml
-        ln -fs ${pkgs.writeText "secrets.yml" secretsYml} ${cfg.statePath}/config/secrets.yml
-        ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.statePath}/config/unicorn.rb
-
-        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
-        chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
+        ln -sf ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
+        ln -sf ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} ${cfg.statePath}/config/database.yml
+        ln -sf ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)} ${cfg.statePath}/config/secrets.yml
+        ln -sf ${./defaultUnicornConfig.rb} ${cfg.statePath}/config/unicorn.rb
 
         # Install the shell required to push repositories
-        ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} "$GITLAB_SHELL_CONFIG_PATH"
-        ln -fs ${cfg.packages.gitlab-shell}/hooks "$GITLAB_SHELL_HOOKS_PATH"
+        ln -sf ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)} /run/gitlab/shell-config.yml
+        [ -L ${cfg.statePath}/shell/hooks ] ||  ln -sf ${cfg.packages.gitlab-shell}/hooks ${cfg.statePath}/shell/hooks
         ${cfg.packages.gitlab-shell}/bin/install
 
-        if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
-          if ! test -e "${cfg.statePath}/db-created"; then
+        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
+        chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
+        chown -R ${cfg.user}:${cfg.group} /run/gitlab
+
+        if ! test -e "${cfg.statePath}/db-created"; then
+          if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
             ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.databasePassword}'"
             ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName}
-            touch "${cfg.statePath}/db-created"
+
+            # enable required pg_trgm extension for gitlab
+            ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql ${cfg.databaseName} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
           fi
 
-          # enable required pg_trgm extension for gitlab
-          ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql ${cfg.databaseName} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"
+          ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load
+
+          touch "${cfg.statePath}/db-created"
         fi
 
         # Always do the db migrations just to be sure the database is up-to-date
-        ${gitlab-rake}/bin/gitlab-rake db:migrate RAILS_ENV=production
+        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:migrate
 
-        # The gitlab:setup task is horribly broken somehow, the db:migrate
-        # task above and the db:seed_fu below will do the same for setting
-        # up the initial database
         if ! test -e "${cfg.statePath}/db-seeded"; then
-          ${gitlab-rake}/bin/gitlab-rake db:seed_fu RAILS_ENV=production \
+          ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \
             GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}'
           touch "${cfg.statePath}/db-seeded"
         fi
 
         # The gitlab:shell:setup regenerates the authorized_keys file so that
         # the store path to the gitlab-shell in it gets updated
-        ${pkgs.sudo}/bin/sudo -u ${cfg.user} force=yes ${gitlab-rake}/bin/gitlab-rake gitlab:shell:setup RAILS_ENV=production
+        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H force=yes ${gitlab-rake}/bin/gitlab-rake gitlab:shell:setup
 
         # The gitlab:shell:create_hooks task seems broken for fixing links
         # so we instead delete all the hooks and create them anew
         rm -f ${cfg.statePath}/repositories/**/*.git/hooks
-        ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks RAILS_ENV=production
+        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake gitlab:shell:create_hooks
+
+        ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input"
 
         # Change permissions in the last step because some of the
         # intermediary scripts like to create directories as root.
-        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}
-        chmod -R ug+rwX,o-rwx+X ${cfg.statePath}
         chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}
         chmod -R ug+rwX,o-rwx ${cfg.statePath}/repositories
         chmod -R ug-s ${cfg.statePath}/repositories
diff --git a/nixos/modules/services/monitoring/kapacitor.nix b/nixos/modules/services/monitoring/kapacitor.nix
new file mode 100644
index 000000000000..1de0a8d5af2f
--- /dev/null
+++ b/nixos/modules/services/monitoring/kapacitor.nix
@@ -0,0 +1,154 @@
+{ options, config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.kapacitor;
+
+  kapacitorConf = pkgs.writeTextFile {
+    name = "kapacitord.conf";
+    text = ''
+      hostname="${config.networking.hostName}"
+      data_dir="${cfg.dataDir}"
+
+      [http]
+        bind-address = "${cfg.bind}:${toString cfg.port}"
+        log-enabled = false
+        auth-enabled = false
+
+      [task]
+        dir = "${cfg.dataDir}/tasks"
+        snapshot-interval = "${cfg.taskSnapshotInterval}"
+
+      [replay]
+        dir = "${cfg.dataDir}/replay"
+
+      [storage]
+        boltdb = "${cfg.dataDir}/kapacitor.db"
+
+      ${optionalString (cfg.loadDirectory != null) ''
+        [load]
+          enabled = true
+          dir = "${cfg.loadDirectory}"
+      ''}
+
+      ${optionalString (cfg.defaultDatabase.enable) ''
+        [[influxdb]]
+          name = "default"
+          enabled = true
+          default = true
+          urls = [ "${cfg.defaultDatabase.url}" ]
+          username = "${cfg.defaultDatabase.username}"
+          password = "${cfg.defaultDatabase.password}"
+      ''}
+
+      ${cfg.extraConfig}
+    '';
+  };
+in
+{
+  options.services.kapacitor = {
+    enable = mkEnableOption "kapacitor";
+
+    dataDir = mkOption {
+      type = types.path;
+      example = "/var/lib/kapacitor";
+      default = "/var/lib/kapacitor";
+      description = "Location where Kapacitor stores its state";
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 9092;
+      description = "Port of Kapacitor";
+    };
+
+    bind = mkOption {
+      type = types.str;
+      default = "";
+      example = literalExample "0.0.0.0";
+      description = "Address to bind to. The default is to bind to all addresses";
+    };
+
+    extraConfig = mkOption {
+      description = "These lines go into kapacitord.conf verbatim.";
+      default = "";
+      type = types.lines;
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "kapacitor";
+      description = "User account under which Kapacitor runs";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "kapacitor";
+      description = "Group under which Kapacitor runs";
+    };
+
+    taskSnapshotInterval = mkOption {
+      type = types.str;
+      description = "Specifies how often to snapshot the task state  (in InfluxDB time units)";
+      default = "1m0s";
+      example = "1m0s";
+    };
+
+    loadDirectory = mkOption {
+      type = types.nullOr types.path;
+      description = "Directory where to load services from, such as tasks, templates and handlers (or null to disable service loading on startup)";
+      default = null;
+    };
+
+    defaultDatabase = {
+      enable = mkEnableOption "kapacitor.defaultDatabase";
+
+      url = mkOption {
+        description = "The URL to an InfluxDB server that serves as the default database";
+        example = "http://localhost:8086";
+        type = types.string;
+      };
+
+      username = mkOption {
+        description = "The username to connect to the remote InfluxDB server";
+        type = types.string;
+      };
+
+      password = mkOption {
+        description = "The password to connect to the remote InfluxDB server";
+        type = types.string;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.kapacitor ];
+
+    systemd.services.kapacitor = {
+      description = "Kapacitor Real-Time Stream Processing Engine";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.kapacitor}/bin/kapacitord -config ${kapacitorConf}";
+        User = "kapacitor";
+        Group = "kapacitor";
+        PermissionsStartOnly = true;
+      };
+      preStart = ''
+        mkdir -p ${cfg.dataDir}
+        chown ${cfg.user}:${cfg.group} ${cfg.dataDir}
+      '';
+    };
+
+    users.users.kapacitor = {
+      uid = config.ids.uids.kapacitor;
+      description = "Kapacitor user";
+      home = cfg.dataDir;
+    };
+
+    users.groups.kapacitor = {
+      gid = config.ids.gids.kapacitor;
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/chrony.nix b/nixos/modules/services/networking/chrony.nix
index a363b545d649..9b8005e706ae 100644
--- a/nixos/modules/services/networking/chrony.nix
+++ b/nixos/modules/services/networking/chrony.nix
@@ -93,6 +93,8 @@ in
 
     services.timesyncd.enable = mkForce false;
 
+    systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; };
+
     systemd.services.chronyd =
       { description = "chrony NTP daemon";
 
diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix
index ab3f81037681..0e90fed788b9 100644
--- a/nixos/modules/services/networking/consul.nix
+++ b/nixos/modules/services/networking/consul.nix
@@ -6,9 +6,10 @@ let
   dataDir = "/var/lib/consul";
   cfg = config.services.consul;
 
-  configOptions = { data_dir = dataDir; } //
-    (if cfg.webUi then { ui_dir = "${cfg.package.ui}"; } else { }) //
-    cfg.extraConfig;
+  configOptions = {
+    data_dir = dataDir;
+    ui = cfg.webUi;
+  } // cfg.extraConfig;
 
   configFiles = [ "/etc/consul.json" "/etc/consul-addrs.json" ]
     ++ cfg.extraConfigFiles;
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix
index 342350d49ab3..32174100b0f7 100644
--- a/nixos/modules/services/networking/ntpd.nix
+++ b/nixos/modules/services/networking/ntpd.nix
@@ -67,6 +67,8 @@ in
     environment.systemPackages = [ pkgs.ntp ];
     services.timesyncd.enable = mkForce false;
 
+    systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd.service"; };
+
     users.users = singleton
       { name = ntpUser;
         uid = config.ids.uids.ntp;
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index fd31b2a67687..b2ef1885a955 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -63,8 +63,20 @@ in {
         type = types.path;
         default = "/var/lib/syncthing";
         description = ''
+          Path where synced directories will exist.
+        '';
+      };
+
+      configDir = mkOption {
+        type = types.path;
+        description = ''
           Path where the settings and keys will exist.
         '';
+        default =
+          let
+            nixos = config.system.stateVersion;
+            cond  = versionAtLeast nixos "19.03";
+          in cfg.dataDir + (optionalString cond "/.config/syncthing");
       };
 
       openDefaultPorts = mkOption {
@@ -144,7 +156,7 @@ in {
             ${cfg.package}/bin/syncthing \
               -no-browser \
               -gui-address=${cfg.guiAddress} \
-              -home=${cfg.dataDir}
+              -home=${cfg.configDir}
           '';
         };
       };
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index a4cd368397e7..764af3846fe5 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -39,7 +39,8 @@ in
     systemd.services.zerotierone = {
       description = "ZeroTierOne";
       path = [ cfg.package ];
-      after = [ "network.target" ];
+      bindsTo = [ "network-online.target" ];
+      after = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
       preStart = ''
         mkdir -p /var/lib/zerotier-one/networks.d
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 0ed7c0f53eb9..74c150a848d1 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -22,6 +22,7 @@ let
       gnugrep
       findutils
       getent
+      stdenv.cc.libc # nscd in update-users-groups.pl
       shadow
       nettools # needed for hostname
       utillinux # needed for mount and mountpoint
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index c92570582f20..9015200beead 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -53,7 +53,7 @@ let cfg = config.ec2; in
     # Mount all formatted ephemeral disks and activate all swap devices.
     # We cannot do this with the ‘fileSystems’ and ‘swapDevices’ options
     # because the set of devices is dependent on the instance type
-    # (e.g. "m1.large" has one ephemeral filesystem and one swap device,
+    # (e.g. "m1.small" has one ephemeral filesystem and one swap device,
     # while "m1.large" has two ephemeral filesystems and no swap
     # devices).  Also, put /tmp and /var on /disk0, since it has a lot
     # more space than the root device.  Similarly, "move" /nix to /disk0
diff --git a/nixos/modules/virtualisation/docker-preloader.nix b/nixos/modules/virtualisation/docker-preloader.nix
new file mode 100644
index 000000000000..faa94f53d98f
--- /dev/null
+++ b/nixos/modules/virtualisation/docker-preloader.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+with builtins;
+
+let
+  cfg = config.virtualisation;
+
+  sanitizeImageName = image: replaceStrings ["/"] ["-"] image.imageName;
+  hash = drv: head (split "-" (baseNameOf drv.outPath));
+  # The label of an ext4 FS is limited to 16 bytes
+  labelFromImage = image: substring 0 16 (hash image);
+
+  # The Docker image is loaded and some files from /var/lib/docker/
+  # are written into a qcow image.
+  preload = image: pkgs.vmTools.runInLinuxVM (
+    pkgs.runCommand "docker-preload-image-${sanitizeImageName image}" {
+      buildInputs = with pkgs; [ docker e2fsprogs utillinux curl kmod ];
+      preVM = pkgs.vmTools.createEmptyImage {
+        size = cfg.dockerPreloader.qcowSize;
+        fullName = "docker-deamon-image.qcow2";
+      };
+    }
+    ''
+      mkfs.ext4 /dev/vda
+      e2label /dev/vda ${labelFromImage image}
+      mkdir -p /var/lib/docker
+      mount -t ext4 /dev/vda /var/lib/docker
+
+      modprobe overlay
+
+      # from https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
+      mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
+      cd /sys/fs/cgroup
+      for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
+        mkdir -p $sys
+        if ! mountpoint -q $sys; then
+          if ! mount -n -t cgroup -o $sys cgroup $sys; then
+            rmdir $sys || true
+          fi
+        fi
+      done
+
+      dockerd -H tcp://127.0.0.1:5555 -H unix:///var/run/docker.sock &
+
+      until $(curl --output /dev/null --silent --connect-timeout 2 http://127.0.0.1:5555); do
+        printf '.'
+        sleep 1
+      done
+
+      docker load -i ${image}
+
+      kill %1
+      find /var/lib/docker/ -maxdepth 1 -mindepth 1 -not -name "image" -not -name "overlay2" | xargs rm -rf
+    '');
+
+  preloadedImages = map preload cfg.dockerPreloader.images;
+
+in
+
+{
+  options.virtualisation.dockerPreloader = {
+    images = mkOption {
+      default = [ ];
+      type = types.listOf types.package;
+      description =
+      ''
+        A list of Docker images to preload (in the /var/lib/docker directory).
+      '';
+    };
+    qcowSize = mkOption {
+      default = 1024;
+      type = types.int;
+      description =
+      ''
+        The size (MB) of qcow files.
+      '';
+    };
+  };
+
+  config = {
+    assertions = [{
+      # If docker.storageDriver is null, Docker choose the storage
+      # driver. So, in this case, we cannot be sure overlay2 is used.
+      assertion = cfg.dockerPreloader.images == []
+        || cfg.docker.storageDriver == "overlay2"
+        || cfg.docker.storageDriver == "overlay"
+        || cfg.docker.storageDriver == null;
+      message = "The Docker image Preloader only works with overlay2 storage driver!";
+    }];
+
+    virtualisation.qemu.options =
+      map (path: "-drive if=virtio,file=${path}/disk-image.qcow2,readonly,media=cdrom,format=qcow2")
+      preloadedImages;
+
+
+    # All attached QCOW files are mounted and their contents are linked
+    # to /var/lib/docker/ in order to make image available.
+    systemd.services.docker-preloader = {
+      description = "Preloaded Docker images";
+      wantedBy = ["docker.service"];
+      after = ["network.target"];
+      path = with pkgs; [ mount rsync jq ];
+      script = ''
+        mkdir -p /var/lib/docker/overlay2/l /var/lib/docker/image/overlay2
+        echo '{}' > /tmp/repositories.json
+
+        for i in ${concatStringsSep " " (map labelFromImage cfg.dockerPreloader.images)}; do
+          mkdir -p /mnt/docker-images/$i
+
+          # The ext4 label is limited to 16 bytes
+          mount /dev/disk/by-label/$(echo $i | cut -c1-16) -o ro,noload /mnt/docker-images/$i
+
+          find /mnt/docker-images/$i/overlay2/ -maxdepth 1 -mindepth 1 -not -name l\
+             -exec ln -s '{}' /var/lib/docker/overlay2/ \;
+          cp -P /mnt/docker-images/$i/overlay2/l/* /var/lib/docker/overlay2/l/
+
+          rsync -a /mnt/docker-images/$i/image/ /var/lib/docker/image/
+
+          # Accumulate image definitions
+          cp /tmp/repositories.json /tmp/repositories.json.tmp
+          jq -s '.[0] * .[1]' \
+            /tmp/repositories.json.tmp \
+            /mnt/docker-images/$i/image/overlay2/repositories.json \
+            > /tmp/repositories.json
+        done
+
+        mv /tmp/repositories.json /var/lib/docker/image/overlay2/repositories.json
+      '';
+      serviceConfig = {
+        Type = "oneshot";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index caaf6c0aa59d..795858e5eae2 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -144,7 +144,6 @@ in
     path = with pkgs; [ iproute ];
     serviceConfig = {
       ExecStart = "${gce}/bin/google_network_daemon --debug";
-      Type = "oneshot";
     };
   };
 
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 4e9c87222d0a..ed3431554be4 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -185,7 +185,10 @@ let
 in
 
 {
-  imports = [ ../profiles/qemu-guest.nix ];
+  imports = [
+    ../profiles/qemu-guest.nix
+   ./docker-preloader.nix
+  ];
 
   options = {
 
diff --git a/nixos/release.nix b/nixos/release.nix
index 51505d6aab9d..c3a10c9d3300 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -283,6 +283,7 @@ in rec {
   tests.docker-tools = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools.nix {};
   tests.docker-tools-overlay = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools-overlay.nix {};
   tests.docker-edge = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-edge.nix {};
+  tests.docker-preloader = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-preloader.nix {};
   tests.docker-registry = callTest tests/docker-registry.nix {};
   tests.dovecot = callTest tests/dovecot.nix {};
   tests.dnscrypt-proxy = callTestOnMatchingSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {};
@@ -300,7 +301,7 @@ in rec {
   tests.fsck = callTest tests/fsck.nix {};
   tests.fwupd = callTest tests/fwupd.nix {};
   tests.gdk-pixbuf = callTest tests/gdk-pixbuf.nix {};
-  #tests.gitlab = callTest tests/gitlab.nix {};
+  tests.gitlab = callTest tests/gitlab.nix {};
   tests.gitolite = callTest tests/gitolite.nix {};
   tests.gjs = callTest tests/gjs.nix {};
   tests.gocd-agent = callTest tests/gocd-agent.nix {};
diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix
index c341e83961a8..e5097609fb27 100644
--- a/nixos/tests/chromium.nix
+++ b/nixos/tests/chromium.nix
@@ -12,8 +12,10 @@ with pkgs.lib;
 
 mapAttrs (channel: chromiumPkg: makeTest rec {
   name = "chromium-${channel}";
-  meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ aszlig ];
+  meta = {
+    maintainers = with maintainers; [ aszlig ];
+    # https://github.com/NixOS/hydra/issues/591#issuecomment-435125621
+    inherit (chromiumPkg.meta) timeout;
   };
 
   enableOCR = true;
@@ -166,7 +168,7 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
       my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o");
       die "sandbox not working properly: $clipboard"
-      unless $clipboard =~ /namespace sandbox.*yes/mi
+      unless $clipboard =~ /layer 1 sandbox.*namespace/mi
           && $clipboard =~ /pid namespaces.*yes/mi
           && $clipboard =~ /network namespaces.*yes/mi
           && $clipboard =~ /seccomp.*sandbox.*yes/mi
@@ -184,7 +186,7 @@ mapAttrs (channel: chromiumPkg: makeTest rec {
 
       my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o");
       die "copying twice in a row does not work properly: $clipboard"
-      unless $clipboard =~ /namespace sandbox.*yes/mi
+      unless $clipboard =~ /layer 1 sandbox.*namespace/mi
           && $clipboard =~ /pid namespaces.*yes/mi
           && $clipboard =~ /network namespaces.*yes/mi
           && $clipboard =~ /seccomp.*sandbox.*yes/mi
diff --git a/nixos/tests/docker-preloader.nix b/nixos/tests/docker-preloader.nix
new file mode 100644
index 000000000000..eeedec9a392e
--- /dev/null
+++ b/nixos/tests/docker-preloader.nix
@@ -0,0 +1,27 @@
+import ./make-test.nix ({ pkgs, ...} : {
+  name = "docker-preloader";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ lewo ];
+  };
+
+  nodes = {
+    docker =
+      { pkgs, ... }:
+        {
+          virtualisation.docker.enable = true;
+          virtualisation.dockerPreloader.images = [ pkgs.dockerTools.examples.nix pkgs.dockerTools.examples.bash ];
+
+          services.openssh.enable = true;
+          services.openssh.permitRootLogin = "yes";
+          services.openssh.extraConfig = "PermitEmptyPasswords yes";
+          users.extraUsers.root.password = "";
+        };
+  };    
+  testScript = ''
+    startAll;
+    
+    $docker->waitForUnit("sockets.target");
+    $docker->succeed("docker run nix nix-store --version");
+    $docker->succeed("docker run bash bash --version");
+  '';
+})
diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix
index 3af2cbcd0988..53675c375e31 100644
--- a/nixos/tests/gitlab.nix
+++ b/nixos/tests/gitlab.nix
@@ -1,14 +1,18 @@
 # This test runs gitlab and checks if it works
 
-import ./make-test.nix ({ pkgs, ...} : {
+import ./make-test.nix ({ pkgs, lib, ...} : with lib; {
   name = "gitlab";
   meta = with pkgs.stdenv.lib.maintainers; {
-    maintainers = [ domenkozar offline ];
+    maintainers = [ globin ];
   };
 
   nodes = {
     gitlab = { ... }: {
-      virtualisation.memorySize = 768;
+      virtualisation.memorySize = 4096;
+      systemd.services.gitlab.serviceConfig.Restart = mkForce "no";
+      systemd.services.gitlab-workhorse.serviceConfig.Restart = mkForce "no";
+      systemd.services.gitaly.serviceConfig.Restart = mkForce "no";
+      systemd.services.gitlab-sidekiq.serviceConfig.Restart = mkForce "no";
 
       services.nginx = {
         enable = true;
@@ -19,10 +23,10 @@ import ./make-test.nix ({ pkgs, ...} : {
         };
       };
 
-      systemd.services.gitlab.serviceConfig.TimeoutStartSec = "10min";
       services.gitlab = {
         enable = true;
         databasePassword = "dbPassword";
+        initialRootPassword = "notproduction";
         secrets = {
           secret = "secret";
           otp = "otpsecret";
@@ -65,8 +69,12 @@ import ./make-test.nix ({ pkgs, ...} : {
 
   testScript = ''
     $gitlab->start();
+    $gitlab->waitForUnit("gitaly.service");
+    $gitlab->waitForUnit("gitlab-workhorse.service");
     $gitlab->waitForUnit("gitlab.service");
     $gitlab->waitForUnit("gitlab-sidekiq.service");
-    $gitlab->waitUntilSucceeds("curl http://localhost:80/users/sign_in");
+    $gitlab->waitForFile("/var/gitlab/state/tmp/sockets/gitlab.socket");
+    $gitlab->waitUntilSucceeds("curl -sSf http://localhost/users/sign_in");
+    $gitlab->succeed("${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2")
   '';
 })