I recently created NixOS Config Base, a modularized base for more advanced NixOS configurations. It aims to help produce modularized NixOS configurations quickly and easily.
I have created a personal NixOS config based on this.
Please check it out! ![]()
I recently created NixOS Config Base, a modularized base for more advanced NixOS configurations. It aims to help produce modularized NixOS configurations quickly and easily.
I have created a personal NixOS config based on this.
Please check it out! ![]()
I want to split configuration of my desktop PC into multiple modules but I was not able to come up with a consistent system that makes sense. It is a hard problem.
I am not particularly convinced system.nix × packages.nix dichotomy is a good idea since the line between user space and system is bit blurry. For example, does services.xserver.desktopManager.gnome.enable = true; go to system? What about environment.systemPackages = [pkgs.gnomeExtensions.dash-to-dock];? And most software is distributed as a package, yet sometimes it needs to be installed using a module – like programs.wireshark.enable = true; which also creates the necessary user group.
The cleanest I have seen were concern-based categories (e.g. desktop-environment.nix, development-tools.nix…), rather than grouping by type (e.g. services.nix, programs.nix…).
It is a bit easier on servers, since there I can split the configuration hierarchically based on domain name. Although some services like nginx are still stuck in top-level configuration.nix.
How about a heirarchy of functionality (for example GNOME shell config would go in desktop-environment/GNOME/shell.nix), using a tree of includes, so there might be a include.nix in every directory, which includes all include.nix one level down plus all other nix files in the current directory?
How about a heirarchy of functionality (for example GNOME shell config would go in desktop-environment/GNOME/shell.nix), using a tree of
includes, so there might be ainclude.nixin every directory, whichincludesallinclude.nixone level down plus all other nix files in the current directory?
The list given by @jtojnar is very helpful for getting some inspiration. For directories, you typically have a top-level default.nix which imports all .nix files in the directory. You don’t need to type out default.nix when importing the directory (./dir implies ./dir/default.nix).
Furthermore, you can define custom options that activate other configs. For example, my modules/desktop/default.nix is always imported but it does nothing until I set zhaofeng.desktop.enable = true;.
Yes, I have something similar based on various roles (home network member → various dns, ntp and backup settings; general desktop → DE and other tools; development → vscode and friends that aren’t in per-project devshells; photography; work stuff; etc) and a set of various container definitions that might notionally be run on any of the hosts.
Another repository I’ve taken inspiration from calls them traits.
I was thinking of a (Java package format)-style way to categorise config files.
E.g. GNOME shell config goes in org/GNOME/shell/
I’ve used many classification systems or methods over the years.
After a while you always end up with a “misc” file/directory/box in which you stuff everything ![]()
And then you start all over again…
BTW I forked Nix Starter Configs into the repo.
Puppet promotes a scheme that is called The roles and profile method. It’s basically an extra layer of abstraction that shields away the technical components, allowing you to focus on the use case of the machine.
A developer workstation computer that may run GNOME by default.
# Role for profiles that need to make a developer machine work
class role::workstation::developer (
$desktop = 'gnome',
) {
# All roles should include the base profile
include profile::base
include profile::authentication::client
include profile::workstation
include "profile::desktop::${desktop}"
include profile::development
}
The base profile configures, e.g., the underlying OS flavor.
# Base profile (includes component modules for all nodes)
class profile::base {
case $::osfamily {
'Debian': {
include unattended_upgrades
package {[
'apt-transport-https',
'aptitude',
'software-properties-common',
'ssh',
'uuid-runtime',
'vim',
]:
ensure => present,
}
service { 'systemd-timesyncd.service':
ensure => running,
enable => true,
flags => '--now',
provider => systemd,
}
}
'RedHat': {
package { 'puppetlabs-release-pc1-el-7':
# enable official Puppet Labs collection repository (old agent otherwise)
ensure => present,
provider => rpm,
source => 'https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm',
}
package {[
'openssh-clients',
'openssh-server',
'puppet-agent',
'vim-enhanced',
]:
ensure => present,
}
}
default: {
notify { "Operating system ${::operatingsystem} not supported": }
}
}
package {[
'curl',
'htop',
'nano',
'ncdu',
'sudo',
'tmux',
'tree',
]:
ensure => present,
}
}
The workstation profile configures common settings and software of any PC of this type.
# Workstation base profile (includes component modules for all client PCs)
class profile::workstation {
include software::browsers::chrome
include software::browsers::firefox
include software::communication::matrix
include mycompany::backup::restore
include mycompany::branding
include mycompany::printers
include mycompany::userdefaults
class { 'keyboard':
layout => 'en',
}
class { 'locales':
default_locale => 'en_US.UTF-8',
locales => [
'en_US.UTF-8 UTF-8',
'de_DE.UTF-8 UTF-8',
'it_IT.UTF-8 UTF-8',
],
}
# common packages needed everywhere
package {[
'gimp',
'python-gpgme',
'virtualbox',
]:
ensure => present,
}
}
The desktop role will apparently configure what’s in the (default setup) of GNOME, KDE, etc.
And so forth.
In essence, this scheme allows to keep a more high-level picture of what use case you’re dealing with when configuring your target system. When the roles are cleanly separated, the composition in a role is a very helpful tool to shape your system.
That’s how it works in Puppet when you do it right. I feel that the typical NixOS configs are too deep into the implementation details. Maybe such a scheme would help to stay more focused. Has anyone ever tried something like this?
I think many long lived NixOS configurations for multi-machine setups converge towards something like that?
I structured my NixOS config based on the puppet “profiles” and “roles” scheme some years ago. (A machine pulls in a single role, and a role contains one or more profiles.) When you’re at that high level abstraction, the config gets quite opinionated: what does the “workstation” role contain? So there’s not much sharing possible at the GitHub - NixOS/nixpkgs: Nix Packages collection & NixOS level.
IMHO, the trick (and challenge) is to separate the concerns correctly. I must be easy to compose a system. Sometimes options help – note the $desktop variable in the developer workstation above.
Simply what “workstation” class computers (desktop PCs, laptops) have in common: The office network configuration, for example.
I think, if made well, it should be possible to customize every aspect by composition. In the extreme case, you stop using a role and use profiles directly to compose your desired custom host configuration.
I’m only wondering whether Nix users would feel at home in such a setup?