1 { config, lib, options, pkgs, utils, ... }:
6 cfg = config.services.gitlab;
7 opt = options.services.gitlab;
9 toml = pkgs.formats.toml {};
10 yaml = pkgs.formats.yaml {};
12 git = cfg.packages.gitaly.git;
14 postgresqlPackage = if config.services.postgresql.enable then
15 config.services.postgresql.package
19 gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
20 gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket";
21 pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
23 gitlabVersionAtLeast = version: lib.versionAtLeast (lib.getVersion cfg.packages.gitlab) version;
27 adapter = "postgresql";
28 database = cfg.databaseName;
29 host = cfg.databaseHost;
30 username = cfg.databaseUsername;
32 pool = cfg.databasePool;
33 } // cfg.extraDatabaseConfig;
36 if (gitlabVersionAtLeast "15.0")
39 ) // lib.optionalAttrs (gitlabVersionAtLeast "15.9") {
41 database_tasks = false;
46 # We only want to create a database if we're actually going to connect to it.
47 databaseActuallyCreateLocally = cfg.databaseCreateLocally && cfg.databaseHost == "";
49 gitalyToml = pkgs.writeText "gitaly.toml" ''
50 socket_path = "${lib.escape ["\""] gitalySocket}"
51 runtime_dir = "/run/gitaly"
52 bin_dir = "${cfg.packages.gitaly}/bin"
53 prometheus_listen_addr = "localhost:9236"
56 bin_path = "${git}/bin/git"
59 dir = "${cfg.packages.gitlab-shell}"
62 custom_hooks_dir = "${cfg.statePath}/custom_hooks"
65 secret_file = "${cfg.statePath}/gitlab_shell_secret"
66 url = "http+unix://${pathUrlQuote gitlabSocket}"
68 [gitlab.http-settings]
69 self_signed_cert = false
71 ${concatStringsSep "\n" (attrValues (mapAttrs (k: v: ''
73 name = "${lib.escape ["\""] k}"
74 path = "${lib.escape ["\""] v.path}"
75 '') gitlabConfig.production.repositories.storages))}
78 gitlabShellConfig = flip recursiveUpdate cfg.extraShellConfig {
80 gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}";
81 http_settings.self_signed_cert = false;
82 repos_path = "${cfg.statePath}/repositories";
83 secret_file = "${cfg.statePath}/gitlab_shell_secret";
84 log_file = "${cfg.statePath}/log/gitlab-shell.log";
87 redisConfig.production.url = cfg.redisUrl;
89 cableYml = yaml.generate "cable.yml" {
93 channel_prefix = "gitlab_production";
97 # Redis configuration file
98 resqueYml = pkgs.writeText "resque.yml" (builtins.toJSON redisConfig);
101 # These are the default settings from config/gitlab.example.yml
102 production = flip recursiveUpdate cfg.extraConfig {
108 email_enabled = true;
109 email_display_name = "GitLab";
110 email_reply_to = "noreply@localhost";
112 default_projects_features = {
114 merge_requests = true;
118 container_registry = true;
121 repositories.storages.default.path = "${cfg.statePath}/repositories";
122 repositories.storages.default.gitaly_address = "unix:${gitalySocket}";
123 artifacts.enabled = true;
125 gravatar.enabled = true;
127 gitlab_ci.builds_path = "${cfg.statePath}/builds";
128 ldap.enabled = false;
129 omniauth.enabled = false;
130 shared.path = "${cfg.statePath}/shared";
131 gitaly.client_path = "${cfg.packages.gitaly}/bin";
133 gitaly_backup_path = "${cfg.packages.gitaly}/bin/gitaly-backup";
134 path = cfg.backup.path;
135 keep_time = cfg.backup.keepTime;
136 } // (optionalAttrs (cfg.backup.uploadOptions != {}) {
137 upload = cfg.backup.uploadOptions;
140 path = "${cfg.packages.gitlab-shell}";
141 hooks_path = "${cfg.statePath}/shell/hooks";
142 secret_file = "${cfg.statePath}/gitlab_shell_secret";
146 workhorse.secret_file = "${cfg.statePath}/.gitlab_workhorse_secret";
147 gitlab_kas.secret_file = "${cfg.statePath}/.gitlab_kas_secret";
148 git.bin_path = "git";
150 ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
153 address = "localhost";
157 registry = lib.optionalAttrs cfg.registry.enable {
159 host = cfg.registry.externalAddress;
160 port = cfg.registry.externalPort;
161 key = cfg.registry.keyFile;
162 api_url = "http://${config.services.dockerRegistry.listenAddress}:${toString config.services.dockerRegistry.port}/";
163 issuer = cfg.registry.issuer;
165 elasticsearch.indexer_path = "${pkgs.gitlab-elasticsearch-indexer}/bin/gitlab-elasticsearch-indexer";
167 uploads.storage_path = cfg.statePath;
168 pages = optionalAttrs cfg.pages.enable {
169 enabled = cfg.pages.enable;
171 host = cfg.pages.settings.pages-domain;
172 secret_file = cfg.pages.settings.api-secret-key;
177 gitlabEnv = cfg.packages.gitlab.gitlabEnv // {
178 HOME = "${cfg.statePath}/home";
179 PUMA_PATH = "${cfg.statePath}/";
180 GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
181 SCHEMA = "${cfg.statePath}/db/structure.sql";
182 GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
183 GITLAB_LOG_PATH = "${cfg.statePath}/log";
184 prometheus_multiproc_dir = "/run/gitlab";
185 RAILS_ENV = "production";
186 MALLOC_ARENA_MAX = "2";
189 runtimeDeps = [ git ] ++ (with pkgs; [
196 findutils # Needed for gitlab:cleanup:orphan_job_artifact_files
199 gitlab-rake = pkgs.stdenv.mkDerivation {
200 name = "gitlab-rake";
201 nativeBuildInputs = [ pkgs.makeWrapper ];
206 makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \
207 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
208 --set PATH '${lib.makeBinPath runtimeDeps}:$PATH' \
209 --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \
210 --chdir '${cfg.packages.gitlab}/share/gitlab'
214 gitlab-rails = pkgs.stdenv.mkDerivation {
215 name = "gitlab-rails";
216 nativeBuildInputs = [ pkgs.makeWrapper ];
221 makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rails $out/bin/gitlab-rails \
222 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \
223 --set PATH '${lib.makeBinPath runtimeDeps}:$PATH' \
224 --chdir '${cfg.packages.gitlab}/share/gitlab'
228 extraGitlabRb = pkgs.writeText "extra-gitlab.rb" cfg.extraGitlabRb;
230 smtpSettings = pkgs.writeText "gitlab-smtp-settings.rb" ''
231 if Rails.env.production?
232 Rails.application.config.action_mailer.delivery_method = :smtp
234 ActionMailer::Base.delivery_method = :smtp
235 ActionMailer::Base.smtp_settings = {
236 address: "${cfg.smtp.address}",
237 port: ${toString cfg.smtp.port},
238 ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''}
239 ${optionalString (cfg.smtp.passwordFile != null) ''password: "@smtpPassword@",''}
240 domain: "${cfg.smtp.domain}",
241 ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"}
242 enable_starttls_auto: ${boolToString cfg.smtp.enableStartTLSAuto},
243 tls: ${boolToString cfg.smtp.tls},
244 ca_file: "/etc/ssl/certs/ca-certificates.crt",
245 openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}'
253 (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
254 (mkRenamedOptionModule [ "services" "gitlab" "backupPath" ] [ "services" "gitlab" "backup" "path" ])
255 (mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ] "")
256 (mkRemovedOptionModule [ "services" "gitlab" "logrotate" "extraConfig" ] "Modify services.logrotate.settings.gitlab directly instead")
257 (mkRemovedOptionModule [ "services" "gitlab" "pagesExtraArgs" ] "Use services.gitlab.pages.settings instead")
266 Enable the gitlab service.
270 packages.gitlab = mkPackageOption pkgs "gitlab" {
271 example = "gitlab-ee";
274 packages.gitlab-shell = mkPackageOption pkgs "gitlab-shell" { };
276 packages.gitlab-workhorse = mkPackageOption pkgs "gitlab-workhorse" { };
278 packages.gitaly = mkPackageOption pkgs "gitaly" { };
280 packages.pages = mkPackageOption pkgs "gitlab-pages" { };
282 statePath = mkOption {
284 default = "/var/gitlab/state";
286 GitLab state directory. Configuration, repositories and
287 logs, among other things, are stored here.
289 The directory will be created automatically if it doesn't
290 exist already. Its parent directories must be owned by
291 either `root` or the user set in
292 {option}`services.gitlab.user`.
296 extraEnv = mkOption {
297 type = types.attrsOf types.str;
300 Additional environment variables for the GitLab environment.
304 backup.startAt = mkOption {
305 type = with types; either str (listOf str);
309 The time(s) to run automatic backup of GitLab
310 state. Specified in systemd's time format; see
311 {manpage}`systemd.time(7)`.
315 backup.path = mkOption {
317 default = cfg.statePath + "/backup";
318 defaultText = literalExpression ''config.${opt.statePath} + "/backup"'';
319 description = "GitLab path for backups.";
322 backup.keepTime = mkOption {
326 apply = x: x * 60 * 60;
328 How long to keep the backups around, in
329 hours. `0` means “keep forever”.
333 backup.skip = mkOption {
347 either value (listOf value);
349 example = [ "artifacts" "lfs" ];
350 apply = x: if isString x then x else concatStringsSep "," x;
352 Directories to exclude from the backup. The example excludes
353 CI artifacts and LFS objects from the backups. The
354 `tar` option skips the creation of a tar
357 Refer to <https://docs.gitlab.com/ee/raketasks/backup_restore.html#excluding-specific-directories-from-the-backup>
358 for more information.
362 backup.uploadOptions = mkOption {
365 example = literalExpression ''
367 # Fog storage connection settings, see http://fog.io/storage/
370 region = "eu-north-1";
371 aws_access_key_id = "AKIAXXXXXXXXXXXXXXXX";
372 aws_secret_access_key = { _secret = config.deployment.keys.aws_access_key.path; };
375 # The remote 'directory' to store your backups in.
376 # For S3, this would be the bucket name.
377 remote_directory = "my-gitlab-backups";
379 # Use multipart uploads when file size reaches 100MB, see
380 # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html
381 multipart_chunk_size = 104857600;
383 # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional
384 encryption = "AES256";
386 # Specifies Amazon S3 storage class to use for backups, this is optional
387 storage_class = "STANDARD";
391 GitLab automatic upload specification. Tells GitLab to
392 upload the backup to a remote location when done.
394 Attributes specified here are added under
395 `production -> backup -> upload` in
396 {file}`config/gitlab.yml`.
400 databaseHost = mkOption {
404 GitLab database hostname. An empty string means
405 “use local unix socket connection”.
409 databasePasswordFile = mkOption {
410 type = with types; nullOr path;
413 File containing the GitLab database user password.
415 This should be a string, not a nix path, since nix paths are
416 copied into the world-readable nix store.
420 databaseCreateLocally = mkOption {
424 Whether a database should be automatically created on the
425 local host. Set this to `false` if you plan
426 on provisioning a local database yourself. This has no effect
427 if {option}`services.gitlab.databaseHost` is customized.
431 databaseName = mkOption {
434 description = "GitLab database name.";
437 databaseUsername = mkOption {
440 description = "GitLab database user.";
443 databasePool = mkOption {
446 description = "Database connection pool size.";
449 extraDatabaseConfig = mkOption {
452 description = "Extra configuration in config/database.yml.";
455 redisUrl = mkOption {
457 default = "unix:/run/gitlab/redis.sock";
458 example = "redis://localhost:6379/";
459 description = "Redis URL for all GitLab services.";
462 extraGitlabRb = mkOption {
466 if Rails.env.production?
467 Rails.application.config.action_mailer.delivery_method = :sendmail
468 ActionMailer::Base.delivery_method = :sendmail
469 ActionMailer::Base.sendmail_settings = {
470 location: "/run/wrappers/bin/sendmail",
476 Extra configuration to be placed in config/extra-gitlab.rb. This can
477 be used to add configuration not otherwise exposed through this module's
484 default = config.networking.hostName;
485 defaultText = literalExpression "config.networking.hostName";
486 description = "GitLab host name. Used e.g. for copy-paste URLs.";
493 GitLab server port for copy-paste URLs, e.g. 80 or 443 if you're
501 description = "Whether gitlab prints URLs with https as scheme.";
507 description = "User to run gitlab and all related services.";
513 description = "Group to run gitlab and all related services.";
516 initialRootEmail = mkOption {
518 default = "admin@local.host";
520 Initial email address of the root account if this is a new install.
524 initialRootPasswordFile = mkOption {
525 type = with types; nullOr path;
528 File containing the initial password of the root account if
529 this is a new install.
531 This should be a string, not a nix path, since nix paths are
532 copied into the world-readable nix store.
540 description = "Enable GitLab container registry.";
543 type = types.package;
545 if versionAtLeast config.system.stateVersion "23.11"
546 then pkgs.gitlab-container-registry
547 else pkgs.docker-distribution;
548 defaultText = literalExpression "pkgs.docker-distribution";
550 Container registry package to use.
552 External container registries such as `pkgs.docker-distribution` are not supported
553 anymore since GitLab 16.0.0.
558 default = config.services.gitlab.host;
559 defaultText = literalExpression "config.services.gitlab.host";
560 description = "GitLab container registry host name.";
565 description = "GitLab container registry port.";
567 certFile = mkOption {
569 description = "Path to GitLab container registry certificate.";
573 description = "Path to GitLab container registry certificate-key.";
575 defaultForProjects = mkOption {
577 default = cfg.registry.enable;
578 defaultText = literalExpression "config.${opt.registry.enable}";
579 description = "If GitLab container registry should be enabled by default for projects.";
583 default = "gitlab-issuer";
584 description = "GitLab container registry issuer.";
586 serviceName = mkOption {
588 default = "container_registry";
589 description = "GitLab container registry service name.";
591 externalAddress = mkOption {
594 description = "External address used to access registry from the internet";
596 externalPort = mkOption {
598 description = "External port used to access registry from the internet";
606 description = "Enable gitlab mail delivery over SMTP.";
611 default = "localhost";
612 description = "Address of the SMTP server for GitLab.";
618 description = "Port of the SMTP server for GitLab.";
621 username = mkOption {
622 type = with types; nullOr str;
624 description = "Username of the SMTP server for GitLab.";
627 passwordFile = mkOption {
628 type = types.nullOr types.path;
631 File containing the password of the SMTP server for GitLab.
633 This should be a string, not a nix path, since nix paths
634 are copied into the world-readable nix store.
640 default = "localhost";
641 description = "HELO domain to use for outgoing mail.";
644 authentication = mkOption {
645 type = with types; nullOr str;
647 description = "Authentication type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
650 enableStartTLSAuto = mkOption {
653 description = "Whether to try to use StartTLS.";
659 description = "Whether to use TLS wrapper-mode.";
662 opensslVerifyMode = mkOption {
665 description = "How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html";
669 pages.enable = mkEnableOption "the GitLab Pages service";
671 pages.settings = mkOption {
672 example = literalExpression ''
674 pages-domain = "example.com";
675 auth-client-id = "generated-id-xxxxxxx";
676 auth-client-secret = { _secret = "/var/keys/auth-client-secret"; };
677 auth-redirect-uri = "https://projects.example.com/auth";
678 auth-secret = { _secret = "/var/keys/auth-secret"; };
679 auth-server = "https://gitlab.example.com";
684 Configuration options to set in the GitLab Pages config
687 Options containing secret data should be set to an attribute
688 set containing the attribute `_secret` - a string pointing
689 to a file containing the value the option should be set
690 to. See the example to get a better picture of this: in the
691 resulting configuration file, the `auth-client-secret` and
692 `auth-secret` keys will be set to the contents of the
693 {file}`/var/keys/auth-client-secret` and
694 {file}`/var/keys/auth-secret` files respectively.
697 type = types.submodule {
698 freeformType = with types; attrsOf (nullOr (oneOf [ str int bool attrs ]));
701 listen-http = mkOption {
702 type = with types; listOf str;
703 apply = x: if x == [] then null else lib.concatStringsSep "," x;
706 The address(es) to listen on for HTTP requests.
710 listen-https = mkOption {
711 type = with types; listOf str;
712 apply = x: if x == [] then null else lib.concatStringsSep "," x;
715 The address(es) to listen on for HTTPS requests.
719 listen-proxy = mkOption {
720 type = with types; listOf str;
721 apply = x: if x == [] then null else lib.concatStringsSep "," x;
722 default = [ "127.0.0.1:8090" ];
724 The address(es) to listen on for proxy requests.
728 artifacts-server = mkOption {
729 type = with types; nullOr str;
730 default = "http${optionalString cfg.https "s"}://${cfg.host}/api/v4";
731 defaultText = "http(s)://<services.gitlab.host>/api/v4";
732 example = "https://gitlab.example.com/api/v4";
734 API URL to proxy artifact requests to.
738 gitlab-server = mkOption {
739 type = with types; nullOr str;
740 default = "http${optionalString cfg.https "s"}://${cfg.host}";
741 defaultText = "http(s)://<services.gitlab.host>";
742 example = "https://gitlab.example.com";
744 Public GitLab server URL.
748 internal-gitlab-server = mkOption {
749 type = with types; nullOr str;
751 defaultText = "http(s)://<services.gitlab.host>";
752 example = "https://gitlab.example.internal";
754 Internal GitLab server used for API requests, useful
755 if you want to send that traffic over an internal load
756 balancer. By default, the value of
757 `services.gitlab.pages.settings.gitlab-server` is
762 api-secret-key = mkOption {
763 type = with types; nullOr str;
764 default = "${cfg.statePath}/gitlab_pages_secret";
767 File with secret key used to authenticate with the
772 pages-domain = mkOption {
773 type = with types; nullOr str;
774 example = "example.com";
776 The domain to serve static pages on.
780 pages-root = mkOption {
782 default = "${gitlabConfig.production.shared.path}/pages";
783 defaultText = literalExpression ''config.${opt.extraConfig}.production.shared.path + "/pages"'';
785 The directory where pages are stored.
792 secrets.secretFile = mkOption {
793 type = with types; nullOr path;
796 A file containing the secret used to encrypt variables in
797 the DB. If you change or lose this key you will be unable to
798 access variables stored in database.
800 Make sure the secret is at least 32 characters and all random,
801 no regular words or you'll be exposed to dictionary attacks.
803 This should be a string, not a nix path, since nix paths are
804 copied into the world-readable nix store.
808 secrets.dbFile = mkOption {
809 type = with types; nullOr path;
812 A file containing the secret used to encrypt variables in
813 the DB. If you change or lose this key you will be unable to
814 access variables stored in database.
816 Make sure the secret is at least 32 characters and all random,
817 no regular words or you'll be exposed to dictionary attacks.
819 This should be a string, not a nix path, since nix paths are
820 copied into the world-readable nix store.
824 secrets.otpFile = mkOption {
825 type = with types; nullOr path;
828 A file containing the secret used to encrypt secrets for OTP
829 tokens. If you change or lose this key, users which have 2FA
830 enabled for login won't be able to login anymore.
832 Make sure the secret is at least 32 characters and all random,
833 no regular words or you'll be exposed to dictionary attacks.
835 This should be a string, not a nix path, since nix paths are
836 copied into the world-readable nix store.
840 secrets.jwsFile = mkOption {
841 type = with types; nullOr path;
844 A file containing the secret used to encrypt session
845 keys. If you change or lose this key, users will be
848 Make sure the secret is an RSA private key in PEM format. You can
853 This should be a string, not a nix path, since nix paths are
854 copied into the world-readable nix store.
858 extraShellConfig = mkOption {
861 description = "Extra configuration to merge into shell-config.yml";
864 puma.workers = mkOption {
867 apply = x: builtins.toString x;
869 The number of worker processes Puma should spawn. This
870 controls the amount of parallel Ruby code can be
871 executed. GitLab recommends `Number of CPU cores - 1`, but at least two.
874 Each worker consumes quite a bit of memory, so
875 be careful when increasing this.
880 puma.threadsMin = mkOption {
883 apply = x: builtins.toString x;
885 The minimum number of threads Puma should use per
889 Each thread consumes memory and contributes to Global VM
890 Lock contention, so be careful when increasing this.
895 puma.threadsMax = mkOption {
898 apply = x: builtins.toString x;
900 The maximum number of threads Puma should use per
901 worker. This limits how many threads Puma will automatically
902 spawn in response to requests. In contrast to workers,
903 threads will never be able to run Ruby code in parallel, but
904 give higher IO parallelism.
907 Each thread consumes memory and contributes to Global VM
908 Lock contention, so be careful when increasing this.
913 sidekiq.concurrency = mkOption {
914 type = with types; nullOr int;
917 How many processor threads to use for processing sidekiq background job queues. When null, the GitLab default is used.
919 See <https://docs.gitlab.com/ee/administration/sidekiq/extra_sidekiq_processes.html#manage-thread-counts-explicitly> for details.
923 sidekiq.memoryKiller.enable = mkOption {
927 Whether the Sidekiq MemoryKiller should be turned
928 on. MemoryKiller kills Sidekiq when its memory consumption
929 exceeds a certain limit.
931 See <https://docs.gitlab.com/ee/administration/operations/sidekiq_memory_killer.html>
936 sidekiq.memoryKiller.maxMemory = mkOption {
939 apply = x: builtins.toString (x * 1024);
941 The maximum amount of memory, in MiB, a Sidekiq worker is
942 allowed to consume before being killed.
946 sidekiq.memoryKiller.graceTime = mkOption {
949 apply = x: builtins.toString x;
951 The time MemoryKiller waits after noticing excessive memory
952 consumption before killing Sidekiq.
956 sidekiq.memoryKiller.shutdownWait = mkOption {
959 apply = x: builtins.toString x;
961 The time allowed for all jobs to finish before Sidekiq is
971 Enable rotation of log files.
975 frequency = mkOption {
978 description = "How often to rotate the logs.";
984 description = "How many rotations to keep.";
988 workhorse.config = mkOption {
991 example = literalExpression ''
993 object_storage.provider = "AWS";
994 object_storage.s3 = {
995 aws_access_key_id = "AKIAXXXXXXXXXXXXXXXX";
996 aws_secret_access_key = { _secret = "/var/keys/aws_secret_access_key"; };
1001 Configuration options to add to Workhorse's configuration
1005 <https://gitlab.com/gitlab-org/gitlab/-/blob/master/workhorse/config.toml.example>
1007 <https://docs.gitlab.com/ee/development/workhorse/configuration.html>
1008 for examples and option documentation.
1010 Options containing secret data should be set to an attribute
1011 set containing the attribute `_secret` - a string pointing
1012 to a file containing the value the option should be set
1013 to. See the example to get a better picture of this: in the
1014 resulting configuration file, the
1015 `object_storage.s3.aws_secret_access_key` key will be set to
1016 the contents of the {file}`/var/keys/aws_secret_access_key`
1021 extraConfig = mkOption {
1024 example = literalExpression ''
1027 default_projects_features = {
1033 auto_sign_in_with_provider = "openid_connect";
1034 allow_single_sign_on = ["openid_connect"];
1035 block_auto_created_users = false;
1038 name = "openid_connect";
1039 label = "OpenID Connect";
1041 name = "openid_connect";
1042 scope = ["openid" "profile"];
1043 response_type = "code";
1044 issuer = "https://keycloak.example.com/auth/realms/My%20Realm";
1046 client_auth_method = "query";
1047 uid_field = "preferred_username";
1049 identifier = "gitlab";
1050 secret = { _secret = "/var/keys/gitlab_oidc_secret"; };
1051 redirect_uri = "https://git.example.com/users/auth/openid_connect/callback";
1060 Extra options to be added under
1062 {file}`config/gitlab.yml`, as a nix attribute
1065 Options containing secret data should be set to an attribute
1066 set containing the attribute `_secret` - a
1067 string pointing to a file containing the value the option
1068 should be set to. See the example to get a better picture of
1069 this: in the resulting
1070 {file}`config/gitlab.yml` file, the
1071 `production.omniauth.providers[0].args.client_options.secret`
1072 key will be set to the contents of the
1073 {file}`/var/keys/gitlab_oidc_secret` file.
1079 config = mkIf cfg.enable {
1082 (cfg.registry.enable && versionAtLeast (getVersion cfg.packages.gitlab) "16.0.0" && cfg.registry.package == pkgs.docker-distribution)
1083 ''Support for container registries other than gitlab-container-registry has ended since GitLab 16.0.0 and is scheduled for removal in a future release.
1084 Please back up your data and migrate to the gitlab-container-registry package.''
1087 (versionAtLeast (getVersion cfg.packages.gitlab) "16.2.0" && versionOlder (getVersion cfg.packages.gitlab) "16.5.0")
1088 ''GitLab instances created or updated between versions [15.11.0, 15.11.2] have an incorrect database schema.
1089 Check the upstream documentation for a workaround: https://docs.gitlab.com/ee/update/versions/gitlab_16_changes.html#undefined-column-error-upgrading-to-162-or-later''
1095 assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.databaseUsername);
1096 message = ''For local automatic database provisioning (services.gitlab.databaseCreateLocally == true) with peer authentication (services.gitlab.databaseHost == "") to work services.gitlab.user and services.gitlab.databaseUsername must be identical.'';
1099 assertion = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null);
1100 message = "When services.gitlab.databaseHost is customized, services.gitlab.databasePasswordFile must be set!";
1103 assertion = cfg.initialRootPasswordFile != null;
1104 message = "services.gitlab.initialRootPasswordFile must be set!";
1107 assertion = cfg.secrets.secretFile != null;
1108 message = "services.gitlab.secrets.secretFile must be set!";
1111 assertion = cfg.secrets.dbFile != null;
1112 message = "services.gitlab.secrets.dbFile must be set!";
1115 assertion = cfg.secrets.otpFile != null;
1116 message = "services.gitlab.secrets.otpFile must be set!";
1119 assertion = cfg.secrets.jwsFile != null;
1120 message = "services.gitlab.secrets.jwsFile must be set!";
1123 assertion = versionAtLeast postgresqlPackage.version "14.9";
1124 message = "PostgreSQL >= 14.9 is required to run GitLab 17. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading";
1128 environment.systemPackages = [ gitlab-rake gitlab-rails cfg.packages.gitlab-shell ];
1130 systemd.slices.system-gitlab = {
1131 description = "GitLab DevOps Platform Slice";
1132 documentation = [ "https://docs.gitlab.com/" ];
1135 systemd.targets.gitlab = {
1136 description = "Common target for all GitLab services.";
1137 wantedBy = [ "multi-user.target" ];
1140 # Redis is required for the sidekiq queue runner.
1141 services.redis.servers.gitlab = {
1142 enable = mkDefault true;
1143 user = mkDefault cfg.user;
1144 unixSocket = mkDefault "/run/gitlab/redis.sock";
1145 unixSocketPerm = mkDefault 770;
1148 # We use postgres as the main data store.
1149 services.postgresql = optionalAttrs databaseActuallyCreateLocally {
1151 ensureUsers = singleton { name = cfg.databaseUsername; };
1154 # Enable rotation of log files
1155 services.logrotate = {
1156 enable = cfg.logrotate.enable;
1159 files = "${cfg.statePath}/log/*.log";
1160 su = "${cfg.user} ${cfg.group}";
1161 frequency = cfg.logrotate.frequency;
1162 rotate = cfg.logrotate.keep;
1163 copytruncate = true;
1169 # The postgresql module doesn't currently support concepts like
1170 # objects owners and extensions; for now we tack on what's needed
1172 systemd.services.gitlab-postgresql = let pgsql = config.services.postgresql; in mkIf databaseActuallyCreateLocally {
1173 after = [ "postgresql.service" ];
1174 bindsTo = [ "postgresql.service" ];
1175 wantedBy = [ "gitlab.target" ];
1176 partOf = [ "gitlab.target" ];
1185 psql --port=${toString pgsql.settings.port} "$@"
1188 PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"'
1189 current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'")
1190 if [[ "$current_owner" != "${cfg.databaseUsername}" ]]; then
1191 PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"'
1192 if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" ]]; then
1193 echo "Reassigning ownership of database ${cfg.databaseName} to user ${cfg.databaseUsername} failed on last boot. Failing..."
1196 touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}"
1197 PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\""
1198 rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}"
1200 PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm"
1201 PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS btree_gist;"
1205 Slice = "system-gitlab.slice";
1206 User = pgsql.superUser;
1208 RemainAfterExit = true;
1212 systemd.services.gitlab-registry-cert = optionalAttrs cfg.registry.enable {
1213 path = with pkgs; [ openssl ];
1216 mkdir -p $(dirname ${cfg.registry.keyFile})
1217 mkdir -p $(dirname ${cfg.registry.certFile})
1218 openssl req -nodes -newkey rsa:4096 -keyout ${cfg.registry.keyFile} -out /tmp/registry-auth.csr -subj "/CN=${cfg.registry.issuer}"
1219 openssl x509 -in /tmp/registry-auth.csr -out ${cfg.registry.certFile} -req -signkey ${cfg.registry.keyFile} -days 3650
1220 chown ${cfg.user}:${cfg.group} $(dirname ${cfg.registry.keyFile})
1221 chown ${cfg.user}:${cfg.group} $(dirname ${cfg.registry.certFile})
1222 chown ${cfg.user}:${cfg.group} ${cfg.registry.keyFile}
1223 chown ${cfg.user}:${cfg.group} ${cfg.registry.certFile}
1227 ConditionPathExists = "!${cfg.registry.certFile}";
1230 Slice = "system-gitlab.slice";
1234 # Ensure Docker Registry launches after the certificate generation job
1235 systemd.services.docker-registry = optionalAttrs cfg.registry.enable {
1236 wants = [ "gitlab-registry-cert.service" ];
1237 after = [ "gitlab-registry-cert.service" ];
1240 # Enable Docker Registry, if GitLab-Container Registry is enabled
1241 services.dockerRegistry = optionalAttrs cfg.registry.enable {
1243 enableDelete = true; # This must be true, otherwise GitLab won't manage it correctly
1244 package = cfg.registry.package;
1247 realm = "http${optionalString (cfg.https == true) "s"}://${cfg.host}/jwt/auth";
1248 service = cfg.registry.serviceName;
1249 issuer = cfg.registry.issuer;
1250 rootcertbundle = cfg.registry.certFile;
1255 # Use postfix to send out mails.
1256 services.postfix.enable = mkDefault (cfg.smtp.enable && cfg.smtp.address == "localhost");
1258 users.users.${cfg.user} =
1259 { group = cfg.group;
1260 home = "${cfg.statePath}/home";
1261 shell = "${pkgs.bash}/bin/bash";
1262 uid = config.ids.uids.gitlab;
1265 users.groups.${cfg.group}.gid = config.ids.gids.gitlab;
1267 systemd.tmpfiles.rules = [
1268 "d /run/gitlab 0755 ${cfg.user} ${cfg.group} -"
1269 "d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -"
1270 "z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -"
1271 "d ${cfg.backup.path} 0750 ${cfg.user} ${cfg.group} -"
1272 "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -"
1273 "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -"
1274 "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -"
1275 "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -"
1276 "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -"
1277 "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -"
1278 "d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -"
1279 "d ${cfg.statePath}/tmp 0750 ${cfg.user} ${cfg.group} -"
1280 "d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -"
1281 "d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -"
1282 "d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -"
1283 "d ${cfg.statePath}/custom_hooks 0700 ${cfg.user} ${cfg.group} -"
1284 "d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -"
1285 "d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -"
1286 "d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -"
1287 "d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -"
1288 "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -"
1289 "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -"
1290 "d ${gitlabConfig.production.shared.path}/packages 0750 ${cfg.user} ${cfg.group} -"
1291 "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -"
1292 "d ${gitlabConfig.production.shared.path}/registry 0750 ${cfg.user} ${cfg.group} -"
1293 "d ${gitlabConfig.production.shared.path}/terraform_state 0750 ${cfg.user} ${cfg.group} -"
1294 "d ${gitlabConfig.production.shared.path}/ci_secure_files 0750 ${cfg.user} ${cfg.group} -"
1295 "d ${gitlabConfig.production.shared.path}/external-diffs 0750 ${cfg.user} ${cfg.group} -"
1296 "L+ /run/gitlab/config - - - - ${cfg.statePath}/config"
1297 "L+ /run/gitlab/log - - - - ${cfg.statePath}/log"
1298 "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp"
1299 "L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads"
1301 "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}"
1305 systemd.services.gitlab-config = {
1306 wantedBy = [ "gitlab.target" ];
1307 partOf = [ "gitlab.target" ];
1308 path = [ git ] ++ (with pkgs; [
1317 TimeoutSec = "infinity";
1318 Restart = "on-failure";
1319 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
1320 Slice = "system-gitlab.slice";
1321 RemainAfterExit = true;
1324 preStartFullPrivileges = ''
1325 set -o errexit -o pipefail -o nounset
1326 shopt -s dotglob nullglob inherit_errexit
1328 chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/*
1329 if [[ -n "$(ls -A '${cfg.statePath}'/config/)" ]]; then
1330 chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/*
1333 in "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}";
1335 ExecStart = pkgs.writeShellScript "gitlab-config" ''
1336 set -o errexit -o pipefail -o nounset
1337 shopt -s inherit_errexit
1341 cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
1342 rm -rf ${cfg.statePath}/db/*
1343 rm -f ${cfg.statePath}/lib
1344 find '${cfg.statePath}/config/' -maxdepth 1 -mindepth 1 -type d -execdir rm -rf {} \;
1345 cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
1346 cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db
1347 ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb
1348 ln -sf ${cableYml} ${cfg.statePath}/config/cable.yml
1349 ln -sf ${resqueYml} ${cfg.statePath}/config/resque.yml
1351 ${cfg.packages.gitlab-shell}/bin/gitlab-shell-install
1353 ${optionalString cfg.smtp.enable ''
1354 install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb
1355 ${optionalString (cfg.smtp.passwordFile != null) ''
1356 replace-secret '@smtpPassword@' '${cfg.smtp.passwordFile}' '${cfg.statePath}/config/initializers/smtp_settings.rb'
1363 openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret
1364 ${optionalString cfg.pages.enable ''
1365 openssl rand -base64 32 > ${cfg.pages.settings.api-secret-key}
1368 rm -f '${cfg.statePath}/config/database.yml'
1370 ${lib.optionalString (cfg.databasePasswordFile != null) ''
1371 db_password="$(<'${cfg.databasePasswordFile}')"
1374 if [[ -z "$db_password" ]]; then
1375 >&2 echo "Database password was an empty string!"
1380 # GitLab expects the `production.main` section to be the first entry in the file.
1381 jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} '{
1383 ${lib.optionalString (cfg.databasePasswordFile != null) (
1384 builtins.concatStringsSep "\n " (
1385 [ ".production${lib.optionalString (gitlabVersionAtLeast "15.0") ".main"}.password = $ENV.db_password" ]
1386 ++ lib.optional (gitlabVersionAtLeast "15.9") "| .production.ci.password = $ENV.db_password"
1395 }' >'${cfg.statePath}/config/database.yml'
1397 ${utils.genJqSecretsReplacementSnippet
1399 "${cfg.statePath}/config/gitlab.yml"
1402 rm -f '${cfg.statePath}/config/secrets.yml'
1404 secret="$(<'${cfg.secrets.secretFile}')"
1405 db="$(<'${cfg.secrets.dbFile}')"
1406 otp="$(<'${cfg.secrets.otpFile}')"
1407 jws="$(<'${cfg.secrets.jwsFile}')"
1408 export secret db otp jws
1409 jq -n '{production: {secret_key_base: $ENV.secret,
1410 otp_key_base: $ENV.otp,
1411 db_key_base: $ENV.db,
1412 openid_connect_signing_key: $ENV.jws}}' \
1413 > '${cfg.statePath}/config/secrets.yml'
1416 # We remove potentially broken links to old gitlab-shell versions
1417 rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks
1419 git config --global core.autocrlf "input"
1424 systemd.services.gitlab-db-config = {
1425 after = [ "gitlab-config.service" "gitlab-postgresql.service" "postgresql.service" ];
1426 wants = optional (cfg.databaseHost == "") "postgresql.service" ++ optional databaseActuallyCreateLocally "gitlab-postgresql.service";
1427 bindsTo = [ "gitlab-config.service" ];
1428 wantedBy = [ "gitlab.target" ];
1429 partOf = [ "gitlab.target" ];
1434 TimeoutSec = "infinity";
1435 Restart = "on-failure";
1436 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
1437 Slice = "system-gitlab.slice";
1438 RemainAfterExit = true;
1440 ExecStart = pkgs.writeShellScript "gitlab-db-config" ''
1441 set -o errexit -o pipefail -o nounset
1442 shopt -s inherit_errexit
1445 initial_root_password="$(<'${cfg.initialRootPasswordFile}')"
1446 ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \
1447 GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' > /dev/null
1452 systemd.services.gitlab-sidekiq = {
1455 "redis-gitlab.service"
1456 "postgresql.service"
1457 "gitlab-config.service"
1458 "gitlab-db-config.service"
1461 "gitlab-config.service"
1462 "gitlab-db-config.service"
1464 wants = [ "redis-gitlab.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service";
1465 wantedBy = [ "gitlab.target" ];
1466 partOf = [ "gitlab.target" ];
1467 environment = gitlabEnv // (optionalAttrs cfg.sidekiq.memoryKiller.enable {
1468 SIDEKIQ_MEMORY_KILLER_MAX_RSS = cfg.sidekiq.memoryKiller.maxMemory;
1469 SIDEKIQ_MEMORY_KILLER_GRACE_TIME = cfg.sidekiq.memoryKiller.graceTime;
1470 SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT = cfg.sidekiq.memoryKiller.shutdownWait;
1472 path = [ git ] ++ (with pkgs; [
1479 "${cfg.packages.gitlab}/share/gitlab/vendor/gems/sidekiq-${cfg.packages.gitlab.rubyEnv.gems.sidekiq.version}"
1481 # Needed for GitLab project imports
1485 procps # Sidekiq MemoryKiller
1491 TimeoutSec = "infinity";
1493 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
1494 Slice = "system-gitlab.slice";
1495 ExecStart = utils.escapeSystemdExecArgs (
1497 "${cfg.packages.gitlab}/share/gitlab/bin/sidekiq-cluster"
1498 "*" # all queue groups
1499 ] ++ lib.optionals (cfg.sidekiq.concurrency != null) [
1500 "--concurrency" (toString cfg.sidekiq.concurrency)
1502 "--environment" "production"
1509 systemd.services.gitaly = {
1510 after = [ "network.target" "gitlab-config.service" ];
1511 bindsTo = [ "gitlab-config.service" ];
1512 wantedBy = [ "gitlab.target" ];
1513 partOf = [ "gitlab.target" ];
1514 path = [ git ] ++ (with pkgs; [
1523 TimeoutSec = "infinity";
1524 Restart = "on-failure";
1525 WorkingDirectory = gitlabEnv.HOME;
1526 RuntimeDirectory = "gitaly";
1527 Slice = "system-gitlab.slice";
1528 ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}";
1532 services.gitlab.pages.settings = {
1533 api-secret-key = "${cfg.statePath}/gitlab_pages_secret";
1536 systemd.services.gitlab-pages =
1538 filteredConfig = filterAttrs (_: v: v != null) cfg.pages.settings;
1539 isSecret = v: isAttrs v && v ? _secret && isString v._secret;
1540 mkPagesKeyValue = lib.generators.toKeyValue {
1541 mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" rec {
1543 if isInt v then toString v
1544 else if isString v then v
1545 else if true == v then "true"
1546 else if false == v then "false"
1547 else if isSecret v then builtins.hashString "sha256" v._secret
1548 else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}";
1551 secretPaths = lib.catAttrs "_secret" (lib.collect isSecret filteredConfig);
1552 mkSecretReplacement = file: ''
1553 replace-secret ${lib.escapeShellArgs [ (builtins.hashString "sha256" file) file "/run/gitlab-pages/gitlab-pages.conf" ]}
1555 secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
1556 configFile = pkgs.writeText "gitlab-pages.conf" (mkPagesKeyValue filteredConfig);
1558 mkIf cfg.pages.enable {
1559 description = "GitLab static pages daemon";
1560 after = [ "network.target" "gitlab-config.service" "gitlab.service" ];
1561 bindsTo = [ "gitlab-config.service" "gitlab.service" ];
1562 wantedBy = [ "gitlab.target" ];
1563 partOf = [ "gitlab.target" ];
1572 TimeoutSec = "infinity";
1573 Restart = "on-failure";
1578 ExecStartPre = pkgs.writeShellScript "gitlab-pages-pre-start" ''
1579 set -o errexit -o pipefail -o nounset
1580 shopt -s dotglob nullglob inherit_errexit
1582 install -m u=rw ${configFile} /run/gitlab-pages/gitlab-pages.conf
1583 ${secretReplacements}
1585 ExecStart = "${cfg.packages.pages}/bin/gitlab-pages -config=/run/gitlab-pages/gitlab-pages.conf";
1586 WorkingDirectory = gitlabEnv.HOME;
1587 RuntimeDirectory = "gitlab-pages";
1588 RuntimeDirectoryMode = "0700";
1589 Slice = "system-gitlab.slice";
1593 systemd.services.gitlab-workhorse = {
1594 after = [ "network.target" ];
1595 wantedBy = [ "gitlab.target" ];
1596 partOf = [ "gitlab.target" ];
1597 path = [ git ] ++ (with pkgs; [
1604 cfg.packages.gitlab-workhorse
1610 TimeoutSec = "infinity";
1611 Restart = "on-failure";
1612 WorkingDirectory = gitlabEnv.HOME;
1613 Slice = "system-gitlab.slice";
1614 ExecStartPre = pkgs.writeShellScript "gitlab-workhorse-pre-start" ''
1615 set -o errexit -o pipefail -o nounset
1616 shopt -s dotglob nullglob inherit_errexit
1618 ${utils.genJqSecretsReplacementSnippet
1619 cfg.workhorse.config
1620 "${cfg.statePath}/config/gitlab-workhorse.json"}
1622 json2toml "${cfg.statePath}/config/gitlab-workhorse.json" "${cfg.statePath}/config/gitlab-workhorse.toml"
1623 rm "${cfg.statePath}/config/gitlab-workhorse.json"
1626 "${cfg.packages.gitlab-workhorse}/bin/${
1627 optionalString (lib.versionAtLeast (lib.getVersion cfg.packages.gitlab-workhorse) "16.10") "gitlab-"
1630 + "-listenNetwork unix "
1631 + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
1632 + "-authSocket ${gitlabSocket} "
1633 + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public "
1634 + "-config ${cfg.statePath}/config/gitlab-workhorse.toml "
1635 + "-secretPath ${cfg.statePath}/.gitlab_workhorse_secret";
1639 systemd.services.gitlab-mailroom = mkIf (gitlabConfig.production.incoming_email.enabled or false) {
1640 description = "GitLab incoming mail daemon";
1641 after = [ "network.target" "redis-gitlab.service" "gitlab-config.service" ];
1642 bindsTo = [ "gitlab-config.service" ];
1643 wantedBy = [ "gitlab.target" ];
1644 partOf = [ "gitlab.target" ];
1645 environment = gitlabEnv;
1648 TimeoutSec = "infinity";
1649 Restart = "on-failure";
1653 ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec mail_room -c ${cfg.statePath}/config/mail_room.yml";
1654 WorkingDirectory = gitlabEnv.HOME;
1655 Slice = "system-gitlab.slice";
1659 systemd.services.gitlab = {
1661 "gitlab-workhorse.service"
1663 "redis-gitlab.service"
1664 "gitlab-config.service"
1665 "gitlab-db-config.service"
1668 "gitlab-config.service"
1669 "gitlab-db-config.service"
1671 wants = [ "redis-gitlab.service" ] ++ optional (cfg.databaseHost == "") "postgresql.service";
1672 requiredBy = [ "gitlab.target" ];
1673 partOf = [ "gitlab.target" ];
1674 environment = gitlabEnv;
1675 path = [ git ] ++ (with pkgs; [
1687 TimeoutSec = "infinity";
1688 Restart = "on-failure";
1689 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
1690 Slice = "system-gitlab.slice";
1691 ExecStart = concatStringsSep " " [
1692 "${cfg.packages.gitlab.rubyEnv}/bin/bundle" "exec" "puma"
1694 "-C ${cfg.statePath}/config/puma.rb"
1695 "-w ${cfg.puma.workers}"
1696 "-t ${cfg.puma.threadsMin}:${cfg.puma.threadsMax}"
1702 systemd.services.gitlab-backup = {
1703 after = [ "gitlab.service" ];
1704 bindsTo = [ "gitlab.service" ];
1705 startAt = cfg.backup.startAt;
1707 RAILS_ENV = "production";
1709 } // optionalAttrs (stringLength cfg.backup.skip > 0) {
1710 SKIP = cfg.backup.skip;
1715 Slice = "system-gitlab.slice";
1716 ExecStart = "${gitlab-rake}/bin/gitlab-rake gitlab:backup:create";
1722 meta.doc = ./gitlab.md;
1723 meta.maintainers = teams.gitlab.members;