From c5a99c5b75df00c7380aa26fe60133a6019de248 Mon Sep 17 00:00:00 2001 From: Brian Nelson Date: Wed, 11 Feb 2026 13:45:21 -0500 Subject: [PATCH] switched to flakes and built out modularity --- .DS_Store | Bin 0 -> 6148 bytes README.md | 74 ++++++++- configuration.nix | 151 +----------------- flake.nix | 22 +++ hosts/nixos/default.nix | 63 ++++++++ .../nixos/hardware-configuration.example.nix | 9 ++ modules/control.nix | 119 ++++++++++++++ modules/services/gitea.nix | 22 +++ modules/services/homeassistant.nix | 27 ++++ modules/services/jellyfin.nix | 12 ++ modules/services/k3s.nix | 21 +++ modules/services/navidrome.nix | 20 +++ modules/services/nextcloud.nix | 31 ++++ modules/services/vaultwarden.nix | 20 +++ profiles/server-base.nix | 49 ++++++ 15 files changed, 489 insertions(+), 151 deletions(-) create mode 100644 .DS_Store create mode 100644 flake.nix create mode 100644 hosts/nixos/default.nix create mode 100644 hosts/nixos/hardware-configuration.example.nix create mode 100644 modules/control.nix create mode 100644 modules/services/gitea.nix create mode 100644 modules/services/homeassistant.nix create mode 100644 modules/services/jellyfin.nix create mode 100644 modules/services/k3s.nix create mode 100644 modules/services/navidrome.nix create mode 100644 modules/services/nextcloud.nix create mode 100644 modules/services/vaultwarden.nix create mode 100644 profiles/server-base.nix diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..20868e14a2a7c7bf5b47ce00cb080db251181607 GIT binary patch literal 6148 zcmeHKJxc>Y5S=wK9tt6q2sT?-2^J}Aoh%WV-fYw*>JU+Y#@Jj%S7SWRtzt{!;bi0HxTa}* z-tC{H$)pmk4pl%E_-_jEx7(s)n&Nj#V3CI)p!X`gn;iZy)!oF{58& zCga>b(RSO#GL0z(k1`8ty<>&kXr9C7_RW3jITRx14C*5a>yyO-v_c8iAJL3rXi?h9 z)*q@7c^-FrJI}(SG*6PwxqKg0~oGNqWS^X4O z?o` literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 084cb3a..f2c3b95 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,73 @@ ![](./assets/nixhomelab-logo.png) -This is the config of a nix based homelab server. Currently a work in progress. -## Clustering with K3s -K3s is a simplified version of Kubernetes. +NixOS homelab configuration managed with flakes. + +## Layout + +- `flake.nix`: entrypoint and pinned `nixpkgs` input +- `hosts//default.nix`: per-host settings and service toggles +- `hosts//hardware-configuration.example.nix`: template showing expected file location +- `profiles/*.nix`: shared host profiles (server base, workstation base, etc.) +- `modules/control.nix`: central `control` option definitions +- `modules/services/*.nix`: per-service implementation modules + +## Central Service Control + +Services are toggled in one place inside each host config: + +```nix +control = { + jellyfin.enable = true; + gitea.enable = true; + + navidrome = { + enable = true; + musicFolder = "/mnt/seagate8tb/navidrome/music"; + dataFolder = "/mnt/seagate8tb/navidrome/data"; + }; +}; +``` + +Each service has its own module in `modules/services/`. + +## Hardware Config Pattern + +`hosts//default.nix` automatically imports `./hardware-configuration.nix` only if that file exists. + +On each NixOS host, generate and store host hardware data in the host folder: + +```bash +sudo nixos-generate-config --show-hardware-config > hosts/nixos/hardware-configuration.nix +``` + +## Usage + +Build a host config: + +```bash +nix build .#nixosConfigurations.nixos.config.system.build.toplevel +``` + +Test on a target machine: + +```bash +sudo nixos-rebuild test --flake .#nixos +``` + +Apply on a target machine: + +```bash +sudo nixos-rebuild switch --flake .#nixos +``` + +## Included Native Modules + +- Home Assistant +- Jellyfin +- Gitea +- Navidrome +- Vaultwarden +- Nextcloud +- k3s + +Nextcloud requires `control.nextcloud.adminPassFile` when enabled. diff --git a/configuration.nix b/configuration.nix index 7f39490..fc1da72 100644 --- a/configuration.nix +++ b/configuration.nix @@ -1,152 +1,7 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - -{ config, pkgs, ... }: - +{ ... }: { - imports = - [ # Include the results of the hardware scan. - ./hardware-configuration.nix - ./homeassistant.nix - ./k3-cluster.nix - ]; - - # Bootloader. - boot.loader.grub.enable = true; - boot.loader.grub.device = "/dev/sda"; - boot.loader.grub.useOSProber = true; - - # enable cd/dvd drive - boot.kernelModules = ["sg"]; - - networking.hostName = "nixos"; # Define your hostname. - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - - # Enable bluetooth - hardware.bluetooth.enable = true; - #services.blueman.enable = true; - - # Configure network proxy if necessary - # networking.proxy.default = "http://user:password@proxy:port/"; - # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; - - # Enable networking - networking.networkmanager.enable = true; - - # Set your time zone. - time.timeZone = "America/New_York"; - - # Select internationalisation properties. - i18n.defaultLocale = "en_US.UTF-8"; - - i18n.extraLocaleSettings = { - LC_ADDRESS = "en_US.UTF-8"; - LC_IDENTIFICATION = "en_US.UTF-8"; - LC_MEASUREMENT = "en_US.UTF-8"; - LC_MONETARY = "en_US.UTF-8"; - LC_NAME = "en_US.UTF-8"; - LC_NUMERIC = "en_US.UTF-8"; - LC_PAPER = "en_US.UTF-8"; - LC_TELEPHONE = "en_US.UTF-8"; - LC_TIME = "en_US.UTF-8"; - }; - - # Enable the X11 windowing system. - # You can disable this if you're only using the Wayland session. - services.xserver.enable = true; - - # Enable the KDE Plasma Desktop Environment. - services.displayManager.sddm.enable = true; - services.desktopManager.plasma6.enable = true; - - # Configure keymap in X11 - services.xserver.xkb = { - layout = "us"; - variant = ""; - }; - - # Enable CUPS to print documents. - services.printing.enable = true; - - # Enable sound with pipewire. - services.pulseaudio.enable = false; - security.rtkit.enable = true; - services.pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - # If you want to use JACK applications, uncomment this - #jack.enable = true; - - # use the example session manager (no others are packaged yet so this is enabled by default, - # no need to redefine it in your config for now) - #media-session.enable = true; - }; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Define a user account. Don't forget to set a password with ‘passwd’. - users.users.brian = { - isNormalUser = true; - description = "Brian"; - extraGroups = [ "networkmanager" "wheel" "docker" ]; - packages = with pkgs; [ - kdePackages.kate - # thunderbird - ]; - }; - - # Install firefox. - programs.firefox.enable = true; - - # Allow unfree packages - nixpkgs.config.allowUnfree = true; - - # Enable Flakes and nix-command - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - - # List packages installed in system profile. To search, run: - # $ nix search wget - environment.systemPackages = with pkgs; [ - # vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. - # wget + imports = [ + ./hosts/nixos/default.nix ]; - - # Set default editor - # environment.variables.EDITOR = "vscodium"; - - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: - - # Enable the OpenSSH daemon. - # services.openssh.enable = true; - - # Open ports in the firewall. - networking.firewall.allowedTCPPorts = [ - 8123 - config.services.home-assistant.config.http.server_port - ]; - networking.firewall.allowedUDPPorts = []; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "25.05"; # Did you read the comment? - } diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9dae790 --- /dev/null +++ b/flake.nix @@ -0,0 +1,22 @@ +{ + description = "NixOS homelab configuration"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; + }; + + outputs = { self, nixpkgs, ... }: + let + system = "x86_64-linux"; + in + { + nixosConfigurations = { + nixos = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./hosts/nixos/default.nix + ]; + }; + }; + }; +} diff --git a/hosts/nixos/default.nix b/hosts/nixos/default.nix new file mode 100644 index 0000000..7fc0a15 --- /dev/null +++ b/hosts/nixos/default.nix @@ -0,0 +1,63 @@ +{ lib, ... }: + +let + hardwarePath = ./hardware-configuration.nix; +in +{ + imports = [ + ../../profiles/server-base.nix + ../../modules/control.nix + ../../modules/services/homeassistant.nix + ../../modules/services/jellyfin.nix + ../../modules/services/gitea.nix + ../../modules/services/navidrome.nix + ../../modules/services/vaultwarden.nix + ../../modules/services/nextcloud.nix + ../../modules/services/k3s.nix + ] ++ lib.optionals (builtins.pathExists hardwarePath) [ hardwarePath ]; + + networking.hostName = "nixos"; + + # Bootloader defaults; adjust per host. + boot.loader.grub.enable = true; + boot.loader.grub.device = "/dev/sda"; + boot.loader.grub.useOSProber = true; + + # Central service switchboard. + control = { + homeassistant.enable = true; + jellyfin.enable = false; + gitea.enable = false; + + navidrome = { + enable = false; + musicFolder = "/mnt/seagate8tb/navidrome/music"; + dataFolder = "/mnt/seagate8tb/navidrome/data"; + port = 4533; + openFirewall = true; + }; + + vaultwarden = { + enable = false; + domain = "http://vaultwarden.local"; + port = 8222; + openFirewall = true; + }; + + nextcloud = { + enable = false; + hostName = "nextcloud.local"; + adminPassFile = null; + openFirewall = true; + }; + + k3s = { + enable = true; + role = "server"; + clusterInit = true; + tokenFile = null; + serverAddr = null; + openFirewall = true; + }; + }; +} diff --git a/hosts/nixos/hardware-configuration.example.nix b/hosts/nixos/hardware-configuration.example.nix new file mode 100644 index 0000000..c0b45d4 --- /dev/null +++ b/hosts/nixos/hardware-configuration.example.nix @@ -0,0 +1,9 @@ +# Copy this file to hardware-configuration.nix on a real NixOS host. +# Generate the real file with: +# sudo nixos-generate-config --show-hardware-config > hosts//hardware-configuration.nix + +{ ... }: + +{ + # Example only. Replace with the real generated content for this host. +} diff --git a/modules/control.nix b/modules/control.nix new file mode 100644 index 0000000..667f81f --- /dev/null +++ b/modules/control.nix @@ -0,0 +1,119 @@ +{ lib, ... }: + +with lib; + +{ + options.control = { + homeassistant.enable = mkEnableOption "Home Assistant"; + + jellyfin.enable = mkEnableOption "Jellyfin"; + + gitea.enable = mkEnableOption "Gitea"; + + navidrome = { + enable = mkEnableOption "Navidrome"; + + musicFolder = mkOption { + type = types.str; + default = "/srv/navidrome/music"; + description = "Folder containing music files."; + }; + + dataFolder = mkOption { + type = types.str; + default = "/srv/navidrome/data"; + description = "Folder for Navidrome state/data."; + }; + + port = mkOption { + type = types.port; + default = 4533; + description = "Navidrome listen port."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Open Navidrome port in firewall."; + }; + }; + + vaultwarden = { + enable = mkEnableOption "Vaultwarden"; + + domain = mkOption { + type = types.str; + default = "http://vaultwarden.local"; + description = "External URL used by Vaultwarden."; + }; + + port = mkOption { + type = types.port; + default = 8222; + description = "Vaultwarden listen port."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Open Vaultwarden port in firewall."; + }; + }; + + nextcloud = { + enable = mkEnableOption "Nextcloud"; + + hostName = mkOption { + type = types.str; + default = "nextcloud.local"; + description = "Hostname served by Nextcloud."; + }; + + adminPassFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to file containing Nextcloud admin password."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Open HTTP(S) ports used by Nextcloud."; + }; + }; + + k3s = { + enable = mkEnableOption "k3s"; + + role = mkOption { + type = types.enum [ "server" "agent" ]; + default = "server"; + description = "k3s node role."; + }; + + clusterInit = mkOption { + type = types.bool; + default = false; + description = "Initialize a new k3s cluster on this node."; + }; + + serverAddr = mkOption { + type = types.nullOr types.str; + default = null; + description = "k3s server address for joining an existing cluster."; + }; + + tokenFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to file containing shared k3s token."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Open k3s ports in the firewall."; + }; + }; + }; +} diff --git a/modules/services/gitea.nix b/modules/services/gitea.nix new file mode 100644 index 0000000..0a49eb5 --- /dev/null +++ b/modules/services/gitea.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: + +let + cfg = config.control.gitea; +in +{ + config = lib.mkIf cfg.enable { + services.gitea = { + enable = true; + settings = { + server = { + HTTP_PORT = 3000; + DOMAIN = "gitea.local"; + ROOT_URL = "http://gitea.local:3000/"; + }; + service.DISABLE_REGISTRATION = true; + }; + }; + + networking.firewall.allowedTCPPorts = [ 3000 ]; + }; +} diff --git a/modules/services/homeassistant.nix b/modules/services/homeassistant.nix new file mode 100644 index 0000000..74d2a01 --- /dev/null +++ b/modules/services/homeassistant.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: + +let + cfg = config.control.homeassistant; +in +{ + config = lib.mkIf cfg.enable { + services.home-assistant = { + enable = true; + + extraComponents = [ + "analytics" + "google_translate" + "met" + "radio_browser" + "shopping_list" + "isal" + ]; + + config = { + default_config = {}; + }; + }; + + networking.firewall.allowedTCPPorts = [ 8123 ]; + }; +} diff --git a/modules/services/jellyfin.nix b/modules/services/jellyfin.nix new file mode 100644 index 0000000..fe46046 --- /dev/null +++ b/modules/services/jellyfin.nix @@ -0,0 +1,12 @@ +{ config, lib, ... }: + +let + cfg = config.control.jellyfin; +in +{ + config = lib.mkIf cfg.enable { + services.jellyfin.enable = true; + + networking.firewall.allowedTCPPorts = [ 8096 ]; + }; +} diff --git a/modules/services/k3s.nix b/modules/services/k3s.nix new file mode 100644 index 0000000..5766457 --- /dev/null +++ b/modules/services/k3s.nix @@ -0,0 +1,21 @@ +{ config, lib, ... }: + +let + cfg = config.control.k3s; +in +{ + config = lib.mkIf cfg.enable { + services.k3s = { + enable = true; + role = cfg.role; + clusterInit = cfg.clusterInit; + serverAddr = cfg.serverAddr; + tokenFile = cfg.tokenFile; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [ 6443 2379 2380 ]; + allowedUDPPorts = [ 8472 ]; + }; + }; +} diff --git a/modules/services/navidrome.nix b/modules/services/navidrome.nix new file mode 100644 index 0000000..2e5c371 --- /dev/null +++ b/modules/services/navidrome.nix @@ -0,0 +1,20 @@ +{ config, lib, ... }: + +let + cfg = config.control.navidrome; +in +{ + config = lib.mkIf cfg.enable { + services.navidrome = { + enable = true; + settings = { + Address = "0.0.0.0"; + Port = cfg.port; + MusicFolder = cfg.musicFolder; + DataFolder = cfg.dataFolder; + }; + }; + + networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ]; + }; +} diff --git a/modules/services/nextcloud.nix b/modules/services/nextcloud.nix new file mode 100644 index 0000000..7b29d60 --- /dev/null +++ b/modules/services/nextcloud.nix @@ -0,0 +1,31 @@ +{ config, lib, ... }: + +let + cfg = config.control.nextcloud; +in +{ + assertions = [ + { + assertion = (!cfg.enable) || (cfg.adminPassFile != null); + message = "control.nextcloud.adminPassFile must be set when Nextcloud is enabled."; + } + ]; + + config = lib.mkIf cfg.enable { + services.nextcloud = { + enable = true; + hostName = cfg.hostName; + https = false; + config = { + dbtype = "sqlite"; + adminuser = "admin"; + adminpassFile = cfg.adminPassFile; + }; + settings = { + trusted_domains = [ cfg.hostName ]; + }; + }; + + networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ 80 443 ]; + }; +} diff --git a/modules/services/vaultwarden.nix b/modules/services/vaultwarden.nix new file mode 100644 index 0000000..588134c --- /dev/null +++ b/modules/services/vaultwarden.nix @@ -0,0 +1,20 @@ +{ config, lib, ... }: + +let + cfg = config.control.vaultwarden; +in +{ + config = lib.mkIf cfg.enable { + services.vaultwarden = { + enable = true; + config = { + DOMAIN = cfg.domain; + ROCKET_ADDRESS = "0.0.0.0"; + ROCKET_PORT = cfg.port; + SIGNUPS_ALLOWED = false; + }; + }; + + networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ]; + }; +} diff --git a/profiles/server-base.nix b/profiles/server-base.nix new file mode 100644 index 0000000..01616fb --- /dev/null +++ b/profiles/server-base.nix @@ -0,0 +1,49 @@ +{ pkgs, ... }: + +{ + time.timeZone = "America/New_York"; + + i18n.defaultLocale = "en_US.UTF-8"; + + i18n.extraLocaleSettings = { + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; + + networking.networkmanager.enable = true; + + services.openssh = { + enable = true; + settings = { + PasswordAuthentication = false; + PermitRootLogin = "no"; + }; + }; + + users.users.brian = { + isNormalUser = true; + description = "Brian"; + extraGroups = [ "networkmanager" "wheel" ]; + packages = with pkgs; [ ]; + }; + + nixpkgs.config.allowUnfree = true; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + environment.systemPackages = with pkgs; [ + git + tmux + curl + htop + ]; + + system.stateVersion = "25.05"; +}