nixos/servers/forgejo.nix
Julian Sutter 29eb6493be forgejo?
2026-02-15 16:57:07 -08:00

314 lines
7.8 KiB
Nix

{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.forgejo;
in {
options.services.forgejo = {
enable = mkEnableOption "Forgejo Git server";
domain = mkOption {
type = types.str;
example = "git.example.com";
description = "The domain name for Forgejo";
};
stateDir = mkOption {
type = types.str;
default = "/var/lib/forgejo";
description = "Directory for Forgejo data";
};
database = {
createLocally = mkOption {
type = types.bool;
default = true;
description = "Whether to create a local PostgreSQL database";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/forgejo-db";
description = "Path to file containing database password";
};
};
mailer = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable email notifications";
};
host = mkOption {
type = types.str;
example = "smtp.gmail.com";
description = "SMTP server hostname";
};
port = mkOption {
type = types.port;
default = 587;
description = "SMTP server port";
};
from = mkOption {
type = types.str;
example = "git@example.com";
description = "Email sender address";
};
userFile = mkOption {
type = types.path;
example = "/run/keys/forgejo-smtp-user";
description = "Path to file containing SMTP username";
};
passwordFile = mkOption {
type = types.path;
example = "/run/keys/forgejo-smtp-pass";
description = "Path to file containing SMTP password";
};
};
oauth2 = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable OAuth2 authentication";
};
github = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable GitHub OAuth2 provider";
};
clientId = mkOption {
type = types.str;
description = "GitHub OAuth client ID";
};
clientSecret = mkOption {
type = types.path;
description = "Path to GitHub OAuth client secret";
};
};
};
lfs = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable Git LFS support";
};
storage = mkOption {
type = types.str;
default = "/var/lib/forgejo/lfs";
description = "Path to LFS storage";
};
};
backup = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable automated backups";
};
interval = mkOption {
type = types.str;
default = "daily";
description = "Backup interval (systemd timer format)";
};
retentionDays = mkOption {
type = types.int;
default = 7;
description = "Number of backups to keep";
};
backupDir = mkOption {
type = types.str;
default = "/var/backups/forgejo";
description = "Directory to store backups";
};
};
};
config = mkIf cfg.enable {
# PostgreSQL database
services.postgresql = mkIf cfg.database.createLocally {
enable = true;
enableTCPIP = false;
ensureDatabases = [ "forgejo" ];
ensureUsers = [
{
name = "forgejo";
ensureDBOwnership = true;
}
];
};
# Forgejo service
services.forgejo = {
enable = true;
settings = {
server = {
HTTP_PORT = 3000;
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}/";
DISABLE_SSH = false;
SSH_PORT = 22;
LFS_START_SERVER = cfg.lfs.enable;
LFS_CONTENT_PATH = cfg.lfs.storage;
};
database = {
DB_TYPE = "postgres";
HOST = "/run/postgresql";
NAME = "forgejo";
USER = "forgejo";
PASSWD = mkIf (cfg.database.passwordFile != null) "#dbpass#";
};
service = {
DISABLE_REGISTRATION = false;
REQUIRE_SIGNIN_VIEW = false;
ENABLE_NOTIFY_MAIL = cfg.mailer.enable;
};
mailer = mkIf cfg.mailer.enable {
ENABLED = true;
FROM = cfg.mailer.from;
SMTP_ADDR = cfg.mailer.host;
SMTP_PORT = toString cfg.mailer.port;
USER = "#smtpuser#";
PASSWD = "#smtppass#";
PROTOCOL = "smtps";
};
session = {
COOKIE_SECURE = true;
COOKIE_SAMESITE = "strict";
};
security = {
INSTALL_LOCK = true;
PASSWORD_CHECK_PWN = true;
PASSWORD_COMPLEXITY = "lower,digit"
};
};
};
# Set database password from file
systemd.services.forgejo = {
serviceConfig = mkMerge [
(mkIf (cfg.database.passwordFile != null) {
EnvironmentFile = cfg.database.passwordFile;
})
(mkIf cfg.mailer.enable {
EnvironmentFile = [ cfg.mailer.userFile cfg.mailer.passwordFile ];
})
];
};
# Nginx reverse proxy
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts.${cfg.domain} = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://localhost:3000";
# Proxy for WebSocket
locations."/" = {
proxyPass = "http://localhost:3000";
proxyWebsockets = true;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 86400;
'';
};
# Proxy for LFS
locations."/.git/info/lfs" = {
proxyPass = "http://localhost:3000";
extraConfig = ''
client_max_body_size 0;
'';
};
};
};
# Firewall
networking.firewall = {
allowedTCPPorts = [ 80 443 ];
allowedUDPPorts = [ 443 ];
};
# Backup service
systemd.services.forgejo-backup = mkIf cfg.backup.enable {
description = "Forgejo backup service";
serviceConfig = {
Type = "oneshot";
User = "forgejo";
Group = "forgejo";
WorkingDirectory = cfg.stateDir;
ExecStart = "${pkgs.forgejo}/bin/forgejo dump --type zip --file ${cfg.backup.backupDir}/forgejo-backup-%Y-%m-%d.zip";
};
};
systemd.timers.forgejo-backup = mkIf cfg.backup.enable {
description = "Forgejo backup timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.backup.interval;
Persistent = true;
};
};
# Backup cleanup
systemd.services.forgejo-backup-cleanup = mkIf cfg.backup.enable {
description = "Clean up old Forgejo backups";
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = pkgs.writeShellScript "forgejo-backup-cleanup" ''
find ${cfg.backup.backupDir} -name "forgejo-backup-*.zip" -mtime +${toString cfg.backup.retentionDays} -delete
'';
};
};
systemd.timers.forgejo-backup-cleanup = mkIf cfg.backup.enable {
description = "Forgejo backup cleanup timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "weekly";
Persistent = true;
};
};
# Create backup directory
system.activationScripts.forgejo-backup-dir = ''
mkdir -p ${cfg.backup.backupDir}
chown forgejo:forgejo ${cfg.backup.backupDir}
chmod 750 ${cfg.backup.backupDir}
'';
# Add Forgejo to known services
environment.systemPackages = with pkgs; [
forgejo
];
};
}