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/desktop/fonts.nixgenerated/modules/traveldroid/desktop/gtk.nixgenerated/modules/traveldroid/desktop/hyprland.nixgenerated/modules/traveldroid/desktop/stylix.nixgenerated/modules/traveldroid/desktop/waybar.nixgenerated/modules/traveldroid/desktop/wayland.nixgenerated/modules/traveldroid/desktop/xdg.nix
- 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;
# Install dconf for the system/user
environment.systemPackages = [
pkgs.dconf
];
# Ensure Home Manager writes dconf safely
programs.dconf.enable = 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, ... }:
{
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
efi.efiSysMountPoint = "/boot";
timeout = 5;
};
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" ];
boot.plymouth = {
enable = true;
theme = "rings";
themePackages = [
(pkgs.adi1090x-plymouth-themes.override {
selected_themes = [ "rings" ];
})
];
};
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/apps/thunar.nix
This is top file of this level which contains just an import statement for all relevant files and/or the subfolder in this folder
{ pkgs, config, lib, ... }:
let
# Resolve the default username from host config
username = config.defaultUser or "henrov";
in
{
############################
# System-level packages
############################
environment.systemPackages = with pkgs; [
thunar # main file manager
thunar-archive-plugin # zip, tar, rar, 7z support
thunar-volman # auto-mount removable drives
gvfs # support for external drives and network shares
xarchiver # optional GUI archive manager
];
############################
# Home Manager user-level configuration
############################
# Direct assignment to the user avoids recursiveUpdate issues
home-manager.users."${username}" = {
home.stateVersion = "26.05"; # required
home.sessionVariables = {
FILE_MANAGER = "thunar";
USERNAME = username;
};
};
}
generated/modules/traveldroid/apps/zenbrowser.nix
This is top file of this level which contains just an import statement for all relevant files and/or the subfolder in this folder
{ config, pkgs, lib, zen-browser, ... }:
let
# Grab the Zen Browser package for this host system
zenBrowser = zen-browser.packages.${pkgs.stdenv.hostPlatform.system}.default;
in
{
environment.systemPackages = lib.mkForce [
zenBrowser
];
}
generated/modules/traveldroid/apps/zsh.nix
This sets up the zsh in the terminal
{ config, pkgs, lib, ... }:
{
#################################
# Zsh configuration
#################################
programs.zsh = {
enable = true;
enableCompletion = true;
syntaxHighlighting.enable = true;
# Oh My Zsh integration
ohMyZsh = {
enable = true;
theme = ""; # default theme, you can override per user
plugins = [
"git"
"sudo"
"extract"
"colored-man-pages"
"command-not-found"
"history"
"docker"
"kubectl"
];
};
# Uncomment if you want additional features
# autosuggestion.enable = true;
# autocd = true;
# dotDir = "${config.xdg.configHome}/zsh";
};
}
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/gtk.nix
Setting up GTK
{ pkgs, config, lib, ... }:
let
# Resolve the username from the host config
username = config.defaultUser or "henrov";
in
{
############################
# System-level GTK packages
############################
environment.systemPackages = with pkgs; [
gtk3
gtk4
];
############################
# Home Manager user-level GTK configuration
############################
# Directly assign the GTK config to the user, no recursiveUpdate
home-manager.users."${username}" = {
gtk = {
enable = true;
# GTK theme
theme = {
name = "Catppuccin-Mocha-Standard-Blue-Dark";
package = pkgs.magnetic-catppuccin-gtk;
};
# Icon theme
iconTheme = {
name = "Papirus-Dark";
package = pkgs.papirus-icon-theme;
};
# Extra GTK3 / GTK4 settings
gtk3.extraConfig = {
"gtk-application-prefer-dark-theme" = 1;
};
gtk4.extraConfig = {
"gtk-application-prefer-dark-theme" = 1;
};
};
};
}
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, ... }:
let
username = config.defaultUser or "henrov";
moduleName = "stylix";
# Path to stylix assets
assetPath = ../../../assets/system/conf/${moduleName};
# 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 packages
############################
environment.systemPackages = [
pkgs.feh
pkgs.st
];
############################
# Home Manager user settings
############################
# Use the _module.args.hmUsers style to avoid "option does not exist"
_module.args.hmUsers = {
"${username}" = {
# Copy all stylix config files into ~/.config/stylix/
xdg.configFile =
lib.mapAttrs' (name: value: {
name = "${moduleName}/${name}";
value = { inherit (value) source; };
}) files;
# Optionally include stylix.conf
home.file."${moduleName}/stylix.conf".text = stylixConf;
# Session variables
home.sessionVariables = {
STYLIX_CONF = "$HOME/.config/stylix/stylix.conf";
XCURSOR_THEME = cursorName;
XCURSOR_SIZE = toString cursorSize;
HYPRCURSOR_THEME = cursorName;
HYPRCURSOR_SIZE = toString cursorSize;
};
# Enable GTK target for Stylix
stylix = {
enable = true;
targets = {
gtk = { enable = true; };
};
};
};
};
}
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/dbus.nix
This sets the dbus implementation
{ config, pkgs, lib, ... }:
{
#################################
# System-level DBus service
#################################
services.dbus = lib.mkForce {
enable = true; # Force DBus to be enabled
};
#################################
# dbus-broker configuration
#################################
environment.etc."dbus-broker/launch.conf".text = ''
[General]
LogLevel=warning
MaxConnectionsPerUser=2048
'';
}
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
{ config, pkgs, lib, ... }:
let
tuigreetBin = "${pkgs.tuigreet}/bin/tuigreet";
sessionsDir = "${pkgs.hyprland}/share/wayland-sessions";
in
{
#################################
# Greetd (tuigreet)
#################################
services.greetd = {
enable = true;
settings = {
default_session = {
command = ''
${tuigreetBin} \
--time \
--remember \
--remember-session \
--sessions ${sessionsDir} \
--cmd "start-hyprland"
'';
user = "greeter";
};
};
};
#################################
# Fix TTY / boot noise issues
#################################
systemd.services.greetd.serviceConfig = {
Type = "idle";
StandardInput = "tty";
StandardOutput = "tty";
StandardError = "journal";
# Prevent boot log spam on tty
TTYReset = true;
TTYVHangup = true;
TTYVTDisallocate = true;
};
}
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}/"
# Fix .config permissions
mkdir -p "${homeDir}/.config"
chown -R ${username}:${username} "${homeDir}/.config"
chmod u+rwx "${homeDir}/.config"
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
{
#################################
# Ensure system user has a valid shell
#################################
users.users.${username} = {
isNormalUser = true;
home = "/home/${username}";
hashedPassword = "$6$S7iShgBxB.77CwmP$i0njK.2r3OL5UEvgZbmwZ0rnpZ4QyJcv8p9uCmJ4AiVPSMXkQkIwMLzyAOnJ0q8.tPLIp/7EquEIZeK8qbmgw/"; # keep your existing hash
shell = pkgs.bash; # ensures login shell exists in this generation
extraGroups = [
"wheel"
"networkmanager"
];
};
#################################
# NixOS system user
#################################
users.users.${username} = {
isNormalUser = true;
home = "/home/${username}";
hashedPassword = "$6$S7iShgBxB.77CwmP$i0njK.2r3OL5UEvgZbmwZ0rnpZ4QyJcv8p9uCmJ4AiVPSMXkQkIwMLzyAOnJ0q8.tPLIp/7EquEIZeK8qbmgw/";
shell = pkgs.bash;
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; };
};
};
};
}