Configuration for my NixOS based systems and Home Manager
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Cleanup

+151 -291
+151
README.md
··· 1 + # NixOS + Home Manager Configuration 2 + 3 + This repository contains my personal NixOS system configurations and Home Manager setup, managed via Nix flakes. 4 + 5 + ## Overview 6 + 7 + This is a multi-host NixOS configuration using: 8 + - **Nix Flakes** for reproducible builds and dependency management 9 + - **Home Manager** for user environment configuration 10 + - **agenix** for secrets management (age-based encryption) 11 + - **pre-commit-hooks** for code quality 12 + 13 + ## Hosts 14 + 15 + | Hostname | Purpose | Platform | 16 + |----------|---------|----------| 17 + | **odin** | Main workstation with AMD GPU, Docker, Coder server, Valheim game server | x86_64-linux | 18 + | **misaki** | NAS/Server with ZFS, Plex, Jellyfin, Immich, Nginx reverse proxy | x86_64-linux | 19 + | **shizuri** | Steam/gaming machine with LXQt desktop and XRDP | x86_64-linux | 20 + | **edge** | Solana validator node with Agave software | x86_64-linux | 21 + | **touma-wsl** | WSL2 NixOS configuration | x86_64-linux | 22 + | **aleister-noah** | macOS workstation (Home Manager only) | aarch64-darwin | 23 + 24 + ## Repository Structure 25 + 26 + ``` 27 + . 28 + ├── flake.nix # Main flake configuration defining all hosts 29 + ├── flake.lock # Lock file for reproducible builds 30 + ├── shell.nix # Development shell configuration 31 + ├── boot.nix # Bootloader configuration (shared) 32 + ├── common.nix # Common system settings (timezone, locale, gc, etc.) 33 + ├── users.nix # User definitions and SSH keys 34 + ├── services.nix # Shared service configurations 35 + ├── default-home.nix # Default Home Manager configuration 36 + ├── host-specific/ # Per-host configurations 37 + │ ├── odin/ 38 + │ ├── misaki/ 39 + │ ├── shizuri/ 40 + │ ├── edge/ 41 + │ └── touma-wsl.nix 42 + ├── overlays/ # Package overlays 43 + ├── secrets/ # Encrypted secrets (agenix) 44 + ├── nvim/ # Neovim configuration 45 + ├── fish/ # Fish shell configuration 46 + ├── aerc/ # aerc email client configuration 47 + ├── scripts/ # Custom utility scripts 48 + └── README.md # This file 49 + ``` 50 + 51 + ## Host Configuration Pattern 52 + 53 + Each host in `host-specific/<hostname>/` follows a modular structure: 54 + - `configuration.nix` - Main entry point, imports other modules 55 + - `hardware-configuration.nix` - Auto-generated hardware config 56 + - `boot.nix` - Bootloader and kernel settings 57 + - `networking.nix` - Network configuration 58 + - `packages.nix` - System packages 59 + - `services.nix` - Service definitions 60 + - `gui.nix` - GUI/Desktop environment settings (where applicable) 61 + - `users.nix` - Host-specific users and groups 62 + 63 + ## Adding a New Host 64 + 65 + 1. Create a new directory under `host-specific/<hostname>/` 66 + 2. Copy the hardware configuration from the target machine: 67 + ```bash 68 + nixos-generate-config --show-hardware-config > host-specific/<hostname>/hardware-configuration.nix 69 + ``` 70 + 3. Create `configuration.nix` importing necessary modules 71 + 4. Add the host to `flake.nix` in the `nixosConfigurations` output: 72 + ```nix 73 + nixosConfigurations.newhost = basicSystem { 74 + useUnstable = true; 75 + modules = [ 76 + ./host-specific/newhost/configuration.nix 77 + ]; 78 + }; 79 + ``` 80 + 5. Build and switch: 81 + ```bash 82 + sudo nixos-rebuild switch --flake .#newhost 83 + ``` 84 + 85 + ## Secrets Management 86 + 87 + Secrets are encrypted using [agenix](https://github.com/ryantm/agenix) with age encryption. 88 + 89 + - Secrets are stored in `secrets/` with `.age` extension 90 + - Public keys for hosts/users are defined in `secrets/secrets.nix` 91 + - Each secret specifies which hosts/users can decrypt it 92 + - Deployed secrets are owned by specific users/groups with appropriate permissions 93 + 94 + To add a new secret: 95 + 1. Add the secret file to `secrets/` 96 + 2. Define recipients in `secrets/secrets.nix` 97 + 3. Encrypt: `agenix -e secrets/my-secret.age` 98 + 4. Reference in your configuration via `age.secrets.my-secret` 99 + 100 + ## Development 101 + 102 + This repo includes a development shell with formatting and linting tools: 103 + 104 + ```bash 105 + nix develop 106 + ``` 107 + 108 + Pre-commit hooks are configured to run automatically on commit: 109 + - `nixfmt-rfc-style` - Nix code formatting 110 + - `nil` - Nix language server / linter 111 + - `luacheck` - Lua linting (for Neovim config) 112 + 113 + ## Key Features 114 + 115 + ### Overlays 116 + - `neovim.nix` - Pins Neovim to specific version 117 + - `lmstudio.nix` - Local LLM GUI (AppImage wrapper) 118 + - `inetutils.nix` - Version override for inetutils 119 + 120 + ### Home Manager 121 + The `default-home.nix` provides a comprehensive development environment including: 122 + - Neovim with extensive LSP and plugin configuration 123 + - Fish shell with custom configuration 124 + - Multiple language toolchains (Rust, Go, Clojure, Python, etc.) 125 + - Git configuration with delta/difftastic 126 + - Development tools (direnv, fzf, tmux, etc.) 127 + 128 + ## Building 129 + 130 + ### NixOS System 131 + ```bash 132 + sudo nixos-rebuild switch --flake .#<hostname> 133 + ``` 134 + 135 + ### Home Manager (standalone) 136 + ```bash 137 + home-manager switch --flake .#noah 138 + # or for macOS 139 + home-manager switch --flake .#noah-aleister 140 + ``` 141 + 142 + ## Maintenance 143 + 144 + Regular maintenance tasks: 145 + - Update flakes: `nix flake update` 146 + - Garbage collect: `nix-collect-garbage -d` (also automatic via `common.nix`) 147 + - Check configuration: `nix flake check` 148 + 149 + ## License 150 + 151 + This configuration is personal and not licensed for redistribution.
-1
flake.nix
··· 153 153 sha256 = "o2oZ2pz4opdPd8/gzq8E4oaC0NxqDw3RFcgInJdBGIs="; 154 154 }; 155 155 }; 156 - obsidian-headless = final.pkgs.callPackage ./overlays/obsidian-headless.nix { }; 157 156 ## Override the json object that contains verions and hashes for Immich 158 157 #immich = prev.immich.override { sourcesJSON = ./overrides/immich-sources.json; }; 159 158 ## Fix errors wit numpy version failing to resolve in the immich ML package
-79
gui.nix
··· 1 - { pkgs, unstable, ... }: 2 - { 3 - # Enable the X11 windowing system. 4 - services.xserver.enable = true; 5 - 6 - # Configure keymap in X11 7 - # services.xserver.xkb.layout = "us"; 8 - # services.xserver.xkb.options = "eurosign:e,caps:escape"; 9 - 10 - # Enable CUPS to print documents. 11 - # services.printing.enable = true; 12 - 13 - # Enable sound. 14 - security.rtkit.enable = true; 15 - services.pipewire = { 16 - enable = true; 17 - alsa.enable = true; 18 - alsa.support32Bit = true; 19 - pulse.enable = true; 20 - wireplumber.enable = true; 21 - }; 22 - 23 - # Graphics and parallel compute configuration 24 - hardware.opengl.extraPackages = [ 25 - pkgs.libva 26 - ]; 27 - 28 - # Enable touchpad support (enabled default in most desktopManager). 29 - # services.xserver.libinput.enable = true; 30 - 31 - # Fonts 32 - fonts.packages = with pkgs; [ 33 - fira-code 34 - fira-code-symbols 35 - noto-fonts 36 - noto-fonts-cjk 37 - noto-fonts-emoji 38 - noto-fonts-extra 39 - (nerdfonts.override { fonts = [ "FiraCode" ]; }) 40 - ]; 41 - 42 - # Polkit is a dependency of Sway. It's responsible for handling security policies 43 - security.polkit.enable = true; 44 - 45 - # Enable the sway window manager 46 - programs.sway = { 47 - enable = true; 48 - package = unstable.sway; 49 - wrapperFeatures.gtk = true; 50 - }; 51 - # Use greetd as the displaymanager 52 - #services.xserver.displayManager.greetd.enable = true; 53 - #services.xserver.displayManager.lightdm.enable = false; 54 - services.xserver.displayManager.sddm.enable = true; 55 - services.xserver.displayManager.defaultSession = "none+i3"; 56 - services.xserver.displayManager.autoLogin = { 57 - enable = true; 58 - user = "noah"; 59 - }; 60 - 61 - # i3, for when I need XOrg 62 - services.xserver.windowManager.i3 = { 63 - enable = true; 64 - extraPackages = with pkgs; [ 65 - dmenu 66 - i3status 67 - i3lock 68 - i3blocks 69 - ]; 70 - }; 71 - 72 - xdg.portal = { 73 - enable = true; 74 - wlr.enable = true; 75 - extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; 76 - }; 77 - services.dbus.enable = true; 78 - services.gnome.gnome-keyring.enable = true; 79 - }
-1
host-specific/misaki/configuration.nix
··· 7 7 ./networking.nix 8 8 ./packages.nix 9 9 ./services.nix 10 - ../../overlays/obsidian-sync.nix 11 10 ]; 12 11 nixpkgs.config.allowUnfree = true; 13 12 system.stateVersion = "23.11"; # Did you read the comment?
-26
host-specific/odin/configuration.nix
··· 9 9 ./hardware-configuration.nix 10 10 ./boot.nix 11 11 ./networking.nix 12 - #./gui.nix 13 12 ./packages.nix 14 13 ./services.nix 15 14 ./valheim.nix 16 15 ]; 17 16 18 17 nixpkgs.config.allowUnfree = true; 19 - 20 - # Set your time zone. 21 - time.timeZone = "America/Chicago"; 22 - 23 - # Select internationalisation properties. 24 - i18n.defaultLocale = "en_US.UTF-8"; 25 - # console = { 26 - # font = "Lat2-Terminus16"; 27 - # keyMap = "us"; 28 - # useXkbConfig = true; # use xkb.options in tty. 29 - # }; 30 - 31 - # Automatic doc cache generation 32 - documentation.man.generateCaches = true; 33 - 34 - # Automatic system upgrades 35 - system.autoUpgrade = { 36 - enable = true; 37 - dates = "09:00"; 38 - randomizedDelaySec = "45min"; 39 - }; 40 - 41 - # Automatic Garbage Collection 42 - nix.gc.automatic = true; 43 - nix.gc.options = "--delete-older-than 8d"; 44 18 45 19 # This option defines the first version of NixOS you have installed on this particular machine, 46 20 # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
-9
host-specific/odin/default.nix
··· 1 - { ... }: 2 - { 3 - imports = [ 4 - ./hardware-configuration.nix 5 - ./networking.nix 6 - ./packages.nix 7 - ./services.nix 8 - ]; 9 - }
-43
overlays/obsidian-headless.nix
··· 1 - # This is ripped from this PR: 2 - # https://github.com/NixOS/nixpkgs/pull/495287/changes 3 - { 4 - fetchFromGitHub, 5 - fetchPnpmDeps, 6 - lib, 7 - nodejs, 8 - pnpm, 9 - pnpmConfigHook, 10 - stdenv, 11 - }: 12 - 13 - stdenv.mkDerivation (finalAttrs: { 14 - pname = "obsidian-headless"; 15 - version = "0.0.3"; 16 - 17 - src = fetchFromGitHub { 18 - owner = "obsidianmd"; 19 - repo = "obsidian-headless"; 20 - tag = "v${finalAttrs.version}"; 21 - hash = "sha256-b69b55e9261d05fb7c4c0ec82f6dc2b6af81b359"; 22 - }; 23 - 24 - nativeBuildInputs = [ 25 - nodejs # in case scripts are run outside of a pnpm call 26 - pnpmConfigHook 27 - pnpm # At least required by pnpmConfigHook, if not other (custom) phases 28 - ]; 29 - 30 - pnpmDeps = fetchPnpmDeps { 31 - inherit (finalAttrs) pname version src; 32 - fetcherVersion = 3; 33 - hash = "sha256-b69b55e9261d05fb7c4c0ec82f6dc2b6af81b359"; 34 - }; 35 - 36 - meta = { 37 - description = "Headless client for Obsidian Sync. Sync your vaults from the command line without the desktop app. "; 38 - homepage = "https://obsidian.md/sync"; 39 - license = lib.licenses.unfree; 40 - mainProgram = "ob"; 41 - maintainers = with lib.maintainers; [ of-the-stars ]; 42 - }; 43 - })
-132
overlays/obsidian-sync.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: 7 - let 8 - cfg = config.services.obsidian-sync; 9 - 10 - vaultOpts = 11 - { name, ... }: 12 - { 13 - options = { 14 - name = lib.mkOption { 15 - type = lib.types.str; 16 - default = name; 17 - description = "Name or ID of the remote vault to sync."; 18 - }; 19 - 20 - path = lib.mkOption { 21 - type = lib.types.str; 22 - description = "Local directory to sync this vault to."; 23 - }; 24 - }; 25 - }; 26 - in 27 - { 28 - options.services.obsidian-sync = { 29 - enable = lib.mkEnableOption "Obsidian headless sync"; 30 - 31 - package = lib.mkPackageOption pkgs "obsidian-headless" { }; 32 - 33 - path = lib.mkOption { 34 - type = lib.types.str; 35 - default = "/var/lib/obsidian-sync"; 36 - description = "Base directory for Obsidian sync state and data."; 37 - }; 38 - 39 - authTokenFile = lib.mkOption { 40 - type = lib.types.nullOr lib.types.path; 41 - default = null; 42 - description = '' 43 - Path to a file containing the OBSIDIAN_AUTH_TOKEN environment variable 44 - in the format OBSIDIAN_AUTH_TOKEN=<token>. Used for non-interactive 45 - authentication. If null, you must run `ob login` manually as the 46 - service user before enabling the service. 47 - ''; 48 - }; 49 - 50 - user = lib.mkOption { 51 - type = lib.types.str; 52 - default = "obsidian"; 53 - description = "User account under which the sync services run."; 54 - }; 55 - 56 - group = lib.mkOption { 57 - type = lib.types.str; 58 - default = "obsidian"; 59 - description = "Group under which the sync services run."; 60 - }; 61 - 62 - vaults = lib.mkOption { 63 - type = lib.types.attrsOf (lib.types.submodule vaultOpts); 64 - default = { }; 65 - description = '' 66 - Vaults to sync. Each vault must first be linked via 67 - `ob sync-setup --vault <name> --path <path>` before the service 68 - will function. The service runs `ob sync --path <path> --continuous` 69 - for each configured vault. 70 - ''; 71 - example = lib.literalExpression '' 72 - { 73 - "personal-notes" = { 74 - path = "/srv/obsidian/personal"; 75 - }; 76 - "work" = { 77 - path = "/srv/obsidian/work"; 78 - }; 79 - } 80 - ''; 81 - }; 82 - }; 83 - 84 - config = lib.mkIf cfg.enable { 85 - users.users.${cfg.user} = lib.mkIf (cfg.user == "obsidian") { 86 - isSystemUser = true; 87 - group = cfg.group; 88 - home = cfg.path; 89 - createHome = true; 90 - }; 91 - 92 - users.groups.${cfg.group} = lib.mkIf (cfg.group == "obsidian") { }; 93 - 94 - systemd.services = lib.mapAttrs' ( 95 - name: vault: 96 - let 97 - safeName = builtins.replaceStrings [ " " ] [ "-" ] name; 98 - in 99 - lib.nameValuePair "obsidian-sync-${safeName}" { 100 - description = "Obsidian Sync - ${name}"; 101 - after = [ "network-online.target" ]; 102 - wants = [ "network-online.target" ]; 103 - wantedBy = [ "multi-user.target" ]; 104 - 105 - environment = { 106 - HOME = cfg.path; 107 - }; 108 - 109 - serviceConfig = { 110 - Type = "simple"; 111 - User = cfg.user; 112 - Group = cfg.group; 113 - ExecStart = "${lib.getExe cfg.package} sync --path ${lib.escapeShellArg vault.path} --continuous"; 114 - Restart = "on-failure"; 115 - RestartSec = "30s"; 116 - 117 - # Hardening 118 - NoNewPrivileges = true; 119 - ProtectSystem = "strict"; 120 - ReadWritePaths = [ 121 - vault.path 122 - cfg.path 123 - ]; 124 - PrivateTmp = true; 125 - } 126 - // lib.optionalAttrs (cfg.authTokenFile != null) { 127 - EnvironmentFile = cfg.authTokenFile; 128 - }; 129 - } 130 - ) cfg.vaults; 131 - }; 132 - }