# A low-resource, full-featured git forge. { config, lib, ... }: let cfg = config.my.services.forgejo; in { options.my.services.forgejo = with lib; { enable = mkEnableOption "Forgejo"; port = mkOption { type = types.port; default = 3042; example = 8080; description = "Internal port"; }; mail = { enable = mkEnableOption { description = "mailer configuration"; }; host = mkOption { type = types.str; example = "smtp.example.com"; description = "Host for the mail account"; }; port = mkOption { type = types.port; default = 465; example = 587; description = "Port for the mail account"; }; user = mkOption { type = types.str; example = "forgejo@example.com"; description = "User for the mail account"; }; passwordFile = mkOption { type = types.str; example = "/run/secrets/forgejo-mail-password.txt"; description = "Password for the mail account"; }; protocol = mkOption { type = types.str; default = "smtps"; example = "smtp"; description = "Protocol for connection"; }; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = cfg.enable -> !config.my.services.gitea.enable; message = '' `config.my.services.forgejo` is incompatible with `config.my.services.gitea`. ''; } ]; services.forgejo = let inherit (config.networking) domain; forgejoDomain = "git.${domain}"; in { enable = true; user = "git"; group = "git"; lfs.enable = true; useWizard = false; database = { type = "postgres"; # Automatic setup user = "git"; # User needs to be the same as forgejo user name = "git"; # Name must be the same as user for `ensureDBOwnership` }; # NixOS module uses `forgejo dump` to backup repositories and the database, # but it produces a single .zip file that's not very backup friendly. # I configure my backup system manually below. dump.enable = false; secrets = { mailer = lib.mkIf cfg.mail.enable { PASSWD = cfg.mail.passwordFile; }; }; settings = { DEFAULT = { APP_NAME = "Ambroisie's forge"; }; server = { HTTP_PORT = cfg.port; DOMAIN = forgejoDomain; ROOT_URL = "https://${forgejoDomain}"; }; mailer = lib.mkIf cfg.mail.enable { ENABLED = true; SMTP_ADDR = cfg.mail.host; SMTP_PORT = cfg.mail.port; FROM = "Forgejo <${cfg.mail.user}>"; USER = cfg.mail.user; PROTOCOL = cfg.mail.protocol; }; service = { DISABLE_REGISTRATION = true; }; session = { # only send cookies via HTTPS COOKIE_SECURE = true; }; }; }; users.users.git = { description = "Forgejo Service"; home = config.services.forgejo.stateDir; useDefaultShell = true; group = "git"; isSystemUser = true; }; users.groups.git = { }; my.services.nginx.virtualHosts = { # Proxy to Forgejo git = { inherit (cfg) port; }; # Redirect `forgejo.` to actual forge subdomain forgejo = { redirect = config.services.forgejo.settings.server.ROOT_URL; }; }; my.services.backup = { paths = [ config.services.forgejo.lfs.contentDir config.services.forgejo.repositoryRoot ]; }; my.system.persist.directories = [ config.services.forgejo.lfs.contentDir config.services.forgejo.repositoryRoot ]; services.fail2ban.jails = { forgejo = '' enabled = true filter = forgejo action = iptables-allports ''; }; environment.etc = { "fail2ban/filter.d/forgejo.conf".text = '' [Definition] failregex = ^.*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from $ journalmatch = _SYSTEMD_UNIT=forgejo.service ''; }; }; }