forgejo good, nginx coming along

This commit is contained in:
Julian Sutter 2026-02-15 22:46:17 -08:00
parent 29eb6493be
commit 8fdbb33939
40 changed files with 153 additions and 493 deletions

0
.gitignore vendored Normal file → Executable file
View file

0
README.md Normal file → Executable file
View file

0
appflakes/immich/README.md Normal file → Executable file
View file

0
appflakes/immich/flake.lock generated Normal file → Executable file
View file

0
appflakes/immich/flake.nix Normal file → Executable file
View file

0
appflakes/octofriend/README.md Normal file → Executable file
View file

0
appflakes/octofriend/flake.lock generated Normal file → Executable file
View file

0
appflakes/octofriend/flake.nix Normal file → Executable file
View file

View file

@ -1 +0,0 @@
/nix/store/cmq4skpafnf055fyjmk8qxjhvbxl0xws-octofriend-git-6d9c260

0
desktop/3dprinting.nix Normal file → Executable file
View file

0
desktop/daw.nix Normal file → Executable file
View file

0
desktop/dev.nix Normal file → Executable file
View file

0
desktop/dnm.nix Normal file → Executable file
View file

0
desktop/gaming.nix Normal file → Executable file
View file

0
desktop/media.nix Normal file → Executable file
View file

0
desktop/office.nix Normal file → Executable file
View file

0
desktop/plasma.nix Normal file → Executable file
View file

0
desktop/stp-elc-udmshare.nix Normal file → Executable file
View file

0
desktop/tailscale.nix Normal file → Executable file
View file

0
desktop/virtualization.nix Normal file → Executable file
View file

0
docs/README-unstable and stable packages.md Normal file → Executable file
View file

0
docs/agents.md Normal file → Executable file
View file

82
flake.lock generated Normal file → Executable file
View file

@ -1,23 +1,5 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@ -85,54 +67,6 @@
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1765779637,
"narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"octofriend": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2",
"octofriend": "octofriend_2"
},
"locked": {
"path": "./appflakes/octofriend",
"type": "path"
},
"original": {
"path": "./appflakes/octofriend",
"type": "path"
},
"parent": []
},
"octofriend_2": {
"flake": false,
"locked": {
"lastModified": 1766013652,
"narHash": "sha256-X7t2CGNZP+OS2pbMtRAg3XIGq3HA3N3rPk5ELV1FNRQ=",
"owner": "synthetic-lab",
"repo": "octofriend",
"rev": "6d9c260743e4b645fd4b216ce1cd979d5c0dd473",
"type": "github"
},
"original": {
"owner": "synthetic-lab",
"repo": "octofriend",
"type": "github"
}
},
"plasma-manager": {
"inputs": {
"home-manager": [
@ -162,24 +96,8 @@
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"octofriend": "octofriend",
"plasma-manager": "plasma-manager"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

6
flake.nix Normal file → Executable file
View file

@ -17,10 +17,9 @@
inputs.home-manager.follows = "home-manager";
};
octofriend = { url = "path:./appflakes/octofriend"; };
};
outputs = { self, nixpkgs, nixpkgs-unstable, nixos-hardware, home-manager, plasma-manager, octofriend }:
outputs = { self, nixpkgs, nixpkgs-unstable, nixos-hardware, home-manager, plasma-manager }:
let
localSystem = {
system = "x86_64-linux";
@ -32,7 +31,6 @@
config.allowUnfree = true;
};
inherit octofriend;
};
mkSystem = { modules, hardware ? [] }: nixpkgs.lib.nixosSystem {
@ -45,6 +43,7 @@
# Common desktop modules
commonDesktopModules = [
./systems/common.nix
./systems/desktop.nix
./users/jsutter.nix
./desktop/plasma.nix
./desktop/dev.nix
@ -86,6 +85,7 @@
./systems/common.nix
./users/jsutter.nix
./systems/warp.nix
./servers/forgejo.nix
];
};
};

0
servers/README.md Normal file → Executable file
View file

0
servers/common.nix Normal file → Executable file
View file

364
servers/forgejo.nix Normal file → Executable file
View file

@ -1,314 +1,92 @@
{ config, pkgs, lib, ... }:
with lib;
{ lib, pkgs, config, ... }:
let
cfg = config.services.forgejo;
in {
options.services.forgejo = {
enable = mkEnableOption "Forgejo Git server";
srv = cfg.settings.server;
domain = mkOption {
type = types.str;
example = "git.example.com";
description = "The domain name for Forgejo";
fqdn = "git.symbiotrip.com";
smtpPassword = "Monaco55";
runnerToken = "PUT_RUNNER_REGISTRATION_TOKEN_HERE";
adminPassword = "2wiggyWah!";
adminUser = "jsutter";
adminEmail = "jsutter@symbiotrip.com";
in
{
security.acme.certs.${fqdn}.group = config.services.nginx.group;
services.nginx.virtualHosts.${fqdn} = {
forceSSL = true;
enableACME = true;
useACMEHost = fqdn;
acmeRoot = null;
extraConfig = ''
client_max_body_size 512M;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
locations."/".proxyPass = "http://localhost:${toString srv.HTTP_PORT}";
};
stateDir = mkOption {
type = types.str;
default = "/var/lib/forgejo";
description = "Directory for Forgejo data";
services.forgejo = {
enable = true;
database.type = "postgres";
lfs.enable = true;
settings = {
server = {
DOMAIN = fqdn;
ROOT_URL = "https://${fqdn}/";
HTTP_PORT = 3000;
};
database = {
createLocally = mkOption {
type = types.bool;
default = true;
description = "Whether to create a local PostgreSQL database";
};
service.DISABLE_REGISTRATION = true;
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/forgejo-db";
description = "Path to file containing database password";
};
actions = {
ENABLED = true;
DEFAULT_ACTIONS_URL = "github";
};
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"
SMTP_ADDR = "mail.example.com";
FROM = "noreply@${fqdn}";
USER = "noreply@${fqdn}";
PASSWD = smtpPassword;
};
};
};
# 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 ];
})
];
};
# Create/ensure admin user
systemd.services.forgejo.preStart = let
adminCmd = "${lib.getExe cfg.package} admin user";
in ''
${adminCmd} create \
--admin \
--username ${adminUser} \
--email "${adminEmail}" \
--password "${adminPassword}" \
--must-change-password=false || true
'';
# Nginx reverse proxy
services.nginx = {
# Actions runner (runs jobs in Docker containers per labels)
virtualisation.docker.enable = true;
services.gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances.default = {
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
name = "warp";
url = "https://${fqdn}";
token = runnerToken;
labels = [
"node-22:docker://node:22-bookworm"
"nixos-latest:docker://nixos/nix"
# "native:host"
];
};
};
}

0
servers/hugo.nix Normal file → Executable file
View file

29
servers/nginx.nix Normal file
View file

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
let
# WARNING: this ends up world-readable in the Nix store if you inline it.
cloudflareEnv = pkgs.writeText "cloudflare-acme.env" ''
umnyPSYOr9U3m404_IBMl4PTOzg29nz_XzNEGw2v
'';
in
{
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
};
security.acme = {
acceptTerms = true;
# These defaults are inherited by security.acme.certs.* unless overridden. :contentReference[oaicite:0]{index=0}
defaults = {
email = "admin@symbiotrip.com";
dnsProvider = "cloudflare"; # :contentReference[oaicite:1]{index=1}
environmentFile = cloudflareEnv; # :contentReference[oaicite:2]{index=2}
};
};
}

0
systems/aurora.nix Normal file → Executable file
View file

22
systems/common.nix Normal file → Executable file
View file

@ -61,12 +61,6 @@
# Services
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
services.openssh.enable = true;
security.polkit.enable = true;
programs.zsh.enable = true;
@ -103,7 +97,6 @@
python3
pv
whois
kdePackages.xdg-desktop-portal-kde
fwupd
usbutils # lsusb
pciutils # lspci
@ -113,23 +106,8 @@
nvme-cli
smartmontools
ripgrep
mesa-demos
];
services.flatpak.enable = true;
fonts.packages = with pkgs; [
noto-fonts-color-emoji
noto-fonts-cjk-sans
liberation_ttf
fira-code
fira-code-symbols
mplus-outline-fonts.githubRelease
dina-font
proggyfonts
];
services.xserver.excludePackages = [ pkgs.xterm ];
documentation.nixos.enable = false;
systemd.network.wait-online.enable = false;

29
systems/desktop.nix Executable file
View file

@ -0,0 +1,29 @@
{ config, pkgs, ... }:
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
environment.systemPackages = with pkgs; [
kdePackages.xdg-desktop-portal-kde
mesa-demos
];
services.flatpak.enable = true;
fonts.packages = with pkgs; [
noto-fonts-color-emoji
noto-fonts-cjk-sans
liberation_ttf
fira-code
fira-code-symbols
mplus-outline-fonts.githubRelease
dina-font
proggyfonts
];
services.xserver.excludePackages = [ pkgs.xterm ];
}

0
systems/framework.nix Normal file → Executable file
View file

0
systems/labrizor.nix Normal file → Executable file
View file

View file

@ -1,71 +0,0 @@
{ config, lib, pkgs, modulesPath, ... }: {
networking.hostName = "skip01";
# CPU Settings for 13th gen Intel Core i5
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Boot and kernel modules for Intel NUC
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ]; # No graphics drivers needed for headless
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [];
# Server-specific kernel parameters
boot.kernelParams = [
"intel_iommu=on" # Enable IOMMU for virtualization
];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# No graphics support needed for headless server
# Power management optimized for server use
powerManagement = {
enable = true;
cpuFreqGovernor = "ondemand";
};
# Thermal management
services.thermald.enable = true;
# Server-oriented settings
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "no";
};
};
# Swap configuration (smaller for server use)
swapDevices = [
{
device = "/swapfile";
size = 8192; # 8GB swap for server workload
priority = 0;
}
];
# Enable hardware monitoring (CLI only)
hardware.sensor.iio.enable = true;
# Network performance tuning for server use
boot.kernel.sysctl = {
"net.core.rmem_max" = 134217728;
"net.core.wmem_max" = 134217728;
"net.ipv4.tcp_rmem" = "4096 65536 134217728";
"net.ipv4.tcp_wmem" = "4096 65536 134217728";
"net.core.netdev_max_backlog" = 5000;
};
# Disable unnecessary services for server use
services.xserver.enable = lib.mkForce false;
services.pipewire.enable = lib.mkForce false;
# Enable container support for future server services
virtualisation.docker.enable = true;
virtualisation.podman.enable = lib.mkDefault true;
}

36
systems/warp.nix Normal file → Executable file
View file

@ -1,32 +1,33 @@
{ config, lib, pkgs, modulesPath, ... }: {
networking.hostName = "warp";
# CPU Settings for 13th gen Intel Core
fileSystems."/" =
{ device = "/dev/disk/by-partlabel/primary";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-partlabel/ESP";
fsType = "vfat";
};
# CPU Settings for 13th gen Intel Core i5
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# Boot and kernel modules for Intel NUC
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
# File system configuration - placeholder for actual hardware
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
boot.initrd.kernelModules = [];
boot.kernelModules = [ "kvm-intel" ];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ]; # No graphics drivers needed for headless
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [];
# Server-specific kernel parameters
boot.kernelParams = [
"intel_iommu=on"
"intel_iommu=on" # Enable IOMMU for virtualization
];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# No graphics support needed for headless server
# Power management optimized for server use
powerManagement = {
enable = true;
@ -46,16 +47,16 @@
};
};
# Swap configuration
# Swap configuration (smaller for server use)
swapDevices = [
{
device = "/swapfile";
size = 8192;
size = 8192; # 8GB swap for server workload
priority = 0;
}
];
# Enable hardware monitoring
# Enable hardware monitoring (CLI only)
hardware.sensor.iio.enable = true;
# Network performance tuning for server use
@ -70,9 +71,8 @@
# Disable unnecessary services for server use
services.xserver.enable = lib.mkForce false;
services.pipewire.enable = lib.mkForce false;
services.flatpak.enable = lib.mkForce false;
# Enable container support for server services
# Enable container support for future server services
virtualisation.docker.enable = true;
virtualisation.podman.enable = lib.mkDefault true;
}

0
users/aksutter.nix Normal file → Executable file
View file

0
users/common-home.nix Normal file → Executable file
View file

0
users/isutter.nix Normal file → Executable file
View file

0
users/jsutter.nix Normal file → Executable file
View file