refactor(nix): extract common system configs into reusable modules

Create 6 new NixOS modules to reduce duplication across system configs:
- hardware/wooting: Wooting keyboard udev rules and Bluetooth compat
- services/nginx-base: Common nginx server settings
- services/acme-base: ACME certificate defaults
- services/xremap: Key remapping with sensible defaults
- system/no-sleep: Disable sleep/suspend/hibernate targets
- system/kernel-tweaks: PM freeze timeout and zram configuration

Update system configuration files to use these new modules.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Harald Hoyer 2026-01-30 06:06:03 +01:00
parent ea849f2488
commit 4622c52d5b
21 changed files with 310 additions and 218 deletions

View file

@ -0,0 +1,25 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.hardware.wooting;
in
{
options.metacfg.hardware.wooting = with types; {
enable = mkBoolOpt false "Whether or not to enable Wooting keyboard support.";
enableBluetoothCompat = mkBoolOpt true "Disable ClassicBondedOnly for Bluetooth compatibility.";
};
config = mkIf cfg.enable {
hardware.bluetooth.input.General.ClassicBondedOnly = mkIf cfg.enableBluetoothCompat false;
services.udev.extraRules = ''
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e4c5", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e489", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
'';
};
}

View file

@ -0,0 +1,41 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.services.acmeBase;
in
{
options.metacfg.services.acmeBase = with types; {
enable = mkBoolOpt false "Whether or not to enable ACME with common settings.";
email = mkOption {
type = types.str;
default = "harald@hoyer.xyz";
description = "Registration email for ACME.";
};
dnsProvider = mkOption {
type = types.str;
default = "cloudflare";
description = "DNS provider for ACME DNS-01 challenge.";
};
credentialsFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to the credentials file for the DNS provider.";
};
};
config = mkIf cfg.enable {
security.acme = {
acceptTerms = true;
defaults = {
email = cfg.email;
dnsProvider = cfg.dnsProvider;
credentialsFile = mkIf (cfg.credentialsFile != null) cfg.credentialsFile;
};
};
};
}

View file

@ -0,0 +1,42 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.services.nginxBase;
in
{
options.metacfg.services.nginxBase = with types; {
enable = mkBoolOpt false "Whether or not to enable nginx with common settings.";
clientMaxBodySize = mkOption {
type = types.str;
default = "1000M";
description = "Maximum allowed size of the client request body.";
};
enableAcmeGroup = mkBoolOpt true "Add nginx user to acme group.";
enableVcombinedLog = mkBoolOpt true "Enable vcombined log format.";
};
config = mkIf cfg.enable {
users.users.nginx.extraGroups = mkIf cfg.enableAcmeGroup [ "acme" ];
services.nginx = {
enable = true;
clientMaxBodySize = cfg.clientMaxBodySize;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
appendHttpConfig = mkIf cfg.enableVcombinedLog ''
log_format vcombined '$host:$server_port '
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log vcombined;
'';
};
};
}

View file

@ -0,0 +1,44 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.services.xremap;
in
{
options.metacfg.services.xremap = with types; {
enable = mkBoolOpt false "Whether or not to enable xremap key remapping.";
userName = mkOption {
type = types.str;
default = "harald";
description = "User to run xremap as.";
};
withGnome = mkBoolOpt true "Enable GNOME support.";
deviceNames = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of device names to remap.";
};
config = mkOption {
type = types.attrs;
default = { };
description = "Xremap configuration.";
};
};
config = {
services.xremap = {
enable = cfg.enable;
userName = mkIf cfg.enable cfg.userName;
serviceMode = mkIf cfg.enable "user";
withGnome = mkIf cfg.enable cfg.withGnome;
deviceNames = mkIf cfg.enable cfg.deviceNames;
config = mkIf cfg.enable cfg.config;
};
users.users.${cfg.userName}.extraGroups = mkIf cfg.enable [ "input" ];
};
}

View file

@ -0,0 +1,29 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.system.kernelTweaks;
in
{
options.metacfg.system.kernelTweaks = with types; {
enable = mkBoolOpt false "Whether or not to enable desktop kernel optimizations.";
pmFreezeTimeout = mkOption {
type = types.int;
default = 30000;
description = "PM freeze timeout in milliseconds.";
};
enableZram = mkBoolOpt true "Enable zram swap.";
};
config = mkIf cfg.enable {
boot.kernel.sysctl = {
"power.pm_freeze_timeout" = cfg.pmFreezeTimeout;
};
zramSwap.enable = cfg.enableZram;
};
}

View file

@ -0,0 +1,28 @@
{
config,
lib,
...
}:
with lib;
with lib.metacfg;
let
cfg = config.metacfg.system.noSleep;
in
{
options.metacfg.system.noSleep = with types; {
enable = mkBoolOpt false "Whether or not to disable all sleep targets.";
disableGdmAutoSuspend = mkBoolOpt false "Disable GDM auto-suspend.";
ignoreLidSwitch = mkBoolOpt false "Ignore lid switch events.";
};
config = mkIf cfg.enable {
systemd.targets.sleep.enable = false;
systemd.targets.suspend.enable = false;
systemd.targets.hibernate.enable = false;
systemd.targets.hybrid-sleep.enable = false;
services.displayManager.gdm.autoSuspend = mkIf cfg.disableGdmAutoSuspend false;
services.logind.settings.Login.HandleLidSwitch = mkIf cfg.ignoreLidSwitch "ignore";
};
}

View file

@ -9,7 +9,13 @@ with lib.metacfg;
services.spice-autorandr.enable = true;
services.spice-vdagentd.enable = true;
services.resolved.enable = true;
services.resolved.extraConfig = ''
ResolveUnicastSingleLabel=yes
'';
metacfg = {
system.noSleep.enable = true;
base.enable = true;
gui.enable = true;
nix-ld.enable = true;
@ -34,13 +40,6 @@ with lib.metacfg;
];
};
# Disable the GNOME3/GDM auto-suspend feature that cannot be disabled in GUI!
# If no user is logged in, the machine will power down after 20 minutes.
systemd.targets.sleep.enable = false;
systemd.targets.suspend.enable = false;
systemd.targets.hibernate.enable = false;
systemd.targets.hybrid-sleep.enable = false;
environment.systemPackages = with pkgs; [
azure-cli
desktop-file-utils
@ -60,16 +59,11 @@ with lib.metacfg;
services.ratbagd.enable = true;
services.resolved.enable = true;
#services.resolved.dnssec = "allow-downgrade";
services.resolved.extraConfig = ''
ResolveUnicastSingleLabel=yes
'';
virtualisation = {
docker.enable = true;
podman.dockerCompat = false;
libvirtd.enable = false;
rosetta.enable = true;
};
system.autoUpgrade = {
@ -78,7 +72,5 @@ with lib.metacfg;
allowReboot = false;
};
virtualisation.rosetta.enable = true;
system.stateVersion = "25.05";
}

View file

@ -9,7 +9,13 @@ with lib.metacfg;
services.spice-autorandr.enable = true;
services.spice-vdagentd.enable = true;
services.resolved.enable = true;
services.resolved.extraConfig = ''
ResolveUnicastSingleLabel=yes
'';
metacfg = {
system.noSleep.enable = true;
base.enable = true;
gui.enable = true;
nix-ld.enable = true;
@ -34,13 +40,6 @@ with lib.metacfg;
];
};
# Disable the GNOME3/GDM auto-suspend feature that cannot be disabled in GUI!
# If no user is logged in, the machine will power down after 20 minutes.
systemd.targets.sleep.enable = false;
systemd.targets.suspend.enable = false;
systemd.targets.hibernate.enable = false;
systemd.targets.hybrid-sleep.enable = false;
environment.systemPackages = with pkgs; [
azure-cli
desktop-file-utils
@ -60,16 +59,11 @@ with lib.metacfg;
services.ratbagd.enable = true;
services.resolved.enable = true;
#services.resolved.dnssec = "allow-downgrade";
services.resolved.extraConfig = ''
ResolveUnicastSingleLabel=yes
'';
virtualisation = {
docker.enable = true;
podman.dockerCompat = false;
libvirtd.enable = false;
rosetta.enable = true;
};
system.autoUpgrade = {
@ -78,7 +72,5 @@ with lib.metacfg;
allowReboot = false;
};
virtualisation.rosetta.enable = true;
system.stateVersion = "25.05";
}

View file

@ -18,21 +18,17 @@ with lib.metacfg;
22000
];
services.tailscale.enable = true;
services.cratedocs-mcp.enable = true;
services.openssh = {
enable = true;
};
hardware.bluetooth.input.General.ClassicBondedOnly = false;
services.udev.extraRules = ''
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e4c5", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e489", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
'';
services.tailscale.enable = true;
services.resolved.enable = true;
metacfg = {
hardware.wooting.enable = true;
base.enable = true;
gui.enable = true;
nix-ld.enable = true;
@ -59,15 +55,21 @@ with lib.metacfg;
"dialout"
"tss"
];
system.kernelTweaks.enable = true;
};
system.autoUpgrade = {
enable = true;
operation = "boot";
allowReboot = false;
};
nixpkgs.config.permittedInsecurePackages = [
"electron-27.3.11"
];
# Kernel tuning
# Additional kernel tuning beyond the module defaults
boot.kernel.sysctl = {
"power.pm_freeze_timeout" = 30000;
# Reduce swap usage (you have zram)
"vm.swappiness" = 10;
# Prefer keeping directory/inode caches
@ -111,32 +113,18 @@ with lib.metacfg;
# zram swap with zstd compression for better performance
zramSwap = {
enable = true;
algorithm = "zstd";
memoryPercent = 50;
};
services.ratbagd.enable = true;
services.resolved.enable = true;
#services.resolved.dnssec = "allow-downgrade";
#services.resolved.extraConfig = ''
# ResolveUnicastSingleLabel=yes
#'';
virtualisation = {
libvirtd.enable = true;
docker.enable = true;
podman.dockerCompat = false;
};
system.autoUpgrade = {
enable = true;
operation = "boot";
allowReboot = false;
};
services.trezord.enable = true;
services.ollama = {

View file

@ -1,33 +1,21 @@
# In /etc/nixos/configuration.nix
{ ... }:
{
users.users.harald.extraGroups = [ "input" ];
# Enable the xremap service
services.xremap.enable = true;
services.xremap.userName = "harald"; # Replace with your username
services.xremap.serviceMode = "user"; # Run as user service, not system-wide
services.xremap.withGnome = true;
# Add a specific configuration block to select your keyboard(s) by name
services.xremap.deviceNames = [
# Use the name found in the log output: "Hangsheng MonsGeek Keyboard System Control"
"Hangsheng MonsGeek Keyboard"
"HS Galaxy100 Keyboard"
# You can usually shorten the name slightly to match the device you want
];
# Define your remapping configuration using Nix's attribute set format
services.xremap.config = {
keymap = [
{
remap = {
# Map Alt+C (LeftAlt-C) to Ctrl+C (LeftControl-C)
LeftAlt-C = "COPY";
LeftAlt-V = "PASTE";
LeftAlt-X = "CUT";
};
}
metacfg.services.xremap = {
enable = true;
deviceNames = [
"Hangsheng MonsGeek Keyboard"
"HS Galaxy100 Keyboard"
];
config = {
keymap = [
{
remap = {
LeftAlt-C = "COPY";
LeftAlt-V = "PASTE";
LeftAlt-X = "CUT";
};
}
];
};
};
}

View file

@ -1,6 +1,4 @@
{
pkgs,
lib,
config,
...
}:
@ -9,14 +7,9 @@
sopsFile = ../../../.secrets/hetzner/internetbs.yaml; # bring your own password file
};
security.acme = {
acceptTerms = true;
defaults = {
email = "harald@hoyer.xyz";
dnsProvider = "cloudflare";
credentialsFile = config.sops.secrets.internetbs.path;
};
certs = {
metacfg.services.acmeBase.credentialsFile = config.sops.secrets.internetbs.path;
security.acme.certs = {
"surfsite.org" = {
extraDomainNames = [ "*.surfsite.org" ];
};
@ -71,5 +64,4 @@
extraDomainNames = [ "*.harald-hoyer.de" ];
};
};
};
}

View file

@ -22,6 +22,8 @@
services.tailscale.enable = true;
metacfg = {
services.nginxBase.enable = true;
services.acmeBase.enable = true;
emailOnFailure.enable = true;
base.enable = true;
nix.enable = true;
@ -42,7 +44,6 @@
dates = "04:00";
operation = "switch";
allowReboot = true;
# flake = lib.mkForce "git+file:///var/lib/gitea/repositories/harald/nixcfg.git#mx";
flake = lib.mkForce "/root/nixcfg/.#mx";
};

View file

@ -1,21 +1,6 @@
{ ... }:
{
users.users.nginx.extraGroups = [ "acme" ];
services.nginx = {
enable = true;
clientMaxBodySize = "1000M";
appendHttpConfig = ''
log_format vcombined '$host:$server_port '
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log vcombined;
'';
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts = {
services.nginx.virtualHosts = {
"00000" = {
useACMEHost = "hoyer.xyz";
serverName = "_";
@ -157,5 +142,4 @@
forceSSL = true;
};
};
};
}

View file

@ -6,8 +6,6 @@
{
imports = [ ./hardware-configuration.nix ];
services.tailscale.enable = true;
boot.kernelPackages = lib.mkOverride 0 pkgs.linuxPackages_latest;
boot.loader.systemd-boot.enable = false;
# Bootloader.
@ -18,6 +16,8 @@
security.tpm2.enable = false;
security.tpm2.abrmd.enable = false;
services.tailscale.enable = true;
metacfg = {
base.enable = true;
nix-ld.enable = true;
@ -37,12 +37,6 @@
podman.dockerCompat = false;
};
system.autoUpgrade = {
enable = true;
operation = "switch";
allowReboot = true;
};
networking.wireless.enable = false; # Enables wireless support via wpa_supplicant.
networking.firewall.allowPing = true;
@ -66,5 +60,11 @@
}
];
system.autoUpgrade = {
enable = true;
operation = "switch";
allowReboot = true;
};
system.stateVersion = "25.05";
}

View file

@ -1,7 +1,5 @@
{
pkgs,
lib,
config,
...
}:
with lib;
@ -17,17 +15,17 @@ with lib.metacfg;
nix.enable = true;
};
virtualisation = {
docker.enable = true;
podman.dockerCompat = false;
};
system.autoUpgrade = {
enable = true;
operation = "switch";
allowReboot = true;
};
virtualisation = {
docker.enable = true;
podman.dockerCompat = false;
};
security.tpm2.enable = false;
security.tpm2.abrmd.enable = false;

View file

@ -7,14 +7,9 @@
sopsFile = ../../../.secrets/sgx/internetbs.yaml; # bring your own password file
};
security.acme = {
acceptTerms = true;
defaults = {
email = "harald@hoyer.xyz";
dnsProvider = "cloudflare";
credentialsFile = config.sops.secrets.internetbs.path;
};
certs = {
metacfg.services.acmeBase.credentialsFile = config.sops.secrets.internetbs.path;
security.acme.certs = {
"internal.hoyer.world" = {
extraDomainNames = [
"openwebui.hoyer.world"
@ -23,5 +18,4 @@
];
};
};
};
}

View file

@ -12,8 +12,6 @@
./wyoming.nix
];
services.tailscale.enable = true;
boot.tmp.useTmpfs = false;
sops.secrets.pccs.sopsFile = ../../../.secrets/sgx/pccs.yaml;
@ -23,7 +21,16 @@
claude-code
];
services.tailscale.enable = true;
metacfg = {
services.nginxBase.enable = true;
services.acmeBase.enable = true;
system.noSleep = {
enable = true;
disableGdmAutoSuspend = true;
ignoreLidSwitch = true;
};
emailOnFailure.enable = true;
base.enable = true;
gui.enable = true;
@ -58,13 +65,5 @@
allowReboot = true;
};
systemd.targets.sleep.enable = false;
systemd.targets.suspend.enable = false;
systemd.targets.hibernate.enable = false;
systemd.targets.hybrid-sleep.enable = false;
services.displayManager.gdm.autoSuspend = false;
services.logind.settings.Login.HandleLidSwitch = "ignore";
system.stateVersion = "23.11";
}

View file

@ -3,22 +3,7 @@
...
}:
{
users.users.nginx.extraGroups = [ "acme" ];
services.nginx = {
enable = true;
clientMaxBodySize = "1000M";
appendHttpConfig = ''
log_format vcombined '$host:$server_port '
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log vcombined;
'';
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts = {
services.nginx.virtualHosts = {
"openwebui.hoyer.world" = {
enableACME = false;
useACMEHost = "internal.hoyer.world";
@ -48,5 +33,4 @@
};
};
};
};
}

View file

@ -2,6 +2,8 @@
{
imports = [ ./hardware-configuration.nix ];
services.resolved.enable = true;
metacfg = {
base.enable = true;
gui.enable = true;
@ -27,9 +29,6 @@
system.stateVersion = "23.11";
services.resolved.enable = true;
#services.resolved.dnssec = "allow-downgrade";
sops.age.sshKeyPaths = [ "/persist/ssh/ssh_host_ed25519_key" ];
sops.secrets.backup-s3.sopsFile = ../../../.secrets/t15/backup-s3.yaml;
sops.secrets.backup-pw.sopsFile = ../../../.secrets/t15/backup-s3.yaml;

View file

@ -20,8 +20,6 @@ with lib.metacfg;
programs.ccache.enable = true;
nix.settings.extra-sandbox-paths = [ config.programs.ccache.cacheDir ];
services.tailscale.enable = true;
services.cratedocs-mcp.enable = true;
sops.age.sshKeyPaths = [ "/var/lib/secrets/ssh_host_ed25519_key" ];
@ -45,13 +43,11 @@ with lib.metacfg;
];
};
hardware.bluetooth.input.General.ClassicBondedOnly = false;
services.udev.extraRules = ''
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e4c5", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="342d", ATTRS{idProduct}=="e489", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl"
'';
services.tailscale.enable = true;
services.resolved.enable = true;
metacfg = {
hardware.wooting.enable = true;
base.enable = true;
gui.enable = true;
nix-ld.enable = true;
@ -77,17 +73,19 @@ with lib.metacfg;
"dialout"
"tss"
];
system.kernelTweaks.enable = true;
};
system.autoUpgrade = {
enable = true;
operation = "boot";
allowReboot = false;
};
nixpkgs.config.permittedInsecurePackages = [
"electron-27.3.11"
];
# increase freezing timeout
boot.kernel.sysctl = {
"power.pm_freeze_timeout" = 30000;
};
environment.systemPackages = with pkgs; [
attic-client
azure-cli
@ -112,26 +110,12 @@ with lib.metacfg;
vscode
];
zramSwap.enable = true;
services.ratbagd.enable = true;
services.resolved.enable = true;
#services.resolved.dnssec = "allow-downgrade";
#services.resolved.extraConfig = ''
# ResolveUnicastSingleLabel=yes
#'';
virtualisation = {
libvirtd.enable = true;
};
system.autoUpgrade = {
enable = true;
operation = "boot";
allowReboot = false;
};
services.trezord.enable = true;
services.ollama = {

View file

@ -1,33 +1,21 @@
# In /etc/nixos/configuration.nix
{ ... }:
{
users.users.harald.extraGroups = [ "input" ];
# Enable the xremap service
services.xremap.enable = true;
services.xremap.userName = "harald"; # Replace with your username
services.xremap.serviceMode = "user"; # Run as user service, not system-wide
services.xremap.withGnome = true;
# Add a specific configuration block to select your keyboard(s) by name
services.xremap.deviceNames = [
# Use the name found in the log output: "Hangsheng MonsGeek Keyboard System Control"
"Hangsheng MonsGeek Keyboard"
"HS Galaxy100 Keyboard"
# You can usually shorten the name slightly to match the device you want
];
# Define your remapping configuration using Nix's attribute set format
services.xremap.config = {
keymap = [
{
remap = {
# Map Alt+C (LeftAlt-C) to Ctrl+C (LeftControl-C)
LeftAlt-C = "COPY";
LeftAlt-V = "PASTE";
LeftAlt-X = "CUT";
};
}
metacfg.services.xremap = {
enable = true;
deviceNames = [
"Hangsheng MonsGeek Keyboard"
"HS Galaxy100 Keyboard"
];
config = {
keymap = [
{
remap = {
LeftAlt-C = "COPY";
LeftAlt-V = "PASTE";
LeftAlt-X = "CUT";
};
}
];
};
};
}