24 KiB
Droidnix: A Dendritic NixOS + Home Manager Configuration NixOS Configuration Structure
- Shortcuts
- Introduction
- Root Level
- Generated Structure
- The Assets Folder
- The Actual Code
- Following are the imported modules
- generated/traveldroid/modules/apps
- generated/modules/traveldroid/desktop
- generated/modules/traveldroid/system
- generated/users
Shortcuts
Introduction intro
What is Droidnix
Droidnix is a modular, declarative NixOS + Home Manager configuration system. It allows users to choose between Hyprland and Mangowc as their window manager, with shared and WM-specific configurations managed via Emacs Org and Nix Flakes. The project is designed for reproducibility, maintainability, and cross-machine compatibility.
Installed components:
Core
Hyprland
Mangowc
Goals, project Structure, import hierarchy
This project uses a modular NixOS configuration with Hyprland and MangoWC support, designed for literate programming and cross-device reusability. The Droidnix repository is organized into two main parts:
.assets/: Static, non-generated files (e.g., configs, scripts, themes).- Generated folders (
system,hyprland,mangowc): NixOS and Home Manager configurations, generated from Org files.
Root Level
-
flake.nixis the entry point and imports:generated/assets/generated/<host>/modules/generated/hosts/
Generated Structure
The generated/ directory contains all generated configurations, divided into three main groups: system, hyprland, and mangowc.
First Setup
- Clone this repository.
- Run the setup script:
./setup_droid. - Edit
.assets/system/conf/base.confto choose your window manager (wm = "hyprland"orwm = "mangowc"). - Tangle this Org file to generate Nix configurations:
C-c C-v tin Emacs or use this:emacs README.org --batch -f org-babel-tangle && emacs --batch --eval "(setq org-html-htmlize-output-type nil)" README.org -f org-html-export-to-html - Build and switch:
sudo nixos-rebuild switch --flake .#<hostname>.
—
The Assets Folder assets
The .assets/ folder contains all static files, such as configs, scripts, and themes. These files are not generated and can be edited directly.
The Actual Code code
This section contains the Org blocks for tangling Nix code into the generated folders.
flake.nix
The Nix flake definition for Droidnix.
{
description = "Droidnix: A dendritic NixOS + Home Manager configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
import-tree.url = "github:vic/import-tree";
stylix = {
url = "github:nix-community/stylix";
inputs.nixpkgs.follows = "nixpkgs";
};
zen-browser = {
url = "github:youwen5/zen-browser-flake";
inputs.nixpkgs.follows = "nixpkgs";
};
hyprland.url = "github:hyprwm/Hyprland";
};
outputs = { self, nixpkgs, home-manager, import-tree, stylix, hyprland, zen-browser, ... }:
let
system = "x86_64-linux";
flakeRoot = self;
in {
nixosConfigurations = {
traveldroid = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
./generated/hosts/traveldroid/host.nix
];
specialArgs = {
inherit flakeRoot;
inherit home-manager import-tree stylix hyprland zen-browser;
};
};
};
};
}
generated/hosts/traveldroid/host.nix
{ lib, config, pkgs, flakeRoot, import-tree, home-manager, ... }:
let
hostname = "traveldroid";
modulesPath = "${flakeRoot}/generated/modules/${hostname}";
usersPath = "${flakeRoot}/generated/users";
hostModules = import-tree modulesPath;
globalUsers = import-tree usersPath;
allModules = hostModules.imports ++ globalUsers.imports;
in
{
#################################
# Core system config
#################################
networking.hostName = hostname;
system.stateVersion = "26.05";
#################################
# Imports
#################################
imports =
[
./boot.nix
./hardware-configuration.nix
# REQUIRED for Home Manager
home-manager.nixosModules.home-manager
]
++ allModules;
#################################
# Home Manager integration
#################################
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
generated/hosts/traveldroid/hardware-configuration.nix
- Boot into NixOS Live ISO or your installed system.
- Open a terminal.
- Run: <code>sudo nixos-generate-config –root /mnt</code> (Omit –root /mnt if already running NixOS.)
{
hostname,
pkgs,
lib,
modulesPath,
user,
config,
...
}:
{
imports = [
# (modulesPath + "/installer/scan/not-detected.nix")
#../../hardware/hardware.nix
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"nvme"
"usb_storage"
"sd_mod"
"rtsx_usb_sdmmc"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/69433a14-fbaf-401b-af85-cd1bbf02b4e2";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/811D-0676";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [
{ device = "/dev/disk/by-uuid/b6c557c2-7682-460b-a5e7-8f6f2f429a3a"; }
];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
generated/hosts/traveldroid/boot.nix
{ config, pkgs, lib, flakeRoot, ... }:
let
hostname = config.networking.hostName or "traveldroid";
# Build GRUB theme from your assets
grubTheme = pkgs.stdenv.mkDerivation {
name = "droidnix-grub-theme";
src = "${flakeRoot}/assets/traveldroid/theming/boot";
installPhase = ''
mkdir -p $out
# GRUB expects this exact name
cp theming.txt $out/theme.txt
# Assets
cp background.png $out/
'';
};
in
{
#################################
# Bootloader (UEFI + GRUB)
#################################
boot.loader = {
efi = {
canTouchEfiVariables = true;
efiSysMountPoint = "/boot";
};
grub = {
enable = true;
efiSupport = true;
device = "nodev";
useOSProber = true;
# Keep multiple generations (fixes menu issue)
configurationLimit = 10;
# ✅ Fully declarative theme
theme = grubTheme;
};
timeout = 5;
};
#################################
# Kernel / initrd
#################################
boot.kernelPackages = pkgs.linuxPackages_latest;
boot.kernelParams = [
"quiet"
"splash"
"udev.log_level=3"
"rd.systemd.show_status=false"
];
boot.consoleLogLevel = 0;
boot.initrd.systemd.enable = true;
boot.initrd.availableKernelModules = [
"xhci_pci"
"nvme"
"usb_storage"
"sd_mod"
"rtsx_usb_sdmmc"
];
boot.kernelModules = [ "kvm-intel" ];
#################################
# Plymouth
#################################
boot.plymouth = {
enable = true;
theme = "rings";
themePackages = [
(pkgs.adi1090x-plymouth-themes.override {
selected_themes = [ "rings" ];
})
];
};
#################################
# CPU / platform
#################################
hardware.cpu.intel.updateMicrocode =
lib.mkDefault config.hardware.enableRedistributableFirmware;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
Following are the imported modules
generated/traveldroid/modules/apps
generated/modules/traveldroid/apps/packages.nix
This installs a list of apps
{ lib, config, pkgs, flakeRoot, ... }:
let
#################################
# Read package list from config file
#################################
packagesConfPath = "${flakeRoot}/assets/traveldroid/conf/packages.conf";
raw = builtins.readFile packagesConfPath;
rawLines = lib.splitString "\n" raw;
# Guard against splitting into characters accidentally
_guard = assert !(builtins.stringLength raw > 1 && builtins.length rawLines == builtins.stringLength raw); true;
# Clean each line: remove CRs, comments, trim whitespace
cleanLine = line:
let
noCR = lib.replaceStrings [ "\r" ] [ "" ] line;
noInlineComment = lib.head (lib.splitString "#" noCR);
in
lib.strings.trim noInlineComment;
# Filter out empty lines
entries = builtins.filter (l: l != "") (map cleanLine rawLines);
# Resolve attribute paths in pkgs
resolvePkg = name:
let
parts = lib.splitString "." name;
found = lib.attrByPath parts null pkgs;
in
if found == null then
(throw ''
packages.nix: package not found in pkgs
Token : ${builtins.toJSON name}
packages.conf : ${packagesConfPath}
Hint : check the attribute name on search.nixos.org/packages
'')
else
found;
# Final system-wide package list
packages = builtins.seq _guard (map resolvePkg entries);
in {
#################################
# Allow unfree packages globally
#################################
nixpkgs.config = { allowUnfree = true; };
#################################
# System packages
#################################
environment.systemPackages = packages;
}
generated/modules/traveldroid/apps/kitty.nix
This file sets up Kitty terminal
{ lib, pkgs, config, ... }:
let
#################################
# Determine default username
#################################
username = config.defaultUser or "henrov";
moduleName = "kitty";
#################################
# Paths to assets
#################################
assetPath = ../../../assets/traveldroid/conf/${moduleName};
programFiles = builtins.readDir assetPath;
# Convert asset files into a nix attribute set
files = lib.genAttrs (builtins.attrNames programFiles) (name: {
source = "${assetPath}/${name}";
});
in
{
#################################
# System-wide packages
#################################
environment.systemPackages = [
pkgs.kitty
];
#################################
# Home Manager user configuration
#################################
_module.args.hmUsers = {
${username} = {
# Enable Kitty through Home Manager
programs.kitty.enable = true;
# Extra user-specific config snippet
programs.kitty.extraConfig = ''
# Include the Catppuccin-Mocha theme
include themes/Catppuccin-Mocha.conf
'';
# Map all asset files into ~/.config/kitty/
home.file = lib.mkMerge (
map (name: { ".config/${moduleName}/${name}" = { source = files.${name}.source; }; })
(builtins.attrNames files)
);
};
};
}
generated/modules/traveldroid/apps/starship.nix
This file sets up starship prompt
{ lib, config, pkgs, flakeRoot, ... }:
let
# Default username fallback
username = config.defaultUser or "henrov";
# Path to the starship config in assets
starshipConfSrc = "${flakeRoot}/assets/traveldroid/conf/starship.toml";
in
{
#################################
# Enable Starship system-wide
#################################
environment.systemPackages = [ pkgs.starship ];
#################################
# Home Manager user configuration
#################################
_module.args.hmUsers = {
${username} = {
programs.starship = {
enable = true;
};
# Copy the starship.toml from assets to ~/.config/starship.toml
home.file = {
".config/starship.toml" = { source = starshipConfSrc; };
};
};
};
}
generated/modules/traveldroid/desktop
generated/modules/traveldroid/desktop/fonts.nix
This file installs and configures fonts
{ lib, pkgs, config, ... }:
{
fonts.packages = with pkgs; [
nerd-fonts.iosevka
nerd-fonts.fira-code
];
}
generated/modules/traveldroid/desktop/hyprland.nix
Setting up Hyprland
{ lib, config, pkgs, flakeRoot, home-manager, inputs, ... }:
let
username = config.defaultUser or "henrov";
assetPath = "${flakeRoot}/assets/traveldroid/conf/hypr/";
# Read all files in the asset directory
assetFiles = builtins.attrNames (builtins.readDir assetPath);
# Convert files to Home Manager xdg config entries
hyprFiles = lib.genAttrs assetFiles (f: {
# Destination path in home directory
name = ".config/hypr/${f}";
# Source file path
value = { source = "${assetPath}/${f}"; };
});
# Determine Hyprland package
hyprlandPkg =
pkgs.hyprland or
pkgs.hyprland-git or
inputs.hyprland.packages.${pkgs.system}.default;
in
{
environment.systemPackages = [ hyprlandPkg ];
_module.args.hmUsers = {
${username} = {
home.packages = [ hyprlandPkg ];
# Merge all files in the asset folder into ~/.config/hypr/
home.file = lib.mkMerge hyprFiles;
# Optional: Hyprland settings
settings.general."col.active_border" = "0xff97cbcd 0xff89b4fa";
};
};
}
generated/modules/traveldroid/desktop/stylix.nix
{ lib, config, pkgs, flakeRoot, ... }:
let
# Determine the default username from host config
username = config.defaultUser or "henrov";
moduleName = "stylix";
assetPath = "${flakeRoot}/assets/traveldroid/conf/";
# Read all files in the asset directory
programFiles = builtins.readDir assetPath;
files = lib.genAttrs (builtins.attrNames programFiles) (name: {
source = "${assetPath}/${name}";
});
# Optional stylix.conf
stylixConfFile = "${assetPath}/stylix.conf";
stylixConf =
if builtins.pathExists stylixConfFile
then builtins.readFile stylixConfFile
else "";
# Cursor defaults
cursorName = "phinger-cursors-light";
cursorSize = 24;
in
{
############################
# System-level packages
############################
environment.systemPackages = [
pkgs.feh
pkgs.st
];
############################
# Home Manager user config
############################
_module.args.hmUsers = {
${username} = {
# Map all config files into ~/.config/stylix/
xdg.configFile =
lib.mapAttrs' (name: value: {
name = "${moduleName}/${name}";
value = { inherit (value) source; };
}) files;
# Environment variables for the session
home.sessionVariables = {
STYLIX_CONF = "$HOME/.config/stylix/stylix.conf";
XCURSOR_THEME = cursorName;
XCURSOR_SIZE = toString cursorSize;
HYPRCURSOR_THEME = cursorName;
HYPRCURSOR_SIZE = toString cursorSize;
};
};
};
}
generated/modules/traveldroid/desktop/waybar.nix
This file installs and configures waybar
{ lib, config, pkgs, flakeRoot, ... }:
let
username = config.defaultUser or "henrov";
moduleName = "waybar";
assetPath = "${flakeRoot}/assets/traveldroid/conf/${moduleName}";
in
{
#################################
# System packages
#################################
environment.systemPackages = [
pkgs.waybar
];
#################################
# Home Manager integration
#################################
_module.args.hmUsers = {
${username} = {
# Install Waybar for the user as well (optional but recommended)
home.packages = [ pkgs.waybar ];
# Place config files into ~/.config/waybar/
xdg.configFile = {
"${moduleName}/config".source = "${assetPath}/config";
"${moduleName}/style.css".source = "${assetPath}/style.css";
};
};
};
}
generated/modules/traveldroid/desktop/wayland.nix
{ lib, config, pkgs, ... }:
{
#################################
# Core Wayland packages
#################################
environment.systemPackages = with pkgs; [
wayland
wl-clipboard # optional but commonly used for copy/paste
];
#################################
# Optional: enable graphics stack
#################################
hardware.graphics.enable = true;
#################################
# Optional session variables for Wayland
#################################
environment.sessionVariables = {
# Forces some apps to use Wayland
NIXOS_OZONE_WL = "1";
};
}
generated/modules/traveldroid/desktop/xdg.nix
This sets the XDG implementation
{ lib, config, pkgs, inputs, ... }:
let
#################################
# Default username fallback
#################################
username = config.defaultUser or "henrov";
#################################
# Determine XDG portal package
#################################
xdgPortalHyprlandPkg =
pkgs.xdg-desktop-portal-hyprland or
inputs.xdgPortalHyprland.packages.${pkgs.system}.default;
in
{
#################################
# System-wide packages
#################################
environment.systemPackages = [
xdgPortalHyprlandPkg
];
#################################
# Home Manager user config
#################################
_module.args.hmUsers = {
${username} = {
home.packages = [
xdgPortalHyprlandPkg
];
# Enable XDG portal integration for Hyprland
xdg.portal = {
enable = true;
extraPortals = [ xdgPortalHyprlandPkg ];
config.hyprland = {
"org.freedesktop.impl.portal.Screencast" = [ "hyprland" ];
};
};
};
};
}
generated/modules/traveldroid/system
generated/modules/traveldroid/system/networking.nix
This sets the networking.
{ lib, config, pkgs, ... }:
{
#################################
# Networking core
#################################
networking = {
# Let DHCP be default unless overridden elsewhere
useDHCP = lib.mkDefault true;
# Hostname comes from host.nix, do NOT redefine here
#################################
# NetworkManager (primary stack)
#################################
networkmanager = {
enable = true;
# Use iwd backend for WiFi
wifi.backend = "iwd";
};
#################################
# iwd (WiFi daemon)
#################################
wireless.iwd = {
enable = true;
# Allow user control via NM / CLI
settings.General.EnableNetworkConfiguration = true;
};
#################################
# Firewall
#################################
firewall = {
enable = true;
# KDE Connect support
allowedTCPPortRanges = [
{ from = 1714; to = 1764; }
];
allowedUDPPortRanges = [
{ from = 1714; to = 1764; }
];
};
};
#################################
# System packages
#################################
environment.systemPackages = [
pkgs.networkmanager
];
}
generated/modules/traveldroid/system/nix.nix
{ lib, config, ... }:
{
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
download-buffer-size = 536870912; # 512 MB
cores = 2;
max-jobs = 1;
};
}
generated/modules/traveldroid/system/login-tuigreet.nix
This sets up tuigreeter which is not fancy but imo fits the aesthetic I am aiming for
{ lib, config, pkgs, flakeRoot, ... }:
let
username = config.defaultUser or "henrov";
in
{
#################################
# Install tuigreet
#################################
environment.systemPackages = [ pkgs.tuigreet ];
#################################
# Greetd (simple + reliable)
#################################
services.greetd = {
enable = true;
settings.default_session = {
user = "greeter";
command = ''
${pkgs.tuigreet}/bin/tuigreet \
--remember \
--time \
--time-format "%I:%M %p | %a • %h | %F" \
--cmd ${pkgs.hyprland}/bin/Hyprland
'';
};
};
}
generated/users
generated/users/copy_2_home.nix
This copies stuff to the user home-folder
{ config, pkgs, lib, flakeRoot, ... }:
let
username = config.users.users.defaultUser or "henrov";
homeDir = "/home/${username}";
assetPath = "${flakeRoot}/assets/copy_2_home";
in
{
environment.systemPackages = [ pkgs.rsync ];
systemd.services.copyAssets = {
description = "Copy assets to ${username}'s home directory";
wantedBy = [ "multi-user.target" ];
# oneshot service runs once at boot
serviceConfig.Type = "oneshot";
# Always use /bin/sh -c for multi-line commands
serviceConfig.ExecStart = ''
/bin/sh -c '
echo "Copying assets from ${assetPath} to ${homeDir} ..."
if [ ! -d "${assetPath}" ]; then
echo "ERROR: ${assetPath} does not exist"
exit 1
fi
mkdir -p "${homeDir}"
chown ${username}:${username} "${homeDir}"
${pkgs.rsync}/bin/rsync -a --no-owner --no-group "${assetPath}/" "${homeDir}/"
echo "Done copying assets."
'
'';
};
}
generated/users/henrov.nix
This is the default user, just search and replace henrov another name if you want to change
{ lib, config, pkgs, ... }:
let
username = "henrov";
in
{
#################################
# NixOS system user
#################################
users.users.${username} = {
isNormalUser = true;
home = "/home/${username}";
hashedPassword = "$6$S7iShgBxB.77CwmP$i0njK.2r3OL5UEvgZbmwZ0rnpZ4QyJcv8p9uCmJ4AiVPSMXkQkIwMLzyAOnJ0q8.tPLIp/7EquEIZeK8qbmgw/";
extraGroups = [ "wheel" "networkmanager" ];
};
#################################
# Home Manager user definition
#################################
_module.args.hmUsers = {
${username} = {
# Minimal required
home.username = username;
home.homeDirectory = "/home/${username}";
home.stateVersion = "26.05";
# Add user-specific packages here
home.packages = [
];
# Add user dotfiles, session variables, etc. here if needed
home.file = {
# Example:
# ".bashrc" = { source = /path/to/bashrc; };
};
};
};
}