at 22.05-pre 50 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4 5let 6 cfg = config.services.gitlab; 7 8 ruby = cfg.packages.gitlab.ruby; 9 10 postgresqlPackage = if config.services.postgresql.enable then 11 config.services.postgresql.package 12 else 13 pkgs.postgresql_12; 14 15 gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket"; 16 gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket"; 17 pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url; 18 19 databaseConfig = { 20 production = { 21 adapter = "postgresql"; 22 database = cfg.databaseName; 23 host = cfg.databaseHost; 24 username = cfg.databaseUsername; 25 encoding = "utf8"; 26 pool = cfg.databasePool; 27 } // cfg.extraDatabaseConfig; 28 }; 29 30 # We only want to create a database if we're actually going to connect to it. 31 databaseActuallyCreateLocally = cfg.databaseCreateLocally && cfg.databaseHost == ""; 32 33 gitalyToml = pkgs.writeText "gitaly.toml" '' 34 socket_path = "${lib.escape ["\""] gitalySocket}" 35 bin_dir = "${cfg.packages.gitaly}/bin" 36 prometheus_listen_addr = "localhost:9236" 37 38 [git] 39 bin_path = "${pkgs.git}/bin/git" 40 41 [gitaly-ruby] 42 dir = "${cfg.packages.gitaly.ruby}" 43 44 [gitlab-shell] 45 dir = "${cfg.packages.gitlab-shell}" 46 47 [hooks] 48 custom_hooks_dir = "${cfg.statePath}/custom_hooks" 49 50 [gitlab] 51 secret_file = "${cfg.statePath}/gitlab_shell_secret" 52 url = "http+unix://${pathUrlQuote gitlabSocket}" 53 54 [gitlab.http-settings] 55 self_signed_cert = false 56 57 ${concatStringsSep "\n" (attrValues (mapAttrs (k: v: '' 58 [[storage]] 59 name = "${lib.escape ["\""] k}" 60 path = "${lib.escape ["\""] v.path}" 61 '') gitlabConfig.production.repositories.storages))} 62 ''; 63 64 gitlabShellConfig = flip recursiveUpdate cfg.extraShellConfig { 65 user = cfg.user; 66 gitlab_url = "http+unix://${pathUrlQuote gitlabSocket}"; 67 http_settings.self_signed_cert = false; 68 repos_path = "${cfg.statePath}/repositories"; 69 secret_file = "${cfg.statePath}/gitlab_shell_secret"; 70 log_file = "${cfg.statePath}/log/gitlab-shell.log"; 71 redis = { 72 bin = "${pkgs.redis}/bin/redis-cli"; 73 host = "127.0.0.1"; 74 port = 6379; 75 database = 0; 76 namespace = "resque:gitlab"; 77 }; 78 }; 79 80 redisConfig.production.url = cfg.redisUrl; 81 82 pagesArgs = [ 83 "-pages-domain" gitlabConfig.production.pages.host 84 "-pages-root" "${gitlabConfig.production.shared.path}/pages" 85 ] ++ cfg.pagesExtraArgs; 86 87 gitlabConfig = { 88 # These are the default settings from config/gitlab.example.yml 89 production = flip recursiveUpdate cfg.extraConfig { 90 gitlab = { 91 host = cfg.host; 92 port = cfg.port; 93 https = cfg.https; 94 user = cfg.user; 95 email_enabled = true; 96 email_display_name = "GitLab"; 97 email_reply_to = "noreply@localhost"; 98 default_theme = 2; 99 default_projects_features = { 100 issues = true; 101 merge_requests = true; 102 wiki = true; 103 snippets = true; 104 builds = true; 105 container_registry = true; 106 }; 107 }; 108 repositories.storages.default.path = "${cfg.statePath}/repositories"; 109 repositories.storages.default.gitaly_address = "unix:${gitalySocket}"; 110 artifacts.enabled = true; 111 lfs.enabled = true; 112 gravatar.enabled = true; 113 cron_jobs = { }; 114 gitlab_ci.builds_path = "${cfg.statePath}/builds"; 115 ldap.enabled = false; 116 omniauth.enabled = false; 117 shared.path = "${cfg.statePath}/shared"; 118 gitaly.client_path = "${cfg.packages.gitaly}/bin"; 119 backup = { 120 gitaly_backup_path = "${cfg.packages.gitaly}/bin/gitaly-backup"; 121 path = cfg.backup.path; 122 keep_time = cfg.backup.keepTime; 123 } // (optionalAttrs (cfg.backup.uploadOptions != {}) { 124 upload = cfg.backup.uploadOptions; 125 }); 126 gitlab_shell = { 127 path = "${cfg.packages.gitlab-shell}"; 128 hooks_path = "${cfg.statePath}/shell/hooks"; 129 secret_file = "${cfg.statePath}/gitlab_shell_secret"; 130 upload_pack = true; 131 receive_pack = true; 132 }; 133 workhorse.secret_file = "${cfg.statePath}/.gitlab_workhorse_secret"; 134 gitlab_kas.secret_file = "${cfg.statePath}/.gitlab_kas_secret"; 135 git.bin_path = "git"; 136 monitoring = { 137 ip_whitelist = [ "127.0.0.0/8" "::1/128" ]; 138 sidekiq_exporter = { 139 enable = true; 140 address = "localhost"; 141 port = 3807; 142 }; 143 }; 144 registry = lib.optionalAttrs cfg.registry.enable { 145 enabled = true; 146 host = cfg.registry.externalAddress; 147 port = cfg.registry.externalPort; 148 key = cfg.registry.keyFile; 149 api_url = "http://${config.services.dockerRegistry.listenAddress}:${toString config.services.dockerRegistry.port}/"; 150 issuer = "gitlab-issuer"; 151 }; 152 extra = {}; 153 uploads.storage_path = cfg.statePath; 154 }; 155 }; 156 157 gitlabEnv = cfg.packages.gitlab.gitlabEnv // { 158 HOME = "${cfg.statePath}/home"; 159 PUMA_PATH = "${cfg.statePath}/"; 160 GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/"; 161 SCHEMA = "${cfg.statePath}/db/structure.sql"; 162 GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; 163 GITLAB_LOG_PATH = "${cfg.statePath}/log"; 164 GITLAB_REDIS_CONFIG_FILE = pkgs.writeText "redis.yml" (builtins.toJSON redisConfig); 165 prometheus_multiproc_dir = "/run/gitlab"; 166 RAILS_ENV = "production"; 167 MALLOC_ARENA_MAX = "2"; 168 } // cfg.extraEnv; 169 170 gitlab-rake = pkgs.stdenv.mkDerivation { 171 name = "gitlab-rake"; 172 buildInputs = [ pkgs.makeWrapper ]; 173 dontBuild = true; 174 dontUnpack = true; 175 installPhase = '' 176 mkdir -p $out/bin 177 makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \ 178 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \ 179 --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar postgresqlPackage pkgs.coreutils pkgs.procps ]}:$PATH' \ 180 --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \ 181 --run 'cd ${cfg.packages.gitlab}/share/gitlab' 182 ''; 183 }; 184 185 gitlab-rails = pkgs.stdenv.mkDerivation { 186 name = "gitlab-rails"; 187 buildInputs = [ pkgs.makeWrapper ]; 188 dontBuild = true; 189 dontUnpack = true; 190 installPhase = '' 191 mkdir -p $out/bin 192 makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rails $out/bin/gitlab-rails \ 193 ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \ 194 --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar postgresqlPackage pkgs.coreutils pkgs.procps ]}:$PATH' \ 195 --run 'cd ${cfg.packages.gitlab}/share/gitlab' 196 ''; 197 }; 198 199 extraGitlabRb = pkgs.writeText "extra-gitlab.rb" cfg.extraGitlabRb; 200 201 smtpSettings = pkgs.writeText "gitlab-smtp-settings.rb" '' 202 if Rails.env.production? 203 Rails.application.config.action_mailer.delivery_method = :smtp 204 205 ActionMailer::Base.delivery_method = :smtp 206 ActionMailer::Base.smtp_settings = { 207 address: "${cfg.smtp.address}", 208 port: ${toString cfg.smtp.port}, 209 ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''} 210 ${optionalString (cfg.smtp.passwordFile != null) ''password: "@smtpPassword@",''} 211 domain: "${cfg.smtp.domain}", 212 ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"} 213 enable_starttls_auto: ${boolToString cfg.smtp.enableStartTLSAuto}, 214 tls: ${boolToString cfg.smtp.tls}, 215 ca_file: "/etc/ssl/certs/ca-certificates.crt", 216 openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}' 217 } 218 end 219 ''; 220 221in { 222 223 imports = [ 224 (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ]) 225 (mkRenamedOptionModule [ "services" "gitlab" "backupPath" ] [ "services" "gitlab" "backup" "path" ]) 226 (mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ] "") 227 ]; 228 229 options = { 230 services.gitlab = { 231 enable = mkOption { 232 type = types.bool; 233 default = false; 234 description = '' 235 Enable the gitlab service. 236 ''; 237 }; 238 239 packages.gitlab = mkOption { 240 type = types.package; 241 default = pkgs.gitlab; 242 defaultText = literalExpression "pkgs.gitlab"; 243 description = "Reference to the gitlab package"; 244 example = literalExpression "pkgs.gitlab-ee"; 245 }; 246 247 packages.gitlab-shell = mkOption { 248 type = types.package; 249 default = pkgs.gitlab-shell; 250 defaultText = literalExpression "pkgs.gitlab-shell"; 251 description = "Reference to the gitlab-shell package"; 252 }; 253 254 packages.gitlab-workhorse = mkOption { 255 type = types.package; 256 default = pkgs.gitlab-workhorse; 257 defaultText = literalExpression "pkgs.gitlab-workhorse"; 258 description = "Reference to the gitlab-workhorse package"; 259 }; 260 261 packages.gitaly = mkOption { 262 type = types.package; 263 default = pkgs.gitaly; 264 defaultText = literalExpression "pkgs.gitaly"; 265 description = "Reference to the gitaly package"; 266 }; 267 268 packages.pages = mkOption { 269 type = types.package; 270 default = pkgs.gitlab-pages; 271 defaultText = literalExpression "pkgs.gitlab-pages"; 272 description = "Reference to the gitlab-pages package"; 273 }; 274 275 statePath = mkOption { 276 type = types.str; 277 default = "/var/gitlab/state"; 278 description = '' 279 GitLab state directory. Configuration, repositories and 280 logs, among other things, are stored here. 281 282 The directory will be created automatically if it doesn't 283 exist already. Its parent directories must be owned by 284 either <literal>root</literal> or the user set in 285 <option>services.gitlab.user</option>. 286 ''; 287 }; 288 289 extraEnv = mkOption { 290 type = types.attrsOf types.str; 291 default = {}; 292 description = '' 293 Additional environment variables for the GitLab environment. 294 ''; 295 }; 296 297 backup.startAt = mkOption { 298 type = with types; either str (listOf str); 299 default = []; 300 example = "03:00"; 301 description = '' 302 The time(s) to run automatic backup of GitLab 303 state. Specified in systemd's time format; see 304 <citerefentry><refentrytitle>systemd.time</refentrytitle> 305 <manvolnum>7</manvolnum></citerefentry>. 306 ''; 307 }; 308 309 backup.path = mkOption { 310 type = types.str; 311 default = cfg.statePath + "/backup"; 312 description = "GitLab path for backups."; 313 }; 314 315 backup.keepTime = mkOption { 316 type = types.int; 317 default = 0; 318 example = 48; 319 apply = x: x * 60 * 60; 320 description = '' 321 How long to keep the backups around, in 322 hours. <literal>0</literal> means <quote>keep 323 forever</quote>. 324 ''; 325 }; 326 327 backup.skip = mkOption { 328 type = with types; 329 let value = enum [ 330 "db" 331 "uploads" 332 "builds" 333 "artifacts" 334 "lfs" 335 "registry" 336 "pages" 337 "repositories" 338 "tar" 339 ]; 340 in 341 either value (listOf value); 342 default = []; 343 example = [ "artifacts" "lfs" ]; 344 apply = x: if isString x then x else concatStringsSep "," x; 345 description = '' 346 Directories to exclude from the backup. The example excludes 347 CI artifacts and LFS objects from the backups. The 348 <literal>tar</literal> option skips the creation of a tar 349 file. 350 351 Refer to <link xlink:href="https://docs.gitlab.com/ee/raketasks/backup_restore.html#excluding-specific-directories-from-the-backup"/> 352 for more information. 353 ''; 354 }; 355 356 backup.uploadOptions = mkOption { 357 type = types.attrs; 358 default = {}; 359 example = literalExpression '' 360 { 361 # Fog storage connection settings, see http://fog.io/storage/ 362 connection = { 363 provider = "AWS"; 364 region = "eu-north-1"; 365 aws_access_key_id = "AKIAXXXXXXXXXXXXXXXX"; 366 aws_secret_access_key = { _secret = config.deployment.keys.aws_access_key.path; }; 367 }; 368 369 # The remote 'directory' to store your backups in. 370 # For S3, this would be the bucket name. 371 remote_directory = "my-gitlab-backups"; 372 373 # Use multipart uploads when file size reaches 100MB, see 374 # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html 375 multipart_chunk_size = 104857600; 376 377 # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional 378 encryption = "AES256"; 379 380 # Specifies Amazon S3 storage class to use for backups, this is optional 381 storage_class = "STANDARD"; 382 }; 383 ''; 384 description = '' 385 GitLab automatic upload specification. Tells GitLab to 386 upload the backup to a remote location when done. 387 388 Attributes specified here are added under 389 <literal>production -> backup -> upload</literal> in 390 <filename>config/gitlab.yml</filename>. 391 ''; 392 }; 393 394 databaseHost = mkOption { 395 type = types.str; 396 default = ""; 397 description = '' 398 GitLab database hostname. An empty string means <quote>use 399 local unix socket connection</quote>. 400 ''; 401 }; 402 403 databasePasswordFile = mkOption { 404 type = with types; nullOr path; 405 default = null; 406 description = '' 407 File containing the GitLab database user password. 408 409 This should be a string, not a nix path, since nix paths are 410 copied into the world-readable nix store. 411 ''; 412 }; 413 414 databaseCreateLocally = mkOption { 415 type = types.bool; 416 default = true; 417 description = '' 418 Whether a database should be automatically created on the 419 local host. Set this to <literal>false</literal> if you plan 420 on provisioning a local database yourself. This has no effect 421 if <option>services.gitlab.databaseHost</option> is customized. 422 ''; 423 }; 424 425 databaseName = mkOption { 426 type = types.str; 427 default = "gitlab"; 428 description = "GitLab database name."; 429 }; 430 431 databaseUsername = mkOption { 432 type = types.str; 433 default = "gitlab"; 434 description = "GitLab database user."; 435 }; 436 437 databasePool = mkOption { 438 type = types.int; 439 default = 5; 440 description = "Database connection pool size."; 441 }; 442 443 extraDatabaseConfig = mkOption { 444 type = types.attrs; 445 default = {}; 446 description = "Extra configuration in config/database.yml."; 447 }; 448 449 redisUrl = mkOption { 450 type = types.str; 451 default = "redis://localhost:6379/"; 452 description = "Redis URL for all GitLab services except gitlab-shell"; 453 }; 454 455 extraGitlabRb = mkOption { 456 type = types.str; 457 default = ""; 458 example = '' 459 if Rails.env.production? 460 Rails.application.config.action_mailer.delivery_method = :sendmail 461 ActionMailer::Base.delivery_method = :sendmail 462 ActionMailer::Base.sendmail_settings = { 463 location: "/run/wrappers/bin/sendmail", 464 arguments: "-i -t" 465 } 466 end 467 ''; 468 description = '' 469 Extra configuration to be placed in config/extra-gitlab.rb. This can 470 be used to add configuration not otherwise exposed through this module's 471 options. 472 ''; 473 }; 474 475 host = mkOption { 476 type = types.str; 477 default = config.networking.hostName; 478 description = "GitLab host name. Used e.g. for copy-paste URLs."; 479 }; 480 481 port = mkOption { 482 type = types.port; 483 default = 8080; 484 description = '' 485 GitLab server port for copy-paste URLs, e.g. 80 or 443 if you're 486 service over https. 487 ''; 488 }; 489 490 https = mkOption { 491 type = types.bool; 492 default = false; 493 description = "Whether gitlab prints URLs with https as scheme."; 494 }; 495 496 user = mkOption { 497 type = types.str; 498 default = "gitlab"; 499 description = "User to run gitlab and all related services."; 500 }; 501 502 group = mkOption { 503 type = types.str; 504 default = "gitlab"; 505 description = "Group to run gitlab and all related services."; 506 }; 507 508 initialRootEmail = mkOption { 509 type = types.str; 510 default = "admin@local.host"; 511 description = '' 512 Initial email address of the root account if this is a new install. 513 ''; 514 }; 515 516 initialRootPasswordFile = mkOption { 517 type = with types; nullOr path; 518 default = null; 519 description = '' 520 File containing the initial password of the root account if 521 this is a new install. 522 523 This should be a string, not a nix path, since nix paths are 524 copied into the world-readable nix store. 525 ''; 526 }; 527 528 registry = { 529 enable = mkOption { 530 type = types.bool; 531 default = false; 532 description = "Enable GitLab container registry."; 533 }; 534 host = mkOption { 535 type = types.str; 536 default = config.services.gitlab.host; 537 description = "GitLab container registry host name."; 538 }; 539 port = mkOption { 540 type = types.int; 541 default = 4567; 542 description = "GitLab container registry port."; 543 }; 544 certFile = mkOption { 545 type = types.path; 546 description = "Path to GitLab container registry certificate."; 547 }; 548 keyFile = mkOption { 549 type = types.path; 550 description = "Path to GitLab container registry certificate-key."; 551 }; 552 defaultForProjects = mkOption { 553 type = types.bool; 554 default = cfg.registry.enable; 555 description = "If GitLab container registry should be enabled by default for projects."; 556 }; 557 issuer = mkOption { 558 type = types.str; 559 default = "gitlab-issuer"; 560 description = "GitLab container registry issuer."; 561 }; 562 serviceName = mkOption { 563 type = types.str; 564 default = "container_registry"; 565 description = "GitLab container registry service name."; 566 }; 567 externalAddress = mkOption { 568 type = types.str; 569 default = ""; 570 description = "External address used to access registry from the internet"; 571 }; 572 externalPort = mkOption { 573 type = types.int; 574 description = "External port used to access registry from the internet"; 575 }; 576 }; 577 578 smtp = { 579 enable = mkOption { 580 type = types.bool; 581 default = false; 582 description = "Enable gitlab mail delivery over SMTP."; 583 }; 584 585 address = mkOption { 586 type = types.str; 587 default = "localhost"; 588 description = "Address of the SMTP server for GitLab."; 589 }; 590 591 port = mkOption { 592 type = types.int; 593 default = 25; 594 description = "Port of the SMTP server for GitLab."; 595 }; 596 597 username = mkOption { 598 type = with types; nullOr str; 599 default = null; 600 description = "Username of the SMTP server for GitLab."; 601 }; 602 603 passwordFile = mkOption { 604 type = types.nullOr types.path; 605 default = null; 606 description = '' 607 File containing the password of the SMTP server for GitLab. 608 609 This should be a string, not a nix path, since nix paths 610 are copied into the world-readable nix store. 611 ''; 612 }; 613 614 domain = mkOption { 615 type = types.str; 616 default = "localhost"; 617 description = "HELO domain to use for outgoing mail."; 618 }; 619 620 authentication = mkOption { 621 type = with types; nullOr str; 622 default = null; 623 description = "Authentication type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html"; 624 }; 625 626 enableStartTLSAuto = mkOption { 627 type = types.bool; 628 default = true; 629 description = "Whether to try to use StartTLS."; 630 }; 631 632 tls = mkOption { 633 type = types.bool; 634 default = false; 635 description = "Whether to use TLS wrapper-mode."; 636 }; 637 638 opensslVerifyMode = mkOption { 639 type = types.str; 640 default = "peer"; 641 description = "How OpenSSL checks the certificate, see http://api.rubyonrails.org/classes/ActionMailer/Base.html"; 642 }; 643 }; 644 645 pagesExtraArgs = mkOption { 646 type = types.listOf types.str; 647 default = [ "-listen-proxy" "127.0.0.1:8090" ]; 648 description = "Arguments to pass to the gitlab-pages daemon"; 649 }; 650 651 secrets.secretFile = mkOption { 652 type = with types; nullOr path; 653 default = null; 654 description = '' 655 A file containing the secret used to encrypt variables in 656 the DB. If you change or lose this key you will be unable to 657 access variables stored in database. 658 659 Make sure the secret is at least 32 characters and all random, 660 no regular words or you'll be exposed to dictionary attacks. 661 662 This should be a string, not a nix path, since nix paths are 663 copied into the world-readable nix store. 664 ''; 665 }; 666 667 secrets.dbFile = mkOption { 668 type = with types; nullOr path; 669 default = null; 670 description = '' 671 A file containing the secret used to encrypt variables in 672 the DB. If you change or lose this key you will be unable to 673 access variables stored in database. 674 675 Make sure the secret is at least 32 characters and all random, 676 no regular words or you'll be exposed to dictionary attacks. 677 678 This should be a string, not a nix path, since nix paths are 679 copied into the world-readable nix store. 680 ''; 681 }; 682 683 secrets.otpFile = mkOption { 684 type = with types; nullOr path; 685 default = null; 686 description = '' 687 A file containing the secret used to encrypt secrets for OTP 688 tokens. If you change or lose this key, users which have 2FA 689 enabled for login won't be able to login anymore. 690 691 Make sure the secret is at least 32 characters and all random, 692 no regular words or you'll be exposed to dictionary attacks. 693 694 This should be a string, not a nix path, since nix paths are 695 copied into the world-readable nix store. 696 ''; 697 }; 698 699 secrets.jwsFile = mkOption { 700 type = with types; nullOr path; 701 default = null; 702 description = '' 703 A file containing the secret used to encrypt session 704 keys. If you change or lose this key, users will be 705 disconnected. 706 707 Make sure the secret is an RSA private key in PEM format. You can 708 generate one with 709 710 openssl genrsa 2048 711 712 This should be a string, not a nix path, since nix paths are 713 copied into the world-readable nix store. 714 ''; 715 }; 716 717 extraShellConfig = mkOption { 718 type = types.attrs; 719 default = {}; 720 description = "Extra configuration to merge into shell-config.yml"; 721 }; 722 723 puma.workers = mkOption { 724 type = types.int; 725 default = 2; 726 apply = x: builtins.toString x; 727 description = '' 728 The number of worker processes Puma should spawn. This 729 controls the amount of parallel Ruby code can be 730 executed. GitLab recommends <quote>Number of CPU cores - 731 1</quote>, but at least two. 732 733 <note> 734 <para> 735 Each worker consumes quite a bit of memory, so 736 be careful when increasing this. 737 </para> 738 </note> 739 ''; 740 }; 741 742 puma.threadsMin = mkOption { 743 type = types.int; 744 default = 0; 745 apply = x: builtins.toString x; 746 description = '' 747 The minimum number of threads Puma should use per 748 worker. 749 750 <note> 751 <para> 752 Each thread consumes memory and contributes to Global VM 753 Lock contention, so be careful when increasing this. 754 </para> 755 </note> 756 ''; 757 }; 758 759 puma.threadsMax = mkOption { 760 type = types.int; 761 default = 4; 762 apply = x: builtins.toString x; 763 description = '' 764 The maximum number of threads Puma should use per 765 worker. This limits how many threads Puma will automatically 766 spawn in response to requests. In contrast to workers, 767 threads will never be able to run Ruby code in parallel, but 768 give higher IO parallelism. 769 770 <note> 771 <para> 772 Each thread consumes memory and contributes to Global VM 773 Lock contention, so be careful when increasing this. 774 </para> 775 </note> 776 ''; 777 }; 778 779 sidekiq.memoryKiller.enable = mkOption { 780 type = types.bool; 781 default = true; 782 description = '' 783 Whether the Sidekiq MemoryKiller should be turned 784 on. MemoryKiller kills Sidekiq when its memory consumption 785 exceeds a certain limit. 786 787 See <link xlink:href="https://docs.gitlab.com/ee/administration/operations/sidekiq_memory_killer.html"/> 788 for details. 789 ''; 790 }; 791 792 sidekiq.memoryKiller.maxMemory = mkOption { 793 type = types.int; 794 default = 2000; 795 apply = x: builtins.toString (x * 1024); 796 description = '' 797 The maximum amount of memory, in MiB, a Sidekiq worker is 798 allowed to consume before being killed. 799 ''; 800 }; 801 802 sidekiq.memoryKiller.graceTime = mkOption { 803 type = types.int; 804 default = 900; 805 apply = x: builtins.toString x; 806 description = '' 807 The time MemoryKiller waits after noticing excessive memory 808 consumption before killing Sidekiq. 809 ''; 810 }; 811 812 sidekiq.memoryKiller.shutdownWait = mkOption { 813 type = types.int; 814 default = 30; 815 apply = x: builtins.toString x; 816 description = '' 817 The time allowed for all jobs to finish before Sidekiq is 818 killed forcefully. 819 ''; 820 }; 821 822 logrotate = { 823 enable = mkOption { 824 type = types.bool; 825 default = true; 826 description = '' 827 Enable rotation of log files. 828 ''; 829 }; 830 831 frequency = mkOption { 832 type = types.str; 833 default = "daily"; 834 description = "How often to rotate the logs."; 835 }; 836 837 keep = mkOption { 838 type = types.int; 839 default = 30; 840 description = "How many rotations to keep."; 841 }; 842 843 extraConfig = mkOption { 844 type = types.lines; 845 default = '' 846 copytruncate 847 compress 848 ''; 849 description = '' 850 Extra logrotate config options for this path. Refer to 851 <link xlink:href="https://linux.die.net/man/8/logrotate"/> for details. 852 ''; 853 }; 854 }; 855 856 extraConfig = mkOption { 857 type = types.attrs; 858 default = {}; 859 example = literalExpression '' 860 { 861 gitlab = { 862 default_projects_features = { 863 builds = false; 864 }; 865 }; 866 omniauth = { 867 enabled = true; 868 auto_sign_in_with_provider = "openid_connect"; 869 allow_single_sign_on = ["openid_connect"]; 870 block_auto_created_users = false; 871 providers = [ 872 { 873 name = "openid_connect"; 874 label = "OpenID Connect"; 875 args = { 876 name = "openid_connect"; 877 scope = ["openid" "profile"]; 878 response_type = "code"; 879 issuer = "https://keycloak.example.com/auth/realms/My%20Realm"; 880 discovery = true; 881 client_auth_method = "query"; 882 uid_field = "preferred_username"; 883 client_options = { 884 identifier = "gitlab"; 885 secret = { _secret = "/var/keys/gitlab_oidc_secret"; }; 886 redirect_uri = "https://git.example.com/users/auth/openid_connect/callback"; 887 }; 888 }; 889 } 890 ]; 891 }; 892 }; 893 ''; 894 description = '' 895 Extra options to be added under 896 <literal>production</literal> in 897 <filename>config/gitlab.yml</filename>, as a nix attribute 898 set. 899 900 Options containing secret data should be set to an attribute 901 set containing the attribute <literal>_secret</literal> - a 902 string pointing to a file containing the value the option 903 should be set to. See the example to get a better picture of 904 this: in the resulting 905 <filename>config/gitlab.yml</filename> file, the 906 <literal>production.omniauth.providers[0].args.client_options.secret</literal> 907 key will be set to the contents of the 908 <filename>/var/keys/gitlab_oidc_secret</filename> file. 909 ''; 910 }; 911 }; 912 }; 913 914 config = mkIf cfg.enable { 915 916 assertions = [ 917 { 918 assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.databaseUsername); 919 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.''; 920 } 921 { 922 assertion = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null); 923 message = "When services.gitlab.databaseHost is customized, services.gitlab.databasePasswordFile must be set!"; 924 } 925 { 926 assertion = cfg.initialRootPasswordFile != null; 927 message = "services.gitlab.initialRootPasswordFile must be set!"; 928 } 929 { 930 assertion = cfg.secrets.secretFile != null; 931 message = "services.gitlab.secrets.secretFile must be set!"; 932 } 933 { 934 assertion = cfg.secrets.dbFile != null; 935 message = "services.gitlab.secrets.dbFile must be set!"; 936 } 937 { 938 assertion = cfg.secrets.otpFile != null; 939 message = "services.gitlab.secrets.otpFile must be set!"; 940 } 941 { 942 assertion = cfg.secrets.jwsFile != null; 943 message = "services.gitlab.secrets.jwsFile must be set!"; 944 } 945 { 946 assertion = versionAtLeast postgresqlPackage.version "12.0.0"; 947 message = "PostgreSQL >=12 is required to run GitLab 14. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading"; 948 } 949 ]; 950 951 environment.systemPackages = [ pkgs.git gitlab-rake gitlab-rails cfg.packages.gitlab-shell ]; 952 953 systemd.targets.gitlab = { 954 description = "Common target for all GitLab services."; 955 wantedBy = [ "multi-user.target" ]; 956 }; 957 958 # Redis is required for the sidekiq queue runner. 959 services.redis.enable = mkDefault true; 960 961 # We use postgres as the main data store. 962 services.postgresql = optionalAttrs databaseActuallyCreateLocally { 963 enable = true; 964 ensureUsers = singleton { name = cfg.databaseUsername; }; 965 }; 966 967 # Enable rotation of log files 968 services.logrotate = { 969 enable = cfg.logrotate.enable; 970 paths = { 971 gitlab = { 972 path = "${cfg.statePath}/log/*.log"; 973 user = cfg.user; 974 group = cfg.group; 975 frequency = cfg.logrotate.frequency; 976 keep = cfg.logrotate.keep; 977 extraConfig = cfg.logrotate.extraConfig; 978 }; 979 }; 980 }; 981 982 # The postgresql module doesn't currently support concepts like 983 # objects owners and extensions; for now we tack on what's needed 984 # here. 985 systemd.services.gitlab-postgresql = let pgsql = config.services.postgresql; in mkIf databaseActuallyCreateLocally { 986 after = [ "postgresql.service" ]; 987 bindsTo = [ "postgresql.service" ]; 988 wantedBy = [ "gitlab.target" ]; 989 partOf = [ "gitlab.target" ]; 990 path = [ 991 pgsql.package 992 pkgs.util-linux 993 ]; 994 script = '' 995 set -eu 996 997 PSQL() { 998 psql --port=${toString pgsql.port} "$@" 999 } 1000 1001 PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"' 1002 current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'") 1003 if [[ "$current_owner" != "${cfg.databaseUsername}" ]]; then 1004 PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"' 1005 if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" ]]; then 1006 echo "Reassigning ownership of database ${cfg.databaseName} to user ${cfg.databaseUsername} failed on last boot. Failing..." 1007 exit 1 1008 fi 1009 touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" 1010 PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\"" 1011 rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" 1012 fi 1013 PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm" 1014 PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS btree_gist;" 1015 ''; 1016 1017 serviceConfig = { 1018 User = pgsql.superUser; 1019 Type = "oneshot"; 1020 RemainAfterExit = true; 1021 }; 1022 }; 1023 1024 systemd.services.gitlab-registry-cert = optionalAttrs cfg.registry.enable { 1025 path = with pkgs; [ openssl ]; 1026 1027 script = '' 1028 mkdir -p $(dirname ${cfg.registry.keyFile}) 1029 mkdir -p $(dirname ${cfg.registry.certFile}) 1030 openssl req -nodes -newkey rsa:4096 -keyout ${cfg.registry.keyFile} -out /tmp/registry-auth.csr -subj "/CN=${cfg.registry.issuer}" 1031 openssl x509 -in /tmp/registry-auth.csr -out ${cfg.registry.certFile} -req -signkey ${cfg.registry.keyFile} -days 3650 1032 chown ${cfg.user}:${cfg.group} $(dirname ${cfg.registry.keyFile}) 1033 chown ${cfg.user}:${cfg.group} $(dirname ${cfg.registry.certFile}) 1034 chown ${cfg.user}:${cfg.group} ${cfg.registry.keyFile} 1035 chown ${cfg.user}:${cfg.group} ${cfg.registry.certFile} 1036 ''; 1037 1038 serviceConfig = { 1039 ConditionPathExists = "!${cfg.registry.certFile}"; 1040 }; 1041 }; 1042 1043 # Ensure Docker Registry launches after the certificate generation job 1044 systemd.services.docker-registry = optionalAttrs cfg.registry.enable { 1045 wants = [ "gitlab-registry-cert.service" ]; 1046 }; 1047 1048 # Enable Docker Registry, if GitLab-Container Registry is enabled 1049 services.dockerRegistry = optionalAttrs cfg.registry.enable { 1050 enable = true; 1051 enableDelete = true; # This must be true, otherwise GitLab won't manage it correctly 1052 extraConfig = { 1053 auth.token = { 1054 realm = "http${if cfg.https == true then "s" else ""}://${cfg.host}/jwt/auth"; 1055 service = cfg.registry.serviceName; 1056 issuer = cfg.registry.issuer; 1057 rootcertbundle = cfg.registry.certFile; 1058 }; 1059 }; 1060 }; 1061 1062 # Use postfix to send out mails. 1063 services.postfix.enable = mkDefault (cfg.smtp.enable && cfg.smtp.address == "localhost"); 1064 1065 users.users.${cfg.user} = 1066 { group = cfg.group; 1067 home = "${cfg.statePath}/home"; 1068 shell = "${pkgs.bash}/bin/bash"; 1069 uid = config.ids.uids.gitlab; 1070 }; 1071 1072 users.groups.${cfg.group}.gid = config.ids.gids.gitlab; 1073 1074 systemd.tmpfiles.rules = [ 1075 "d /run/gitlab 0755 ${cfg.user} ${cfg.group} -" 1076 "d ${gitlabEnv.HOME} 0750 ${cfg.user} ${cfg.group} -" 1077 "z ${gitlabEnv.HOME}/.ssh/authorized_keys 0600 ${cfg.user} ${cfg.group} -" 1078 "d ${cfg.backup.path} 0750 ${cfg.user} ${cfg.group} -" 1079 "d ${cfg.statePath} 0750 ${cfg.user} ${cfg.group} -" 1080 "d ${cfg.statePath}/builds 0750 ${cfg.user} ${cfg.group} -" 1081 "d ${cfg.statePath}/config 0750 ${cfg.user} ${cfg.group} -" 1082 "d ${cfg.statePath}/db 0750 ${cfg.user} ${cfg.group} -" 1083 "d ${cfg.statePath}/log 0750 ${cfg.user} ${cfg.group} -" 1084 "d ${cfg.statePath}/repositories 2770 ${cfg.user} ${cfg.group} -" 1085 "d ${cfg.statePath}/shell 0750 ${cfg.user} ${cfg.group} -" 1086 "d ${cfg.statePath}/tmp 0750 ${cfg.user} ${cfg.group} -" 1087 "d ${cfg.statePath}/tmp/pids 0750 ${cfg.user} ${cfg.group} -" 1088 "d ${cfg.statePath}/tmp/sockets 0750 ${cfg.user} ${cfg.group} -" 1089 "d ${cfg.statePath}/uploads 0700 ${cfg.user} ${cfg.group} -" 1090 "d ${cfg.statePath}/custom_hooks 0700 ${cfg.user} ${cfg.group} -" 1091 "d ${cfg.statePath}/custom_hooks/pre-receive.d 0700 ${cfg.user} ${cfg.group} -" 1092 "d ${cfg.statePath}/custom_hooks/post-receive.d 0700 ${cfg.user} ${cfg.group} -" 1093 "d ${cfg.statePath}/custom_hooks/update.d 0700 ${cfg.user} ${cfg.group} -" 1094 "d ${gitlabConfig.production.shared.path} 0750 ${cfg.user} ${cfg.group} -" 1095 "d ${gitlabConfig.production.shared.path}/artifacts 0750 ${cfg.user} ${cfg.group} -" 1096 "d ${gitlabConfig.production.shared.path}/lfs-objects 0750 ${cfg.user} ${cfg.group} -" 1097 "d ${gitlabConfig.production.shared.path}/pages 0750 ${cfg.user} ${cfg.group} -" 1098 "L+ /run/gitlab/config - - - - ${cfg.statePath}/config" 1099 "L+ /run/gitlab/log - - - - ${cfg.statePath}/log" 1100 "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp" 1101 "L+ /run/gitlab/uploads - - - - ${cfg.statePath}/uploads" 1102 1103 "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" 1104 ]; 1105 1106 1107 systemd.services.gitlab-config = { 1108 wantedBy = [ "gitlab.target" ]; 1109 partOf = [ "gitlab.target" ]; 1110 path = with pkgs; [ 1111 jq 1112 openssl 1113 replace-secret 1114 git 1115 ]; 1116 serviceConfig = { 1117 Type = "oneshot"; 1118 User = cfg.user; 1119 Group = cfg.group; 1120 TimeoutSec = "infinity"; 1121 Restart = "on-failure"; 1122 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; 1123 RemainAfterExit = true; 1124 1125 ExecStartPre = let 1126 preStartFullPrivileges = '' 1127 shopt -s dotglob nullglob 1128 set -eu 1129 1130 chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/* 1131 if [[ -n "$(ls -A '${cfg.statePath}'/config/)" ]]; then 1132 chown --no-dereference '${cfg.user}':'${cfg.group}' '${cfg.statePath}'/config/* 1133 fi 1134 ''; 1135 in "+${pkgs.writeShellScript "gitlab-pre-start-full-privileges" preStartFullPrivileges}"; 1136 1137 ExecStart = pkgs.writeShellScript "gitlab-config" '' 1138 set -eu 1139 1140 umask u=rwx,g=rx,o= 1141 1142 cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION 1143 rm -rf ${cfg.statePath}/db/* 1144 rm -f ${cfg.statePath}/lib 1145 find '${cfg.statePath}/config/' -maxdepth 1 -mindepth 1 -type d -execdir rm -rf {} \; 1146 cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config 1147 cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db 1148 ln -sf ${extraGitlabRb} ${cfg.statePath}/config/initializers/extra-gitlab.rb 1149 1150 ${cfg.packages.gitlab-shell}/bin/install 1151 1152 ${optionalString cfg.smtp.enable '' 1153 install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb 1154 ${optionalString (cfg.smtp.passwordFile != null) '' 1155 replace-secret '@smtpPassword@' '${cfg.smtp.passwordFile}' '${cfg.statePath}/config/initializers/smtp_settings.rb' 1156 ''} 1157 ''} 1158 1159 ( 1160 umask u=rwx,g=,o= 1161 1162 openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret 1163 1164 rm -f '${cfg.statePath}/config/database.yml' 1165 1166 ${if cfg.databasePasswordFile != null then '' 1167 export db_password="$(<'${cfg.databasePasswordFile}')" 1168 1169 if [[ -z "$db_password" ]]; then 1170 >&2 echo "Database password was an empty string!" 1171 exit 1 1172 fi 1173 1174 jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ 1175 '.production.password = $ENV.db_password' \ 1176 >'${cfg.statePath}/config/database.yml' 1177 '' 1178 else '' 1179 jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ 1180 >'${cfg.statePath}/config/database.yml' 1181 '' 1182 } 1183 1184 ${utils.genJqSecretsReplacementSnippet 1185 gitlabConfig 1186 "${cfg.statePath}/config/gitlab.yml" 1187 } 1188 1189 rm -f '${cfg.statePath}/config/secrets.yml' 1190 1191 export secret="$(<'${cfg.secrets.secretFile}')" 1192 export db="$(<'${cfg.secrets.dbFile}')" 1193 export otp="$(<'${cfg.secrets.otpFile}')" 1194 export jws="$(<'${cfg.secrets.jwsFile}')" 1195 jq -n '{production: {secret_key_base: $ENV.secret, 1196 otp_key_base: $ENV.otp, 1197 db_key_base: $ENV.db, 1198 openid_connect_signing_key: $ENV.jws}}' \ 1199 > '${cfg.statePath}/config/secrets.yml' 1200 ) 1201 1202 # We remove potentially broken links to old gitlab-shell versions 1203 rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks 1204 1205 git config --global core.autocrlf "input" 1206 ''; 1207 }; 1208 }; 1209 1210 systemd.services.gitlab-db-config = { 1211 after = [ "gitlab-config.service" "gitlab-postgresql.service" "postgresql.service" ]; 1212 bindsTo = [ 1213 "gitlab-config.service" 1214 ] ++ optional (cfg.databaseHost == "") "postgresql.service" 1215 ++ optional databaseActuallyCreateLocally "gitlab-postgresql.service"; 1216 wantedBy = [ "gitlab.target" ]; 1217 partOf = [ "gitlab.target" ]; 1218 serviceConfig = { 1219 Type = "oneshot"; 1220 User = cfg.user; 1221 Group = cfg.group; 1222 TimeoutSec = "infinity"; 1223 Restart = "on-failure"; 1224 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; 1225 RemainAfterExit = true; 1226 1227 ExecStart = pkgs.writeShellScript "gitlab-db-config" '' 1228 set -eu 1229 umask u=rwx,g=rx,o= 1230 1231 initial_root_password="$(<'${cfg.initialRootPasswordFile}')" 1232 ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \ 1233 GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' > /dev/null 1234 ''; 1235 }; 1236 }; 1237 1238 systemd.services.gitlab-sidekiq = { 1239 after = [ 1240 "network.target" 1241 "redis.service" 1242 "postgresql.service" 1243 "gitlab-config.service" 1244 "gitlab-db-config.service" 1245 ]; 1246 bindsTo = [ 1247 "redis.service" 1248 "gitlab-config.service" 1249 "gitlab-db-config.service" 1250 ] ++ optional (cfg.databaseHost == "") "postgresql.service"; 1251 wantedBy = [ "gitlab.target" ]; 1252 partOf = [ "gitlab.target" ]; 1253 environment = gitlabEnv // (optionalAttrs cfg.sidekiq.memoryKiller.enable { 1254 SIDEKIQ_MEMORY_KILLER_MAX_RSS = cfg.sidekiq.memoryKiller.maxMemory; 1255 SIDEKIQ_MEMORY_KILLER_GRACE_TIME = cfg.sidekiq.memoryKiller.graceTime; 1256 SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT = cfg.sidekiq.memoryKiller.shutdownWait; 1257 }); 1258 path = with pkgs; [ 1259 postgresqlPackage 1260 git 1261 ruby 1262 openssh 1263 nodejs 1264 gnupg 1265 1266 # Needed for GitLab project imports 1267 gnutar 1268 gzip 1269 1270 procps # Sidekiq MemoryKiller 1271 ]; 1272 serviceConfig = { 1273 Type = "simple"; 1274 User = cfg.user; 1275 Group = cfg.group; 1276 TimeoutSec = "infinity"; 1277 Restart = "always"; 1278 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; 1279 ExecStart="${cfg.packages.gitlab.rubyEnv}/bin/sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production"; 1280 }; 1281 }; 1282 1283 systemd.services.gitaly = { 1284 after = [ "network.target" "gitlab-config.service" ]; 1285 bindsTo = [ "gitlab-config.service" ]; 1286 wantedBy = [ "gitlab.target" ]; 1287 partOf = [ "gitlab.target" ]; 1288 path = with pkgs; [ 1289 openssh 1290 procps # See https://gitlab.com/gitlab-org/gitaly/issues/1562 1291 git 1292 cfg.packages.gitaly.rubyEnv 1293 cfg.packages.gitaly.rubyEnv.wrappedRuby 1294 gzip 1295 bzip2 1296 ]; 1297 serviceConfig = { 1298 Type = "simple"; 1299 User = cfg.user; 1300 Group = cfg.group; 1301 TimeoutSec = "infinity"; 1302 Restart = "on-failure"; 1303 WorkingDirectory = gitlabEnv.HOME; 1304 ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}"; 1305 }; 1306 }; 1307 1308 systemd.services.gitlab-pages = mkIf (gitlabConfig.production.pages.enabled or false) { 1309 description = "GitLab static pages daemon"; 1310 after = [ "network.target" "gitlab-config.service" ]; 1311 bindsTo = [ "gitlab-config.service" ]; 1312 wantedBy = [ "gitlab.target" ]; 1313 partOf = [ "gitlab.target" ]; 1314 1315 path = [ pkgs.unzip ]; 1316 1317 serviceConfig = { 1318 Type = "simple"; 1319 TimeoutSec = "infinity"; 1320 Restart = "on-failure"; 1321 1322 User = cfg.user; 1323 Group = cfg.group; 1324 1325 ExecStart = "${cfg.packages.pages}/bin/gitlab-pages ${escapeShellArgs pagesArgs}"; 1326 WorkingDirectory = gitlabEnv.HOME; 1327 }; 1328 }; 1329 1330 systemd.services.gitlab-workhorse = { 1331 after = [ "network.target" ]; 1332 wantedBy = [ "gitlab.target" ]; 1333 partOf = [ "gitlab.target" ]; 1334 path = with pkgs; [ 1335 exiftool 1336 git 1337 gnutar 1338 gzip 1339 openssh 1340 gitlab-workhorse 1341 ]; 1342 serviceConfig = { 1343 Type = "simple"; 1344 User = cfg.user; 1345 Group = cfg.group; 1346 TimeoutSec = "infinity"; 1347 Restart = "on-failure"; 1348 WorkingDirectory = gitlabEnv.HOME; 1349 ExecStart = 1350 "${cfg.packages.gitlab-workhorse}/bin/workhorse " 1351 + "-listenUmask 0 " 1352 + "-listenNetwork unix " 1353 + "-listenAddr /run/gitlab/gitlab-workhorse.socket " 1354 + "-authSocket ${gitlabSocket} " 1355 + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public " 1356 + "-secretPath ${cfg.statePath}/.gitlab_workhorse_secret"; 1357 }; 1358 }; 1359 1360 systemd.services.gitlab-mailroom = mkIf (gitlabConfig.production.incoming_email.enabled or false) { 1361 description = "GitLab incoming mail daemon"; 1362 after = [ "network.target" "redis.service" "gitlab-config.service" ]; 1363 bindsTo = [ "gitlab-config.service" ]; 1364 wantedBy = [ "gitlab.target" ]; 1365 partOf = [ "gitlab.target" ]; 1366 environment = gitlabEnv; 1367 serviceConfig = { 1368 Type = "simple"; 1369 TimeoutSec = "infinity"; 1370 Restart = "on-failure"; 1371 1372 User = cfg.user; 1373 Group = cfg.group; 1374 ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec mail_room -c ${cfg.statePath}/config/mail_room.yml"; 1375 WorkingDirectory = gitlabEnv.HOME; 1376 }; 1377 }; 1378 1379 systemd.services.gitlab = { 1380 after = [ 1381 "gitlab-workhorse.service" 1382 "network.target" 1383 "redis.service" 1384 "gitlab-config.service" 1385 "gitlab-db-config.service" 1386 ]; 1387 bindsTo = [ 1388 "redis.service" 1389 "gitlab-config.service" 1390 "gitlab-db-config.service" 1391 ] ++ optional (cfg.databaseHost == "") "postgresql.service"; 1392 wantedBy = [ "gitlab.target" ]; 1393 partOf = [ "gitlab.target" ]; 1394 environment = gitlabEnv; 1395 path = with pkgs; [ 1396 postgresqlPackage 1397 git 1398 openssh 1399 nodejs 1400 procps 1401 gnupg 1402 ]; 1403 serviceConfig = { 1404 Type = "notify"; 1405 User = cfg.user; 1406 Group = cfg.group; 1407 TimeoutSec = "infinity"; 1408 Restart = "on-failure"; 1409 WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; 1410 ExecStart = concatStringsSep " " [ 1411 "${cfg.packages.gitlab.rubyEnv}/bin/puma" 1412 "-e production" 1413 "-C ${cfg.statePath}/config/puma.rb" 1414 "-w ${cfg.puma.workers}" 1415 "-t ${cfg.puma.threadsMin}:${cfg.puma.threadsMax}" 1416 ]; 1417 }; 1418 1419 }; 1420 1421 systemd.services.gitlab-backup = { 1422 after = [ "gitlab.service" ]; 1423 bindsTo = [ "gitlab.service" ]; 1424 startAt = cfg.backup.startAt; 1425 environment = { 1426 RAILS_ENV = "production"; 1427 CRON = "1"; 1428 } // optionalAttrs (stringLength cfg.backup.skip > 0) { 1429 SKIP = cfg.backup.skip; 1430 }; 1431 serviceConfig = { 1432 User = cfg.user; 1433 Group = cfg.group; 1434 ExecStart = "${gitlab-rake}/bin/gitlab-rake gitlab:backup:create"; 1435 }; 1436 }; 1437 1438 }; 1439 1440 meta.doc = ./gitlab.xml; 1441 1442}