From 1d182437dbda7be6365163afe071dd08f3ab809b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 16:17:10 +0100 Subject: [PATCH 01/16] feat: nb update to 25.11 --- hosts/nb/channel | 2 +- hosts/nb/configuration.nix | 2 +- hosts/nb/hardware-configuration.nix | 2 +- hosts/nb/modules/desktop/bitwarden.nix | 5 +- hosts/nb/modules/desktop/default.nix | 6 +- hosts/nb/modules/development/coding.nix | 2 +- hosts/nb/modules/development/default.nix | 7 +- .../modules/development/nvim/config/init.lua | 3 + .../development/nvim/config/lspconfig.lua | 59 ++---- .../development/nvim/config/project.lua | 2 +- .../development/nvim/config/which-key.lua | 191 +++++++----------- hosts/nb/modules/development/nvim/default.nix | 8 +- hosts/nb/users/default.nix | 2 +- hosts/nb/users/dominik.nix | 23 +-- 14 files changed, 119 insertions(+), 195 deletions(-) diff --git a/hosts/nb/channel b/hosts/nb/channel index 93f5df5..57f31e7 100644 --- a/hosts/nb/channel +++ b/hosts/nb/channel @@ -1 +1 @@ -https://channels.nixos.org/nixos-25.05 +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/nb/configuration.nix b/hosts/nb/configuration.nix index 430764f..8b20710 100644 --- a/hosts/nb/configuration.nix +++ b/hosts/nb/configuration.nix @@ -307,7 +307,7 @@ in { ''; }; - services.xserver.desktopManager.gnome.extraGSettingsOverrides = '' + services.desktopManager.gnome.extraGSettingsOverrides = '' [org.gnome.desktop.interface] cursor-size=24 ''; diff --git a/hosts/nb/hardware-configuration.nix b/hosts/nb/hardware-configuration.nix index e9fc49d..0ae27a3 100644 --- a/hosts/nb/hardware-configuration.nix +++ b/hosts/nb/hardware-configuration.nix @@ -115,7 +115,7 @@ hardware.graphics = { enable = true; extraPackages = with pkgs; [ - vaapiVdpau + libva-vdpau-driver libvdpau-va-gl libva libva-utils diff --git a/hosts/nb/modules/desktop/bitwarden.nix b/hosts/nb/modules/desktop/bitwarden.nix index fbe3db6..9de4724 100644 --- a/hosts/nb/modules/desktop/bitwarden.nix +++ b/hosts/nb/modules/desktop/bitwarden.nix @@ -26,14 +26,13 @@ in description = "Bitwarden Desktop"; after = [ "graphical-session.target" "network-online.target" ]; wantedBy = [ "graphical-session.target" ]; - serviceConfig.ExecStart = "${pkgs.bitwarden}/bin/bitwarden"; + serviceConfig.ExecStart = "${pkgs.bitwarden-desktop}/bin/bitwarden-desktop"; serviceConfig.Restart = "on-abort"; }; #### Handy tools ############################################################# environment.systemPackages = with pkgs; [ - goldwarden - bitwarden + bitwarden-desktop bitwarden-cli fprintd lxqt.lxqt-policykit diff --git a/hosts/nb/modules/desktop/default.nix b/hosts/nb/modules/desktop/default.nix index 32088d1..b58ada7 100644 --- a/hosts/nb/modules/desktop/default.nix +++ b/hosts/nb/modules/desktop/default.nix @@ -57,10 +57,10 @@ in { netflix networkmanagerapplet nextcloud-client - onlyoffice-bin + onlyoffice-desktopeditors obs-studio pavucontrol - pinentry + pinentry-gnome3 rbw rofi-rbw swayimg @@ -103,7 +103,7 @@ in { fonts.packages = with pkgs; [ noto-fonts noto-fonts-cjk-sans - noto-fonts-emoji + noto-fonts-color-emoji nerd-fonts._0xproto nerd-fonts.droid-sans-mono open-sans diff --git a/hosts/nb/modules/development/coding.nix b/hosts/nb/modules/development/coding.nix index 482d8a5..fa8c109 100644 --- a/hosts/nb/modules/development/coding.nix +++ b/hosts/nb/modules/development/coding.nix @@ -20,7 +20,7 @@ in { nixpkgs.config.android_sdk.accept_license = true; programs.adb.enable = true; # sets up udev + adb group - services.udev.packages = [ pkgs.android-udev-rules ]; + # android-udev-rules removed in 25.11 - superseded by built-in systemd uaccess rules users.users.dominik.extraGroups = [ "adbusers" ]; } diff --git a/hosts/nb/modules/development/default.nix b/hosts/nb/modules/development/default.nix index eb94961..969fff5 100644 --- a/hosts/nb/modules/development/default.nix +++ b/hosts/nb/modules/development/default.nix @@ -52,11 +52,6 @@ in { # Socket activation - only start when needed to save battery onBoot = "ignore"; onShutdown = "shutdown"; - qemu = { - ovmf = { - enable = true; # Enable OVMF firmware support - }; - # swtpm.enable = true; # enable if you need TPM emulation, etc. - }; + # qemu.swtpm.enable = true; # enable if you need TPM emulation, etc. }; } diff --git a/hosts/nb/modules/development/nvim/config/init.lua b/hosts/nb/modules/development/nvim/config/init.lua index a4af92c..0212e85 100644 --- a/hosts/nb/modules/development/nvim/config/init.lua +++ b/hosts/nb/modules/development/nvim/config/init.lua @@ -1,3 +1,6 @@ +-- Set leader key before any other mappings +vim.g.mapleader = " " + -- vim.opt.expandtab = true -- vim.opt.hidden = true -- vim.opt.incsearch = true diff --git a/hosts/nb/modules/development/nvim/config/lspconfig.lua b/hosts/nb/modules/development/nvim/config/lspconfig.lua index 94238e9..72b90ee 100644 --- a/hosts/nb/modules/development/nvim/config/lspconfig.lua +++ b/hosts/nb/modules/development/nvim/config/lspconfig.lua @@ -1,54 +1,31 @@ -local status, lspc = pcall(require, 'lspconfig') -if (not status) then return end - -lspc.clangd.setup{} - -local buf_map = function(bufnr, mode, lhs, rhs, opts) - vim.api.nvim_buf_set_keymap(bufnr, mode, lhs, rhs, opts or { - silent = true, - }) -end - -local protocol = require('vim.lsp.protocol') - -local on_attach = function(client, buffnr) - if client.server.capabilities.documentFormattingProvider then - vim.api.nvim_create_autocmd("BufWritePre", { - group = vim.api.nvim_create_augroup("format", { clear = true }), - buffer = buffnr, - callback = function() vim.lsp.buf.formatting_seq_sync() end - }) - end -end - +-- LSP Capabilities (for nvim-cmp integration) local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true capabilities = require('cmp_nvim_lsp').default_capabilities(capabilities) -local servers = { 'ts_ls', 'lua_ls', 'cssls', 'yamlls', 'intelephense', 'gopls' } -for _, lsp in pairs(servers) do - require('lspconfig')[lsp].setup { - -- on_attach = on_attach, - capabilities = capabilities, - } -end +-- Global LSP configuration +vim.lsp.config('*', { + capabilities = capabilities, +}) -lspc.yamlls.setup({ +-- Server-specific configurations +vim.lsp.config('clangd', {}) + +vim.lsp.config('yamlls', { settings = { yaml = { keyOrdering = false, }, }, -}); - --- autoformat json files with jq -vim.api.nvim_create_autocmd("FileType", { - pattern = "json", - callback = function(ev) - vim.bo[ev.buf].formatprg = "jq" - print("It's a json file") - end, }) +-- Enable all LSP servers +vim.lsp.enable({ 'clangd', 'ts_ls', 'lua_ls', 'cssls', 'yamlls', 'intelephense', 'gopls' }) --- lspc.intelephense.setup() +-- JSON file formatting with jq +vim.api.nvim_create_autocmd("FileType", { + pattern = "json", + callback = function(ev) + vim.bo[ev.buf].formatprg = "jq" + end, +}) diff --git a/hosts/nb/modules/development/nvim/config/project.lua b/hosts/nb/modules/development/nvim/config/project.lua index f56e374..42cd790 100644 --- a/hosts/nb/modules/development/nvim/config/project.lua +++ b/hosts/nb/modules/development/nvim/config/project.lua @@ -32,7 +32,7 @@ config = { ignore_lsp = {}, } -local status_ok, project = pcall(require, "project_nvim") +local status_ok, project = pcall(require, "project") if not status_ok then return end diff --git a/hosts/nb/modules/development/nvim/config/which-key.lua b/hosts/nb/modules/development/nvim/config/which-key.lua index 6b67e89..c510b8f 100644 --- a/hosts/nb/modules/development/nvim/config/which-key.lua +++ b/hosts/nb/modules/development/nvim/config/which-key.lua @@ -1,5 +1,3 @@ -vim.g.mapleader = " " - local function smart_quit() local bufnr = vim.api.nvim_get_current_buf() local modified = vim.api.nvim_buf_get_option(bufnr, "modified") @@ -27,122 +25,77 @@ end local wk = require("which-key") -wk.setup({}) - -wk.register({ - [""] = { - - [";"] = { "Alpha", "Dashboard" }, - ["w"] = { "w!", "Save" }, - ["q"] = { "smart_quit()", "Quit" }, - ["/"] = { "(comment_toggle_linewise_current)", "Comment toggle current line" }, - ["c"] = { "BufferKill", "Close Buffer" }, - ["f"] = { find_project_files, "Find File" }, - ["h"] = { "nohlsearch", "No Highlight" }, - ["t"] = { "TodoTelescope keywords=TODO,FIX", "Find TODO,FIX" }, - b = { - name = "Buffers", - j = { "BufferLinePick", "Jump" }, - f = { "Telescope buffers", "Find" }, - b = { "BufferLineCyclePrev", "Previous" }, - n = { "BufferLineCycleNext", "Next" }, - -- w = { "BufferWipeout", "Wipeout" }, -- TODO: implement this for bufferline - e = { - "BufferLinePickClose", - "Pick which buffer to close", - }, - h = { "BufferLineCloseLeft", "Close all to the left" }, - l = { - "BufferLineCloseRight", - "Close all to the right", - }, - D = { - "BufferLineSortByDirectory", - "Sort by directory", - }, - L = { - "BufferLineSortByExtension", - "Sort by language", - }, - }, - -- " Available Debug Adapters: - -- " https://microsoft.github.io/debug-adapter-protocol/implementors/adapters/ - -- " Adapter configuration and installation instructions: - -- " https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation - -- " Debug Adapter protocol: - -- " https://microsoft.github.io/debug-adapter-protocol/ - -- " Debugging - g = { - name = "Git", - g = { Lazygit_toggle, "Lazygit" }, - j = { "lua require 'gitsigns'.next_hunk({navigation_message = false})", "Next Hunk" }, - k = { "lua require 'gitsigns'.prev_hunk({navigation_message = false})", "Prev Hunk" }, - l = { "lua require 'gitsigns'.blame_line()", "Blame" }, - p = { "lua require 'gitsigns'.preview_hunk()", "Preview Hunk" }, - r = { "lua require 'gitsigns'.reset_hunk()", "Reset Hunk" }, - R = { "lua require 'gitsigns'.reset_buffer()", "Reset Buffer" }, - s = { "lua require 'gitsigns'.stage_hunk()", "Stage Hunk" }, - u = { - "lua require 'gitsigns'.undo_stage_hunk()", - "Undo Stage Hunk", - }, - o = { "Telescope git_status", "Open changed file" }, - b = { "Telescope git_branches", "Checkout branch" }, - c = { "Telescope git_commits", "Checkout commit" }, - C = { - "Telescope git_bcommits", - "Checkout commit(for current file)", - }, - d = { - "Gitsigns diffthis HEAD", - "Git Diff", - }, - }, - l = { - name = "LSP", - a = { "lua vim.lsp.buf.code_action()", "Code Action" }, - d = { "Telescope diagnostics bufnr=0 theme=get_ivy", "Buffer Diagnostics" }, - w = { "Telescope diagnostics", "Diagnostics" }, - -- f = { require("lvim.lsp.utils").format, "Format" }, - i = { "LspInfo", "Info" }, - I = { "Mason", "Mason Info" }, - j = { - vim.diagnostic.goto_next, - "Next Diagnostic", - }, - k = { - vim.diagnostic.goto_prev, - "Prev Diagnostic", - }, - l = { vim.lsp.codelens.run, "CodeLens Action" }, - q = { vim.diagnostic.setloclist, "Quickfix" }, - r = { vim.lsp.buf.rename, "Rename" }, - s = { "Telescope lsp_document_symbols", "Document Symbols" }, - S = { - "Telescope lsp_dynamic_workspace_symbols", - "Workspace Symbols", - }, - e = { "Telescope quickfix", "Telescope Quickfix" }, - }, - - - a = { "lua require('telescope.builtin').lsp_code_actions()", "Code Actions" }, - d = { "lua require('telescope.builtin').lsp_document_diagnostics()", "LSP Diagnostics" }, - k = { "lua vim.lsp.buf.signature_help()", "Signature Help" }, - P = { "lua require'telescope'.extensions.projects.projects{}", "Signature Help" }, - } +wk.setup({ + preset = "classic", + delay = 0, + triggers = { + { "", mode = "nxso" }, + { " ", mode = "n" }, -- literal space character + }, }) -wk.register( - { - ["/"] = { "(comment_toggle_linewise_visual)", "Comment toggle linewise (visual)" }, - }, - { - mode = "v", -- VISUAL mode - prefix = "", - buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings - silent = true, -- use `silent` when creating keymaps - noremap = true, -- use `noremap` when creating keymaps - nowait = true, -- use `nowait` when creating keymaps - } -) +wk.add({ + -- Single key mappings + { ";", "Alpha", desc = "Dashboard" }, + { "w", "w!", desc = "Save" }, + { "q", smart_quit, desc = "Quit" }, + { "/", "(comment_toggle_linewise_current)", desc = "Comment toggle current line" }, + { "c", "BufferKill", desc = "Close Buffer" }, + { "f", find_project_files, desc = "Find File" }, + { "h", "nohlsearch", desc = "No Highlight" }, + { "t", "TodoTelescope keywords=TODO,FIX", desc = "Find TODO,FIX" }, + + -- Buffers group + { "b", group = "Buffers" }, + { "bj", "BufferLinePick", desc = "Jump" }, + { "bf", "Telescope buffers", desc = "Find" }, + { "bb", "BufferLineCyclePrev", desc = "Previous" }, + { "bn", "BufferLineCycleNext", desc = "Next" }, + { "be", "BufferLinePickClose", desc = "Pick which buffer to close" }, + { "bh", "BufferLineCloseLeft", desc = "Close all to the left" }, + { "bl", "BufferLineCloseRight", desc = "Close all to the right" }, + { "bD", "BufferLineSortByDirectory", desc = "Sort by directory" }, + { "bL", "BufferLineSortByExtension", desc = "Sort by language" }, + + -- Git group + { "g", group = "Git" }, + { "gg", Lazygit_toggle, desc = "Lazygit" }, + { "gj", "lua require 'gitsigns'.next_hunk({navigation_message = false})", desc = "Next Hunk" }, + { "gk", "lua require 'gitsigns'.prev_hunk({navigation_message = false})", desc = "Prev Hunk" }, + { "gl", "lua require 'gitsigns'.blame_line()", desc = "Blame" }, + { "gp", "lua require 'gitsigns'.preview_hunk()", desc = "Preview Hunk" }, + { "gr", "lua require 'gitsigns'.reset_hunk()", desc = "Reset Hunk" }, + { "gR", "lua require 'gitsigns'.reset_buffer()", desc = "Reset Buffer" }, + { "gs", "lua require 'gitsigns'.stage_hunk()", desc = "Stage Hunk" }, + { "gu", "lua require 'gitsigns'.undo_stage_hunk()", desc = "Undo Stage Hunk" }, + { "go", "Telescope git_status", desc = "Open changed file" }, + { "gb", "Telescope git_branches", desc = "Checkout branch" }, + { "gc", "Telescope git_commits", desc = "Checkout commit" }, + { "gC", "Telescope git_bcommits", desc = "Checkout commit(for current file)" }, + { "gd", "Gitsigns diffthis HEAD", desc = "Git Diff" }, + + -- LSP group + { "l", group = "LSP" }, + { "la", "lua vim.lsp.buf.code_action()", desc = "Code Action" }, + { "ld", "Telescope diagnostics bufnr=0 theme=get_ivy", desc = "Buffer Diagnostics" }, + { "lw", "Telescope diagnostics", desc = "Diagnostics" }, + { "li", "LspInfo", desc = "Info" }, + { "lI", "Mason", desc = "Mason Info" }, + { "lj", vim.diagnostic.goto_next, desc = "Next Diagnostic" }, + { "lk", vim.diagnostic.goto_prev, desc = "Prev Diagnostic" }, + { "ll", vim.lsp.codelens.run, desc = "CodeLens Action" }, + { "lq", vim.diagnostic.setloclist, desc = "Quickfix" }, + { "lr", vim.lsp.buf.rename, desc = "Rename" }, + { "ls", "Telescope lsp_document_symbols", desc = "Document Symbols" }, + { "lS", "Telescope lsp_dynamic_workspace_symbols", desc = "Workspace Symbols" }, + { "le", "Telescope quickfix", desc = "Telescope Quickfix" }, + + -- Direct LSP shortcuts + { "a", "lua require('telescope.builtin').lsp_code_actions()", desc = "Code Actions" }, + { "d", "lua require('telescope.builtin').lsp_document_diagnostics()", desc = "LSP Diagnostics" }, + { "k", "lua vim.lsp.buf.signature_help()", desc = "Signature Help" }, + { "P", "lua require'telescope'.extensions.projects.projects{}", desc = "Projects" }, + + -- Visual mode mappings + { "/", "(comment_toggle_linewise_visual)", desc = "Comment toggle linewise (visual)", mode = "v" }, +}) diff --git a/hosts/nb/modules/development/nvim/default.nix b/hosts/nb/modules/development/nvim/default.nix index 17f7d5c..9352643 100644 --- a/hosts/nb/modules/development/nvim/default.nix +++ b/hosts/nb/modules/development/nvim/default.nix @@ -13,7 +13,7 @@ in environment.systemPackages = with pkgs; [ nodePackages.typescript-language-server - sumneko-lua-language-server + lua-language-server nest nodePackages.intelephense nodePackages.vscode-langservers-extracted @@ -105,9 +105,9 @@ in "sops" ]); in '' - lua << EOF - ${luaConfig} - EOF +lua << EOF +${luaConfig} +EOF ''; }; extraLuaPackages = luaPackages: [ luaPackages.lyaml ]; diff --git a/hosts/nb/users/default.nix b/hosts/nb/users/default.nix index 8b0d8cb..07bddc1 100644 --- a/hosts/nb/users/default.nix +++ b/hosts/nb/users/default.nix @@ -1,6 +1,6 @@ { config, pkgs, ... }: let - home-manager = builtins.fetchTarball "https://github.com/nix-community/home-manager/archive/release-24.11.tar.gz"; + home-manager = builtins.fetchTarball "https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz"; in diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index 4a330f2..b97ae50 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -135,11 +135,11 @@ let { name = "q"; value = "{searchTerms}"; } ]; }]; - iconUpdateURL = "https://perplexity.ai/favicon.ico"; + icon = "https://perplexity.ai/favicon.ico"; definedAliases = [ "@perplexity" ]; }; - "Google".metaData.hidden = true; - "Bing".metaData.hidden = true; + "google".metaData.hidden = true; + "bing".metaData.hidden = true; }; }; @@ -301,26 +301,23 @@ in programs.git = { enable = true; lfs.enable = true; - package = pkgs.gitAndTools.gitFull; - userName = "Dominik Polakovics"; - userEmail = "dominik.polakovics@cloonar.com"; + package = pkgs.gitFull; # signing = { # key = "dominik.polakovics@cloonar.com"; # signByDefault = false; # }; - iniContent = { + settings = { + user.name = "Dominik Polakovics"; + user.email = "dominik.polakovics@cloonar.com"; # Branch with most recent change comes first branch.sort = "-committerdate"; # Remember and auto-resolve merge conflicts # https://git-scm.com/book/en/v2/Git-Tools-Rerere rerere.enabled = true; - }; - extraConfig = { - "url.gitea@git.cloonar.com:" = { + "url \"gitea@git.cloonar.com:\"" = { insteadOf = "https://git.cloonar.com/"; }; }; - }; programs.thunderbird = { @@ -525,7 +522,7 @@ in settings = firefoxSettings; # userChrome = firefoxUserChrome; search = firefoxSearchSettings; - extensions = firefoxExtensions; + extensions.packages = firefoxExtensions; }; social = { id = 1; @@ -560,7 +557,7 @@ in id = 3; }; }; - extensions = firefoxExtensions; + extensions.packages = firefoxExtensions; }; }; }; From 209bafd70f42cf9018b0618b30dc7098a36b8d80 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 16:17:28 +0100 Subject: [PATCH 02/16] feat: test-configuration script get real errors --- scripts/test-configuration | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/test-configuration b/scripts/test-configuration index 6c6dcd1..b0d2ed5 100755 --- a/scripts/test-configuration +++ b/scripts/test-configuration @@ -19,9 +19,9 @@ fi HOSTNAME="$1" -# Check if 'nixos-rebuild' command is available -if ! command -v nixos-rebuild > /dev/null; then - echo "ERROR: 'nixos-rebuild' command not found. Please ensure it is installed and in your PATH." >&2 +# Check if 'nix-instantiate' command is available +if ! command -v nix-instantiate > /dev/null; then + echo "ERROR: 'nix-instantiate' command not found. Please ensure Nix is installed and in your PATH." >&2 exit 1 fi @@ -38,27 +38,42 @@ if [ ! -f "$CONFIG_PATH" ]; then exit 1 fi +# Check for host-specific channel file +CHANNEL_PATH="$SCRIPT_DIR/../hosts/$HOSTNAME/channel" +CHANNEL_OPT="" + +if [ -f "$CHANNEL_PATH" ]; then + CHANNEL_URL=$(cat "$CHANNEL_PATH") + # Append /nixexprs.tar.xz to get the actual tarball URL + TARBALL_URL="${CHANNEL_URL}/nixexprs.tar.xz" + echo "INFO: Using channel '$TARBALL_URL' from '$CHANNEL_PATH'." + CHANNEL_OPT="-I nixpkgs=$TARBALL_URL" +else + echo "WARNING: No channel file found at '$CHANNEL_PATH'. Using system default." >&2 +fi + echo "INFO: Attempting dry-build for host '$HOSTNAME' using configuration '$CONFIG_PATH'..." if [ "$VERBOSE" = true ]; then echo "INFO: Verbose mode enabled, --show-trace will be used." fi -# Execute nixos-rebuild dry-build -# Store the output and error streams, and the exit code -NIX_OUTPUT_ERR=$(nixos-rebuild dry-build $SHOW_TRACE_OPT -I nixos-config="$CONFIG_PATH" --show-trace 2>&1) +# Execute nix-instantiate to evaluate the configuration +# nix-instantiate fetches fresh tarballs and catches all evaluation errors +# unlike nixos-rebuild which may use cached results +NIX_OUTPUT_ERR=$(nix-instantiate $SHOW_TRACE_OPT $CHANNEL_OPT -I nixos-config="$CONFIG_PATH" '' -A system 2>&1) NIX_EXIT_STATUS=$? # Check the exit status if [ "$NIX_EXIT_STATUS" -eq 0 ]; then echo "INFO: Dry-build for host '$HOSTNAME' completed successfully." if [ "$VERBOSE" = true ]; then - echo "Output from nixos-rebuild:" + echo "Output from nix-instantiate:" echo "$NIX_OUTPUT_ERR" fi exit 0 else - echo "ERROR: Dry-build for host '$HOSTNAME' failed. 'nixos-rebuild' exited with status $NIX_EXIT_STATUS." >&2 - echo "Output from nixos-rebuild:" >&2 + echo "ERROR: Dry-build for host '$HOSTNAME' failed. 'nix-instantiate' exited with status $NIX_EXIT_STATUS." >&2 + echo "Output from nix-instantiate:" >&2 echo "$NIX_OUTPUT_ERR" >&2 exit "$NIX_EXIT_STATUS" fi From 6e8f530537ebd8f9a69ae3a46d9c45b82a239a70 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 16:17:45 +0100 Subject: [PATCH 03/16] feat: amz add cron job --- hosts/amzebs-01/configuration.nix | 1 + hosts/amzebs-01/modules/laravel-scheduler.nix | 51 +++++++++++++++++++ hosts/amzebs-01/modules/postfix.nix | 11 ++-- 3 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 hosts/amzebs-01/modules/laravel-scheduler.nix diff --git a/hosts/amzebs-01/configuration.nix b/hosts/amzebs-01/configuration.nix index 11336ed..c90cb23 100644 --- a/hosts/amzebs-01/configuration.nix +++ b/hosts/amzebs-01/configuration.nix @@ -7,6 +7,7 @@ ./modules/mysql.nix ./modules/web/stack.nix ./modules/laravel-storage.nix + ./modules/laravel-scheduler.nix ./modules/blackbox-exporter.nix ./modules/postfix.nix ./modules/rspamd.nix diff --git a/hosts/amzebs-01/modules/laravel-scheduler.nix b/hosts/amzebs-01/modules/laravel-scheduler.nix new file mode 100644 index 0000000..cbd0eca --- /dev/null +++ b/hosts/amzebs-01/modules/laravel-scheduler.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, ... }: + +# Daily scheduled Laravel artisan jobs +# Runs artisan finish:reports at 01:00 for production and staging APIs + +let + php = pkgs.php82; + + sites = [ + { + domain = "api.ebs.amz.at"; + user = "api_ebs_amz_at"; + } + { + domain = "api.stage.ebs.amz.at"; + user = "api_stage_ebs_amz_at"; + } + ]; + + mkArtisanService = site: { + name = "artisan-finish-reports-${site.domain}"; + value = { + description = "Laravel artisan finish:reports for ${site.domain}"; + after = [ "network.target" "mysql.service" "phpfpm-${site.domain}.service" ]; + serviceConfig = { + Type = "oneshot"; + User = site.user; + Group = "nginx"; + WorkingDirectory = "/var/www/${site.domain}"; + ExecStart = "${php}/bin/php artisan finish:reports"; + }; + }; + }; + + mkArtisanTimer = site: { + name = "artisan-finish-reports-${site.domain}"; + value = { + description = "Daily timer for artisan finish:reports on ${site.domain}"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "*-*-* 01:00:00"; + Persistent = true; + }; + }; + }; + +in +{ + systemd.services = builtins.listToAttrs (map mkArtisanService sites); + systemd.timers = builtins.listToAttrs (map mkArtisanTimer sites); +} diff --git a/hosts/amzebs-01/modules/postfix.nix b/hosts/amzebs-01/modules/postfix.nix index 6c21698..4ce04df 100644 --- a/hosts/amzebs-01/modules/postfix.nix +++ b/hosts/amzebs-01/modules/postfix.nix @@ -3,15 +3,16 @@ , config , ... }: -{ - # Header checks file for validating email headers - environment.etc."postfix/header_checks".text = '' +let + headerChecksFile = pkgs.writeText "header_checks" '' # Warn about missing critical headers (but don't reject from localhost) # These help identify misconfigured applications /^$/ WARN Missing headers detected ''; - +in +{ services.postfix = { + mapFiles."header_checks" = headerChecksFile; enable = true; hostname = "amzebs-01.amz.at"; domain = "amz.at"; @@ -42,7 +43,7 @@ # Ensure proper header handling # Reject mail that's missing critical headers - header_checks = "regexp:/etc/postfix/header_checks"; + header_checks = "regexp:/var/lib/postfix/conf/header_checks"; # Rate limiting to prevent spam-like behavior # Allow reasonable sending rates for applications From 170becceb0ece610b65224e6764602e830dd7379 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 22:05:24 +0100 Subject: [PATCH 04/16] fix: nvim --- .../development/nvim/config/project.lua | 44 ++++--------------- .../modules/development/nvim/config/utils.lua | 6 +-- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/hosts/nb/modules/development/nvim/config/project.lua b/hosts/nb/modules/development/nvim/config/project.lua index 42cd790..f096f26 100644 --- a/hosts/nb/modules/development/nvim/config/project.lua +++ b/hosts/nb/modules/development/nvim/config/project.lua @@ -1,41 +1,13 @@ -config = { - ---@usage set to false to disable project.nvim. - --- This is on by default since it's currently the expected behavior. - active = true, - - on_config_done = nil, - - ---@usage set to true to disable setting the current-woriking directory - --- Manual mode doesn't automatically change your root directory, so you have - --- the option to manually do so using `:ProjectRoot` command. - manual_mode = false, - - ---@usage Methods of detecting the root directory - --- Allowed values: **"lsp"** uses the native neovim lsp - --- **"pattern"** uses vim-rooter like glob pattern matching. Here - --- order matters: if one is not detected, the other is used as fallback. You - --- can also delete or rearangne the detection methods. - -- detection_methods = { "lsp", "pattern" }, -- NOTE: lsp detection will get annoying with multiple langs in one project - detection_methods = { "pattern" }, - - ---@usage patterns used to detect root dir, when **"pattern"** is in detection_methods - patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json", "pom.xml" }, - - ---@ Show hidden files in telescope when searching for files in a project - show_hidden = false, - - ---@usage When set to false, you will get a message when project.nvim changes your directory. - -- When set to false, you will get a message when project.nvim changes your directory. - silent_chdir = true, - - ---@usage list of lsp client names to ignore when using **lsp** detection. eg: { "efm", ... } - ignore_lsp = {}, -} - local status_ok, project = pcall(require, "project") if not status_ok then return end -project.setup(config) - +project.setup({ + use_lsp = false, -- Use pattern matching only (equivalent to old detection_methods = { "pattern" }) + manual_mode = false, + patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json", "pom.xml" }, + show_hidden = false, + silent_chdir = true, + ignore_lsp = {}, +}) diff --git a/hosts/nb/modules/development/nvim/config/utils.lua b/hosts/nb/modules/development/nvim/config/utils.lua index b734ca1..a541d5e 100644 --- a/hosts/nb/modules/development/nvim/config/utils.lua +++ b/hosts/nb/modules/development/nvim/config/utils.lua @@ -1,7 +1,7 @@ --- none-ls -local status_ok_nls, none_ls_module = pcall(require, "none-ls") +-- none-ls (module is still named "null-ls" for backward compatibility) +local status_ok_nls, none_ls_module = pcall(require, "null-ls") if not status_ok_nls then - vim.notify("none-ls plugin not found or failed to load. Check Nix config and plugin paths.", vim.log.levels.WARN) + vim.notify("null-ls plugin not found or failed to load. Check Nix config and plugin paths.", vim.log.levels.WARN) else local nb = none_ls_module.builtins none_ls_module.setup({ From 28a7bed3b99484e936125ba7a27c229f4f15bd1c Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 22:24:57 +0100 Subject: [PATCH 05/16] feat(mail): update to 25.11 with TLS hardening - Upgrade NixOS channel from 25.05 to 25.11 - Fix dovecot systemd service rename (dovecot2 -> dovecot) - Convert postfix numeric settings to integers (25.11 requirement) - Remove insecure 512-bit DH params, fix 2048-bit DH params - Update postfix ciphers to modern ECDHE/DHE+AESGCM/CHACHA20 - Require TLS 1.2 minimum for OpenLDAP - Remove weak ciphers (3DES, RC4, aNULL) from OpenLDAP --- hosts/mail/channel | 2 +- hosts/mail/modules/dovecot.nix | 6 +++--- hosts/mail/modules/openldap.nix | 4 ++-- hosts/mail/modules/postfix.nix | 14 ++++++-------- hosts/mail/modules/rspamd.nix | 2 +- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/hosts/mail/channel b/hosts/mail/channel index 93f5df5..57f31e7 100644 --- a/hosts/mail/channel +++ b/hosts/mail/channel @@ -1 +1 @@ -https://channels.nixos.org/nixos-25.05 +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/mail/modules/dovecot.nix b/hosts/mail/modules/dovecot.nix index b7f25bd..252e015 100644 --- a/hosts/mail/modules/dovecot.nix +++ b/hosts/mail/modules/dovecot.nix @@ -240,11 +240,11 @@ in sops.secrets.dovecot-ldap-password = { }; - systemd.services.dovecot2.preStart = '' + systemd.services.dovecot.preStart = '' sed -e "s/@ldap-password@/$(cat ${config.sops.secrets.dovecot-ldap-password.path})/" ${ldapConfig} > /run/dovecot2/ldap.conf ''; - systemd.services.dovecot2 = { + systemd.services.dovecot = { wants = [ "acme-imap.${domain}.service" ]; after = [ "acme-imap.${domain}.service" ]; }; @@ -257,7 +257,7 @@ in "imap-test.${domain}" "imap-02.${domain}" ]; - postRun = "systemctl --no-block restart dovecot2.service"; + postRun = "systemctl --no-block restart dovecot.service"; }; networking.firewall.allowedTCPPorts = [ diff --git a/hosts/mail/modules/openldap.nix b/hosts/mail/modules/openldap.nix index 81e63e1..2a22cbb 100644 --- a/hosts/mail/modules/openldap.nix +++ b/hosts/mail/modules/openldap.nix @@ -17,10 +17,10 @@ in { olcTLSCACertificateFile = "/var/lib/acme/ldap.${domain}/full.pem"; olcTLSCertificateFile = "/var/lib/acme/ldap.${domain}/cert.pem"; olcTLSCertificateKeyFile = "/var/lib/acme/ldap.${domain}/key.pem"; - olcTLSCipherSuite = "HIGH:MEDIUM:+3DES:+RC4:+aNULL"; + olcTLSCipherSuite = "HIGH:!aNULL:!MD5:!3DES:!RC4"; olcTLSCRLCheck = "none"; olcTLSVerifyClient = "never"; - olcTLSProtocolMin = "3.1"; + olcTLSProtocolMin = "3.3"; olcSecurity = "tls=1"; }; diff --git a/hosts/mail/modules/postfix.nix b/hosts/mail/modules/postfix.nix index 9226b99..ac71d35 100644 --- a/hosts/mail/modules/postfix.nix +++ b/hosts/mail/modules/postfix.nix @@ -128,16 +128,16 @@ in compatibility_level = "2"; # bigger attachement size - mailbox_size_limit = "202400000"; - message_size_limit = "51200000"; + mailbox_size_limit = 202400000; + message_size_limit = 51200000; smtpd_helo_required = "yes"; smtpd_delay_reject = "yes"; strict_rfc821_envelopes = "yes"; # send Limit smtpd_error_sleep_time = "1s"; - smtpd_soft_error_limit = "10"; - smtpd_hard_error_limit = "20"; + smtpd_soft_error_limit = 10; + smtpd_hard_error_limit = 20; smtpd_use_tls = "yes"; smtp_tls_note_starttls_offer = "yes"; @@ -151,14 +151,13 @@ in smtpd_tls_key_file = "/var/lib/acme/mail.cloonar.com/key.pem"; smtpd_tls_CAfile = "/var/lib/acme/mail.cloonar.com/fullchain.pem"; - smtpd_tls_dh512_param_file = config.security.dhparams.params.postfix512.path; smtpd_tls_dh1024_param_file = config.security.dhparams.params.postfix2048.path; smtpd_tls_session_cache_database = ''btree:''${data_directory}/smtpd_scache''; smtpd_tls_mandatory_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1"; smtpd_tls_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1"; smtpd_tls_mandatory_ciphers = "medium"; - tls_medium_cipherlist = "AES128+EECDH:AES128+EDH"; + tls_medium_cipherlist = "ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20:DHE+CHACHA20"; # authentication smtpd_sasl_auth_enable = "yes"; @@ -225,8 +224,7 @@ in security.dhparams = { enable = true; - params.postfix512.bits = 512; - params.postfix2048.bits = 1024; + params.postfix2048.bits = 2048; }; security.acme.certs."mail.${domain}" = { diff --git a/hosts/mail/modules/rspamd.nix b/hosts/mail/modules/rspamd.nix index abab1e2..becc93b 100644 --- a/hosts/mail/modules/rspamd.nix +++ b/hosts/mail/modules/rspamd.nix @@ -119,7 +119,7 @@ in # systemd.services.rspamd.serviceConfig.SupplementaryGroups = [ "redis-rspamd" ]; - systemd.services.dovecot2.preStart = '' + systemd.services.dovecot.preStart = '' mkdir -p /var/lib/dovecot/sieve/ for i in ${sieve-spam-filter}/share/sieve-rspamd-filter/*.sieve; do dest="/var/lib/dovecot/sieve/$(basename $i)" From 4770d671c0572d57e553928aa18bcabce1d3a170 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 22:25:08 +0100 Subject: [PATCH 06/16] docs: add commit footer convention to CLAUDE.md --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index d745136..0c96d48 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,6 +87,6 @@ utils/pkgs// ## Conventions - Nix files: two-space indentation, lower kebab-case naming -- Commits: Conventional Commits format (`fix:`, `feat:`, `chore:`), scope by host when relevant (`fix(mail):`) +- Commits: Conventional Commits format (`fix:`, `feat:`, `chore:`), scope by host when relevant (`fix(mail):`). Do not add "Generated with Claude Code" or "Co-Authored-By: Claude" footers. - Modules import via explicit paths, not wildcards - Comments explain non-obvious decisions (open ports, unusual service options) From 1b27bafd41534121d0ae51eb2ed5e76022a0e944 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 22:48:02 +0100 Subject: [PATCH 07/16] feat(web-arm): update to 25.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Migrate logind.extraConfig to logind.settings.Login - Update dovecot alert for service rename (dovecot2 → dovecot) - Fix sa-core buildGoModule env attribute for CGO_ENABLED --- hosts/web-arm/channel | 2 +- hosts/web-arm/configuration.nix | 2 +- hosts/web-arm/modules/grafana/alerting/service/dovecot_down.nix | 2 +- hosts/web-arm/pkgs/sa-core.nix | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hosts/web-arm/channel b/hosts/web-arm/channel index 93f5df5..57f31e7 100644 --- a/hosts/web-arm/channel +++ b/hosts/web-arm/channel @@ -1 +1 @@ -https://channels.nixos.org/nixos-25.05 +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/web-arm/configuration.nix b/hosts/web-arm/configuration.nix index 8a6eac0..21ebfff 100644 --- a/hosts/web-arm/configuration.nix +++ b/hosts/web-arm/configuration.nix @@ -63,7 +63,7 @@ time.timeZone = "Europe/Vienna"; - services.logind.extraConfig = "RuntimeDirectorySize=2G"; + services.logind.settings.Login.RuntimeDirectorySize = "2G"; sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; sops.defaultSopsFile = ./secrets.yaml; diff --git a/hosts/web-arm/modules/grafana/alerting/service/dovecot_down.nix b/hosts/web-arm/modules/grafana/alerting/service/dovecot_down.nix index 2a03cb9..18645fd 100644 --- a/hosts/web-arm/modules/grafana/alerting/service/dovecot_down.nix +++ b/hosts/web-arm/modules/grafana/alerting/service/dovecot_down.nix @@ -15,7 +15,7 @@ datasourceUid = "vm-datasource-uid"; model = { editorMode = "code"; - expr = "node_systemd_unit_state{state=\"active\", name=\"dovecot2.service\"} OR on() vector(0)"; + expr = "node_systemd_unit_state{state=\"active\", name=\"dovecot.service\"} OR on() vector(0)"; hide = false; intervalMs = 1000; legendFormat = "__auto"; diff --git a/hosts/web-arm/pkgs/sa-core.nix b/hosts/web-arm/pkgs/sa-core.nix index d09c48d..e025ae4 100644 --- a/hosts/web-arm/pkgs/sa-core.nix +++ b/hosts/web-arm/pkgs/sa-core.nix @@ -17,7 +17,7 @@ buildGoModule rec { subPackages = [ "." ]; # Optional tuning - CGO_ENABLED = 0; + env.CGO_ENABLED = "0"; ldflags = [ "-s" "-w" ]; doCheck = false; From 840f99a7e924f54e0de4ccd95cdde2263a57c262 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 1 Dec 2025 23:02:35 +0100 Subject: [PATCH 08/16] feat(amzebs-01): update to 25.11 --- hosts/amzebs-01/channel | 1 + hosts/amzebs-01/configuration.nix | 3 ++- hosts/amzebs-01/modules/postfix.nix | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 hosts/amzebs-01/channel diff --git a/hosts/amzebs-01/channel b/hosts/amzebs-01/channel new file mode 100644 index 0000000..57f31e7 --- /dev/null +++ b/hosts/amzebs-01/channel @@ -0,0 +1 @@ +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/amzebs-01/configuration.nix b/hosts/amzebs-01/configuration.nix index c90cb23..a4b9faf 100644 --- a/hosts/amzebs-01/configuration.nix +++ b/hosts/amzebs-01/configuration.nix @@ -3,6 +3,7 @@ ./utils/bento.nix ./utils/modules/sops.nix ./utils/modules/nginx.nix + ./utils/modules/set-nix-channel.nix ./modules/mysql.nix ./modules/web/stack.nix @@ -76,5 +77,5 @@ ''; }; - system.stateVersion = "23.11"; + system.stateVersion = "25.11"; } diff --git a/hosts/amzebs-01/modules/postfix.nix b/hosts/amzebs-01/modules/postfix.nix index 4ce04df..1c97c57 100644 --- a/hosts/amzebs-01/modules/postfix.nix +++ b/hosts/amzebs-01/modules/postfix.nix @@ -35,11 +35,11 @@ in compatibility_level = "2"; # Only accept mail from localhost - mynetworks = "127.0.0.0/8 [::1]/128"; + mynetworks = [ "127.0.0.0/8" "[::1]/128" ]; # Larger message size limits for attachments - mailbox_size_limit = "202400000"; # ~200MB - message_size_limit = "51200000"; # ~50MB + mailbox_size_limit = 202400000; # ~200MB + message_size_limit = 51200000; # ~50MB # Ensure proper header handling # Reject mail that's missing critical headers @@ -47,8 +47,8 @@ in # Rate limiting to prevent spam-like behavior # Allow reasonable sending rates for applications - smtpd_client_message_rate_limit = "100"; - smtpd_client_recipient_rate_limit = "200"; + smtpd_client_message_rate_limit = 100; + smtpd_client_recipient_rate_limit = 200; # Milter configuration is handled automatically by rspamd.postfix.enable }; From 8424d771f6bcd0045bed8badbeeac62f47014d4d Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Tue, 2 Dec 2025 08:34:57 +0100 Subject: [PATCH 09/16] feat(fw): update to 25.11 --- hosts/fw/channel | 2 +- hosts/fw/configuration.nix | 3 ++- hosts/fw/modules/web/n8n.nix | 22 ++++++++++------------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/hosts/fw/channel b/hosts/fw/channel index 93f5df5..57f31e7 100644 --- a/hosts/fw/channel +++ b/hosts/fw/channel @@ -1 +1 @@ -https://channels.nixos.org/nixos-25.05 +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index 6a41e33..e3f8115 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -10,6 +10,7 @@ ./utils/modules/victoriametrics ./utils/modules/promtail ./utils/modules/borgbackup.nix + ./utils/modules/set-nix-channel.nix # fw ./modules/network-prefix.nix @@ -103,7 +104,7 @@ time.timeZone = "Europe/Vienna"; - services.logind.extraConfig = "RuntimeDirectorySize=2G"; + services.logind.settings.Login.RuntimeDirectorySize = "2G"; sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; sops.defaultSopsFile = ./secrets.yaml; diff --git a/hosts/fw/modules/web/n8n.nix b/hosts/fw/modules/web/n8n.nix index e1bd713..85f339e 100644 --- a/hosts/fw/modules/web/n8n.nix +++ b/hosts/fw/modules/web/n8n.nix @@ -19,21 +19,19 @@ }; # n8n service configuration - services.n8n = { - enable = true; - settings = { - database.type = "postgresdb"; - database.postgresdb.host = "/run/postgresql"; - database.postgresdb.database = "n8n"; - database.postgresdb.user = "n8n"; - executions.pruneData = true; - executions.pruneDataMaxAge = 168; # 7 days - }; - }; + services.n8n.enable = true; - # Configure git integration via environment variables + # Configure n8n via environment variables systemd.services.n8n = { environment = lib.mkForce { + # Database configuration (migrated from services.n8n.settings) + DB_TYPE = "postgresdb"; + DB_POSTGRESDB_HOST = "/run/postgresql"; + DB_POSTGRESDB_DATABASE = "n8n"; + DB_POSTGRESDB_USER = "n8n"; + EXECUTIONS_DATA_PRUNE = "true"; + EXECUTIONS_DATA_MAX_AGE = "168"; # 7 days + # Other settings N8N_ENCRYPTION_KEY = ""; # Will be set via environmentFile N8N_VERSION_NOTIFICATIONS_ENABLED = "false"; N8N_DIAGNOSTICS_ENABLED = "false"; From 8bf4b185a109e58aad9ea6c729f31d5adf0fe8a0 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Tue, 2 Dec 2025 11:27:52 +0100 Subject: [PATCH 10/16] feat(nas): update to 25.11, add software, add storage plan --- hosts/nas/STORAGE.md | 60 +++++++++++++++++++++++++++++++++++++ hosts/nas/channel | 1 + hosts/nas/configuration.nix | 7 +++++ 3 files changed, 68 insertions(+) create mode 100644 hosts/nas/STORAGE.md create mode 100644 hosts/nas/channel diff --git a/hosts/nas/STORAGE.md b/hosts/nas/STORAGE.md new file mode 100644 index 0000000..3268032 --- /dev/null +++ b/hosts/nas/STORAGE.md @@ -0,0 +1,60 @@ +# NAS Storage Notes + +## Current Issue: XFS Metadata Overhead + +The XFS filesystem on `/var/lib/multimedia` uses ~100GB more than the actual file data due to metadata overhead. + +### Root Cause + +The filesystem was created with advanced features enabled: + +``` +rmapbt=1 # Reverse mapping btree - tracks block ownership +reflink=1 # Copy-on-write support +``` + +These features add metadata that scales with **filesystem size**, not file count. On a 5TB filesystem with 700GB of data, this results in ~100GB (~2%) overhead. + +### Diagnostic Commands + +```bash +# Compare file data vs filesystem usage +du -sh /var/lib/multimedia # Actual file data +df -h /var/lib/multimedia # Filesystem reports + +# Check XFS features +xfs_info /var/lib/multimedia + +# Verify block allocation +xfs_db -r -c "freesp -s" /dev/mapper/vg--data-lv--multimedia +``` + +## Recommendation: LVM + ext4 + +For media storage (write-once, read-many), ext4 with minimal reserved space offers the lowest overhead: + +```bash +# Create filesystem with 0% reserved blocks +mkfs.ext4 -m 0 /dev/vg/lv + +# Or adjust existing ext4 +tune2fs -m 0 /dev/vg/lv +``` + +### Why ext4 over XFS for this use case + +| Consideration | ext4 | XFS (current) | +|---------------|------|---------------| +| Reserved space | 0% with `-m 0` | N/A | +| Metadata overhead | ~0.5% | ~2% (with rmapbt) | +| Shrink support | Yes | No | +| Performance for 4K stream | Identical | Identical | + +A single 4K remux stream requires ~12 MB/s. Any filesystem handles this trivially. + +## Migration Path + +1. Backup data from XFS volumes +2. Recreate LVs with ext4 (`mkfs.ext4 -m 0`) +3. Restore data +4. Update `/etc/fstab` or NixOS `fileSystems` config diff --git a/hosts/nas/channel b/hosts/nas/channel new file mode 100644 index 0000000..57f31e7 --- /dev/null +++ b/hosts/nas/channel @@ -0,0 +1 @@ +https://channels.nixos.org/nixos-25.11 diff --git a/hosts/nas/configuration.nix b/hosts/nas/configuration.nix index 5e594c2..7ee98c9 100644 --- a/hosts/nas/configuration.nix +++ b/hosts/nas/configuration.nix @@ -9,6 +9,7 @@ in { "${impermanence}/nixos.nix" ./utils/bento.nix ./utils/modules/sops.nix + ./utils/modules/set-nix-channel.nix ./utils/modules/victoriametrics ./utils/modules/promtail @@ -76,6 +77,12 @@ in { ]; }; + # System packages + environment.systemPackages = with pkgs; [ + vim + screen + ]; + # Nix settings nix = { settings = { From f97c9185c10833aab4c9d746d470ed91adace007 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Tue, 2 Dec 2025 11:28:05 +0100 Subject: [PATCH 11/16] docs: update claude --- CLAUDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0c96d48..785e93e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,7 +40,7 @@ Each host in `hosts//` contains: - `fleet.nix` → symlink to root `fleet.nix` (SFTP user provisioning) - `utils/` → symlink to root `utils/` (shared modules) -Current hosts: `fw` (firewall/router), `nb` (notebook), `web-arm`, `mail`, `amzebs-01` +Current hosts: `fw` (firewall/router), `nb` (notebook), `web-arm`, `mail`, `amzebs-01`, `nas` ### Shared Components (`utils/`) - `modules/` - Reusable NixOS modules (nginx, sops, borgbackup, lego, promtail, etc.) @@ -90,3 +90,4 @@ utils/pkgs// - Commits: Conventional Commits format (`fix:`, `feat:`, `chore:`), scope by host when relevant (`fix(mail):`). Do not add "Generated with Claude Code" or "Co-Authored-By: Claude" footers. - Modules import via explicit paths, not wildcards - Comments explain non-obvious decisions (open ports, unusual service options) +- **Never update `system.stateVersion`** - it should remain at the original installation version. To upgrade NixOS, update the `channel` file instead. From 18a8fde66e0bc92f00bcc47f2328ab41a4843df6 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 4 Dec 2025 11:38:22 +0100 Subject: [PATCH 12/16] feat: add uv for mcp --- hosts/nb/modules/development/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/hosts/nb/modules/development/default.nix b/hosts/nb/modules/development/default.nix index 969fff5..244f929 100644 --- a/hosts/nb/modules/development/default.nix +++ b/hosts/nb/modules/development/default.nix @@ -38,6 +38,7 @@ in { rbw sops unzip + uv vim wget wireguard-tools From dd456eab696387996e508d9f2583991fba93fb7b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 4 Dec 2025 11:39:08 +0100 Subject: [PATCH 13/16] feat: update pyload --- utils/overlays/packages.nix | 1 + utils/pkgs/aia-chaser/default.nix | 32 +++++++++++++++++++ utils/pkgs/pyload-ng/default.nix | 5 +-- .../pkgs/pyload-ng/update.sh | 15 +++++---- 4 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 utils/pkgs/aia-chaser/default.nix rename scripts/update-pyload-hash => utils/pkgs/pyload-ng/update.sh (89%) diff --git a/utils/overlays/packages.nix b/utils/overlays/packages.nix index 5eced50..e3bfebd 100644 --- a/utils/overlays/packages.nix +++ b/utils/overlays/packages.nix @@ -10,6 +10,7 @@ self: super: { # Python packages python3 = super.python3.override { packageOverrides = pself: psuper: { + aia-chaser = pself.callPackage ../pkgs/aia-chaser { }; mini-racer = pself.callPackage ../pkgs/mini-racer.nix { }; }; }; diff --git a/utils/pkgs/aia-chaser/default.nix b/utils/pkgs/aia-chaser/default.nix new file mode 100644 index 0000000..35fece2 --- /dev/null +++ b/utils/pkgs/aia-chaser/default.nix @@ -0,0 +1,32 @@ +{ lib +, buildPythonPackage +, fetchPypi +, cryptography +}: + +buildPythonPackage rec { + pname = "aia-chaser"; + version = "3.3.0"; + format = "wheel"; + + src = fetchPypi { + pname = "aia_chaser"; + inherit version format; + dist = "py3"; + python = "py3"; + hash = "sha256-L0aBV3kfAVI1aJH7VgiiEXzGBSP/HU2zAlahkHeT8hk="; + }; + + dependencies = [ + cryptography + ]; + + pythonImportsCheck = [ "aia_chaser" ]; + + meta = with lib; { + description = "Retrieve missing certificates to complete SSL certificate chains"; + homepage = "https://github.com/dirkjanm/aia-chaser"; + license = licenses.mit; + maintainers = [ ]; + }; +} diff --git a/utils/pkgs/pyload-ng/default.nix b/utils/pkgs/pyload-ng/default.nix index 01003bf..01cd24e 100644 --- a/utils/pkgs/pyload-ng/default.nix +++ b/utils/pkgs/pyload-ng/default.nix @@ -6,8 +6,8 @@ pyload-ng.overridePythonAttrs (oldAttrs: rec { src = fetchFromGitHub { owner = "pyload"; repo = "pyload"; - rev = "3115740a2210fd57b5d050cd0850a0e61ec493ed"; # [DdownloadCom] fix #4537 - hash = "sha256-g1eEeNnr3Axtr+0BJzMcNQomTEX4EsUG1Jxt+huPyoc="; + rev = "71f2700184ee9344dc313d9833ca7a6bb36007db"; # [DdownloadCom] fix #4537 + hash = "sha256-XAa+XbC3kko+zvEMZkPXRoaHAmEFGsNBDxysX+X06Jc="; }; patches = [ @@ -16,6 +16,7 @@ pyload-ng.overridePythonAttrs (oldAttrs: rec { # Add new dependencies required in newer versions propagatedBuildInputs = (oldAttrs.propagatedBuildInputs or []) ++ (with python3Packages; [ + aia-chaser mini-racer packaging pydantic diff --git a/scripts/update-pyload-hash b/utils/pkgs/pyload-ng/update.sh similarity index 89% rename from scripts/update-pyload-hash rename to utils/pkgs/pyload-ng/update.sh index 100ee8b..a780fec 100755 --- a/scripts/update-pyload-hash +++ b/utils/pkgs/pyload-ng/update.sh @@ -7,9 +7,10 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color -# Script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +# Script and repo directories +cd "$(dirname "${BASH_SOURCE[0]}")" +PKG_DIR="$(pwd)" +REPO_ROOT="$(cd ../../.. && pwd)" # Check if commit SHA is provided if [ $# -ne 1 ]; then @@ -34,7 +35,7 @@ fi echo -e "${GREEN}==> Updating pyload-ng to commit: ${COMMIT_SHA}${NC}" # File to update -PKG_FILE="$REPO_ROOT/utils/pkgs/pyload-ng-updated.nix" +PKG_FILE="$PKG_DIR/default.nix" if [ ! -f "$PKG_FILE" ]; then echo -e "${RED}Error: Package file not found: $PKG_FILE${NC}" @@ -53,7 +54,8 @@ echo " ✓ Updated hash in $PKG_FILE" # Step 3: Build package to discover the correct hash echo -e "${YELLOW}Step 3: Building package to discover hash...${NC}" -BUILD_OUTPUT=$(nix-build --impure -E "with import { overlays = [ (import $REPO_ROOT/utils/overlays/packages.nix) ]; }; callPackage $PKG_FILE { }" 2>&1 || true) +cd "$REPO_ROOT" +BUILD_OUTPUT=$(nix-build --impure -E "with import { overlays = [ (import ./utils/overlays/packages.nix) ]; }; callPackage ./utils/pkgs/pyload-ng { }" 2>&1 || true) # Extract hash from error message HASH=$(echo "$BUILD_OUTPUT" | grep -oP '\s+got:\s+\Ksha256-[A-Za-z0-9+/=]+' | head -1) @@ -74,7 +76,7 @@ echo " ✓ Updated hash in $PKG_FILE" # Step 5: Verify the build succeeds echo -e "${YELLOW}Step 5: Verifying build with correct hash...${NC}" -if nix-build --impure -E "with import { overlays = [ (import $REPO_ROOT/utils/overlays/packages.nix) ]; }; callPackage $PKG_FILE { }" > /dev/null 2>&1; then +if nix-build --impure -E "with import { overlays = [ (import ./utils/overlays/packages.nix) ]; }; callPackage ./utils/pkgs/pyload-ng { }" > /dev/null 2>&1; then echo " ✓ Build verification successful" else echo -e "${RED}Error: Build verification failed${NC}" @@ -83,7 +85,6 @@ fi # Step 6: Test configuration for fw host (which uses pyload) echo -e "${YELLOW}Step 6: Testing fw configuration...${NC}" -cd "$REPO_ROOT" if ./scripts/test-configuration fw > /dev/null 2>&1; then echo " ✓ Configuration test passed" else From 57629169701b69f3f7648ac5e0289cb14974a63a Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 4 Dec 2025 11:39:17 +0100 Subject: [PATCH 14/16] feat: add mcp server --- .mcp.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .mcp.json diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..ca8c928 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "nixos": { + "command": "uvx", + "args": ["mcp-nixos"] + } + } +} From 5995612407181bab408b555a51722ec1bd892987 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 4 Dec 2025 12:46:28 +0100 Subject: [PATCH 15/16] fix: amzebs add mysql port --- hosts/amzebs-01/configuration.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/amzebs-01/configuration.nix b/hosts/amzebs-01/configuration.nix index a4b9faf..700cc30 100644 --- a/hosts/amzebs-01/configuration.nix +++ b/hosts/amzebs-01/configuration.nix @@ -69,7 +69,7 @@ networking.firewall = { enable = true; - allowedTCPPorts = [ 22 80 443 ]; + allowedTCPPorts = [ 22 80 443 3306 ]; # Allow MariaDB access only from specific IP extraCommands = '' From ada9db79424e1d9244e80e6b90afe62de01c40cd Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 4 Dec 2025 15:01:47 +0100 Subject: [PATCH 16/16] feat: add disks to nas --- hosts/nas/hardware-configuration.nix | 15 +++++++++------ hosts/nas/modules/disk-monitoring.nix | 8 ++++++-- hosts/nas/modules/power-management.nix | 5 +++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/hosts/nas/hardware-configuration.nix b/hosts/nas/hardware-configuration.nix index 077f5e9..6f82f8b 100644 --- a/hosts/nas/hardware-configuration.nix +++ b/hosts/nas/hardware-configuration.nix @@ -24,12 +24,16 @@ "i915.enable_fbc=1" # Frame buffer compression ]; - # RAID 1 array for data storage + # RAID 1 arrays for data storage boot.swraid = { enable = true; mdadmConf = '' + DEVICE /dev/disk/by-id/nvme-KIOXIA-EXCERIA_PLUS_G3_SSD_7FJKS1MAZ0E7-part1 + DEVICE /dev/disk/by-id/nvme-KIOXIA-EXCERIA_PLUS_G3_SSD_7FJKS1M9Z0E7-part1 DEVICE /dev/disk/by-id/ata-ST18000NM000J-2TV103_ZR52TBSB-part1 DEVICE /dev/disk/by-id/ata-ST18000NM000J-2TV103_ZR52V9QX-part1 + DEVICE /dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_8582A01SF4MJ-part1 + DEVICE /dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_75V2A0H3F4MJ-part1 ''; }; @@ -84,14 +88,13 @@ # LVM volumes on RAID array fileSystems."/var/lib/downloads" = { - device = "/dev/vg-data/lv-downloads"; - fsType = "xfs"; - options = [ "noatime" ]; + device = "/dev/vg-data-fast/downloads"; + fsType = "ext4"; }; fileSystems."/var/lib/multimedia" = { - device = "/dev/vg-data/lv-multimedia"; - fsType = "xfs"; + device = "/dev/vg-data-slow/multimedia"; + fsType = "ext4"; options = [ "noatime" ]; }; diff --git a/hosts/nas/modules/disk-monitoring.nix b/hosts/nas/modules/disk-monitoring.nix index 9316941..2862f44 100644 --- a/hosts/nas/modules/disk-monitoring.nix +++ b/hosts/nas/modules/disk-monitoring.nix @@ -9,6 +9,10 @@ let disks = [ "/dev/disk/by-id/ata-ST18000NM000J-2TV103_ZR52TBSB" "/dev/disk/by-id/ata-ST18000NM000J-2TV103_ZR52V9QX" + "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_8582A01SF4MJ" + "/dev/disk/by-id/ata-TOSHIBA_MG10ACA20TE_75V2A0H3F4MJ" + "/dev/disk/by-id/nvme-KIOXIA-EXCERIA_PLUS_G3_SSD_7FJKS1MAZ0E7" + "/dev/disk/by-id/nvme-KIOXIA-EXCERIA_PLUS_G3_SSD_7FJKS1M9Z0E7" ]; textfileDir = "/var/lib/prometheus-node-exporter"; @@ -44,8 +48,8 @@ let device=$(readlink -f "$disk") short_name=$(basename "$device") - # Extract serial from disk ID for labels - serial=$(basename "$disk" | sed 's/ata-ST18000NM000J-2TV103_//') + # Extract serial from disk ID for labels (part after last underscore) + serial=$(basename "$disk" | sed 's/.*_//') # Check power state without waking disk power_state=$(${pkgs.hdparm}/bin/hdparm -C "$device" 2>/dev/null | grep -oP '(standby|active/idle|active|idle)' | head -1 || echo "unknown") diff --git a/hosts/nas/modules/power-management.nix b/hosts/nas/modules/power-management.nix index 1d0b338..2138963 100644 --- a/hosts/nas/modules/power-management.nix +++ b/hosts/nas/modules/power-management.nix @@ -15,5 +15,10 @@ ACTION=="add", KERNEL=="sd[a-z]", SUBSYSTEM=="block", \ ATTRS{model}=="ST18000NM000J*", \ RUN+="${pkgs.hdparm}/bin/hdparm -B 127 -S 180 /dev/%k" + + # Toshiba 20TB NAS drives - same settings + ACTION=="add", KERNEL=="sd[a-z]", SUBSYSTEM=="block", \ + ATTRS{model}=="TOSHIBA MG10ACA20TE*", \ + RUN+="${pkgs.hdparm}/bin/hdparm -B 127 -S 180 /dev/%k" ''; }