NixOS Configuration File Organisation

How do you compose your configuration files?

Firstly, I am a big fan of large files, I like growing them and looking for repetition, break off into an abstraction in another file perhaps and tidy up (this is my general approach with code).

However, with NixOS I am unsure what the best approach to follow.

E.g. if I wanted “child” configuration files which utilise specific environment packages or tmpfile rules, somehow I need to combine together on a “parent” level (?).

With Terraform for example I don’t need to do this… Perhaps I am missing something, or perhaps the problem is different in both cases (Terraform resources are more independent?).

Grateful for any words of advice.

2 Likes

How do you compose your configuration files?

E.g. if I wanted “child” configuration files which utilize specific environment packages or tmpfile rules, somehow I need to combine together on a “parent” level (?).

In the case of environment.systemPackages option - you can define it in as many configuration files as you like, these arrays get merged and that’s basically it

If you find a case where you’d like to separate some logic into a separate file - you can just import ./xyz.nix { arg = 123; }

4 Likes

Ahh, I see the merging idea here. Nice!

I organize my modules the same way NixOS does: all module files are imported always and turned on with enable options.

Here’s my directory structure:

$ tree
.
├── flake.lock
├── flake.nix
├── modules
│   ├── bluetooth.nix
│   ├── common.nix
│   ├── default.nix
│   ├── dosbox.nix
│   ├── efi.nix
│   ├── graphical.nix
│   ├── laptop.nix
│   ├── mount-data.nix
│   ├── printing.nix
│   ├── run-or-raise.patch
│   ├── security.nix
│   ├── server.nix
│   ├── ssh.nix
│   └── user.nix
├── secrets
│   ├── redacted.age
│   └── secrets.nix
└── systems
    ├── home1.nix
    ├── home2.nix
    ├── laptop1.nix
    ├── laptop3.nix
    └── mail1.nix

modules/default.nix is a list of all my modules. All my modules have configuration options prefixed by ‘our’ to avoid collisions. For example, ‘our.graphical.enable’. Each system file enables the modules that make sense for it.

5 Likes

I also use a module system with enable options. Almost all are disabled by default (eg. the programs/), cli/ is the other way and enabled by default. I use ‘ncfg’ as my prefix. I’m not happy about some of the layout, I’m still looking for a better finetuning

ncfg = {
  nix.enableDirenv = true;
  network = {
    enable = true;
    wireless-interface = "wlp4s0";
    wired-interfaces."enp0s25" = { macAddress = "redacted"; };
  };
  hardware = {
    laptop.thinkpad.enable = true;
    cpu.intel.enable = true;
    gpu.primaryGPU = "intel";
    ssd.enable = true;
    printer-scanner.enable = true;
  };
  theme.additionalFonts = true;
  sway = {
    background = "${./wallpapers/redacted.jpg} fill";
    gammastep.enable = true;
    waybar.backlightModule = true;
    waybar.batteryModule = true;
    waybar.bluetoothModule = false;
    waybar.clockUtcModule = true;
    waybar.position = "top";
    waybar.pulseAudioModule = true;
    waybar.temperatureModulePath = "/sys/class/thermal/thermal_zone0/temp";
    extraConfig = ''
      assign [app_id="w1"] workspace 1
      assign [app_id="w2"] workspace 2
      assign [app_id="w3"] workspace 3
      assign [app_id="w4"] workspace 4
      assign [app_id="w5"] workspace 5
      assign [app_id="w6"] workspace 6
      assign [app_id="thunderbird"] workspace 7
      assign [app_id="w8"] workspace 8
      assign [app_id="w9"] workspace 9

      exec swaymsg "exec ${packages.${system}.thunderbird}/bin/thunderbird;"
    '';
  };
  programs = {
    browsers.firefox.enable = true;
    browsers.google-chrome.enable = true;
    editors.android-studio.enable = true;
    editors.kolourpaint.enable = true;
    editors.libreoffice.enable = true;
    editors.meld.enable = true;
    editors.lightburn.enable = true;
    editors.sublime.enable = true;
    editors.exiftool.enable = true;
    editors.ffmpeg.enable = true;
    editors.imagemagick.enable = true;
    editors.poppler_utils.enable = true;
    image-viewers.eog.enable = true;
    image-viewers.feh.enable = true;
    image-viewers.imv.enable = true;
    image-viewers.sxiv.enable = true;
    media-players.mpv.enable = true;

    obs.enable = true;
    element.enable = true;
    evince.enable = true;
    nautilus.enable = true;
    terminal.enable = true;
    thunderbird.enable = true;
    xeyes.enable = true;
    yt-dlp.enable = true;
  };
  services = {
    vnstat.enable = true;
  };
  scripts.group.desktop.enable = true;
};
λ tree
├── flake.lock
├── flake.nix
├── hosts
│   ├── my-laptop
│   │   ├── boot-loader.nix
│   │   └── hardware-configuration.nix
│   ├── my-desktop
│   │   ├── boot-loader.nix
│   │   └── hardware-configuration.nix
├── iso.nix
├── modules
│   ├── base-system.nix
│   ├── cli
│   │   ├── autojump.nix
│   │   ├── bat.nix
│   │   ├── default.nix
│   │   ├── dircolors.nix
│   │   ├── exa.nix
│   │   ├── fish.nix
│   │   ├── fzf.nix
│   │   ├── git.nix
│   │   ├── gpg.nix
│   │   ├── neofetch.nix
│   │   ├── neovim
│   │   │   ├── default.nix
│   │   │   ├── lsp.nix
│   │   │   ├── syntax.nix
│   │   │   ├── theme.nix
│   │   │   └── ui.nix
│   │   ├── neovim.nix
│   │   ├── nix-index.nix
│   │   ├── nnn-colemak.h
│   │   ├── nnn.nix
│   │   ├── screen.nix
│   │   ├── shellAliases.nix
│   │   ├── speedtest.nix
│   │   ├── starship.nix
│   │   ├── tmux.nix
│   │   └── zsh.nix
│   ├── common-system
│   │   ├── default.nix
│   │   ├── knowledgebase.nix
│   │   ├── mail.nix
│   │   ├── network.nix
│   │   ├── nix.nix
│   │   ├── smartd.nix
│   │   ├── sshd.nix
│   │   ├── sudo-doas.nix
│   │   ├── tools.nix
│   │   ├── user.nix
│   │   └── zfs.nix
│   ├── containers
│   │   ├── default.nix
│   │   ├── gui-firefox
│   ├── development
│   │   ├── android
│   │   ├── default.nix
│   │   └── docker
│   ├── graphical
│   │   ├── gnome
│   │   │   └── default.nix
│   │   └── sway
│   │       ├── default.nix
│   │       ├── gammastep.nix
│   │       ├── ipcamera-viewer
│   │       │   ├── bg.png
│   │       │   └── default.nix
│   │       ├── mako.nix
│   │       ├── waybar.nix
│   │       ├── waybar-style.css
│   │       ├── waybar-style-horizontal.css
│   │       ├── waybar-style-vertical.css
│   │       ├── wayrecorder.nix
│   │       ├── xdg-desktop-portal.nix
│   │       └── ydotool.nix
│   ├── hardware
│   │   ├── bluetooth
│   │   ├── cpu
│   │   │   ├── amd
│   │   │   ├── arm
│   │   │   ├── default.nix
│   │   │   └── intel
│   │   ├── default.nix
│   │   ├── gpu
│   │   ├── laptop
│   │   │   ├── default.nix
│   │   │   └── thinkpad
│   │   ├── printer-scanner
│   │   ├── rpi4
│   │   └── ssd
│   ├── nixcfg-link-in-home.nix
│   ├── programs
│   │   ├── _browsers
│   │   │   ├── brave
│   │   │   ├── chromium
│   │   │   ├── default.nix
│   │   │   ├── firefox
│   │   │   ├── google-chrome
│   │   │   ├── qutebrowser
│   │   │   └── ungoogled-chromium
│   │   ├── calibre
│   │   ├── default.nix
│   │   ├── dropbox
│   │   ├── _editors
│   │   │   ├── android-studio
│   │   │   ├── default.nix
│   │   │   ├── gimp
│   │   │   ├── idea
│   │   │   ├── inkscape
│   │   │   ├── kolourpaint
│   │   │   ├── libreoffice
│   │   │   ├── lightburn
│   │   │   ├── meld
│   │   │   ├── sublime
│   │   │   └── vscodium
│   │   ├── eid
│   │   ├── element
│   │   ├── evince
│   │   ├── _games
│   │   │   ├── steam
│   │   ├── gnupg
│   │   ├── _image-viewers
│   │   │   ├── default.nix
│   │   │   ├── eog
│   │   │   ├── feh
│   │   │   ├── imv
│   │   │   └── sxiv
│   │   ├── latex
│   │   ├── _media-players
│   │   │   ├── celluloid
│   │   │   ├── deadbeef
│   │   │   ├── default.nix
│   │   │   ├── mpv
│   │   │   └── vlc
│   │   ├── motioneye
│   │   ├── nautilus
│   │   ├── obs
│   │   ├── okular
│   │   ├── pass
│   │   ├── syncthing
│   │   ├── terminal
│   │   ├── thunderbird
│   │   ├── xeyes
│   │   ├── yt-dlp
│   │   └── zathura
│   ├── scripts
│   ├── services
│   │   ├── auto-reboot
│   │   ├── containers
│   │   ├── default.nix
│   │   ├── iphone
│   │   ├── matrix
│   │   ├── nextcloud
│   │   ├── nginx
│   │   ├── numlock-on-tty.nix
│   │   ├── opensnitch
│   │   ├── syncthing
│   │   └── vnstat
│   ├── sound.nix
│   ├── ssh.nix
│   ├── theme.nix
│   └── xdg.nix
├── overlay
├── packages
│   ├── default.nix
│   ├── locksway
│   ├── motioneye
│   ├── systemd-lock-handler
├── README.md
├── scripts
│   ├── create-zfs-storage-pool.sh
│   ├── drvi.sh
│   ├── nixcfg.sh
│   ├── setup-nixos-native-encrypted-zfs-boot.sh
├── secrets
├── secrets.nix
└── wallpapers

edit: home-manager and nixos are both inside these files. It can be specific configs, user groups, …
example: modules/programs/_media-players/mpv/default.nix

{ config
, lib
, pkgs
, primaryUserName
, ...
}:
with lib; let
  cfg = config.ncfg.programs.media-players.mpv;
  dftApp = [ "mpv.desktop" ];
in
{
  options.ncfg.programs.media-players.mpv.enable = lib.mkEnableOption "Enable mpv";

  config = lib.mkIf cfg.enable {
    programs.firejail = {
      enable = true;
      wrappedBinaries = {
        mpv = "${lib.getBin pkgs.mpv}/bin/mpv";
      };
    };
    home-manager.users.${primaryUserName} = { pkgs, ... }: {
      home.packages = with pkgs; [
        yt-dlp
        # streamlink
        # jellyfin-mpv-shim
      ];

      programs.mpv = {
        enable = true;
        scripts = with pkgs.mpvScripts; [
          # autoload
          # mpris  # Allows control of the player using standard media keys
          # mpv-playlistmanager
          # thumbnail # performance-intensive
        ];
        config = {
          ## Video
          # profile = "gpu-hq";
          vo = "gpu";
          scale = "bicubic_fast";
          cscale = "bicubic_fast";
          dscale = "bilinear";
          # tscale="robidouxsharp";
          scale-antiring = 1;
          cscale-antiring = 1;
          sigmoid-upscaling = "yes";
          # tscale-clamp;
          scaler-resizes-only = "yes";
          dither-depth = "auto";
          dither-size-fruit = 6;
          temporal-dither = "yes";
          gamma-factor = "0.9338";
          deband = "no"; # performance-intensive
          gpu-context = "wayland";
          hwdec = "auto-safe"; # "auto-safe" , "vdpau"
          hwdec-codecs = "all";
          target-prim = "bt.709";
          video-output-levels = "full";
          video-sync = "display-resample";
          # interpolation;
          # vd-lavc-skiploopfilter = "bidir";
          sws-scaler = "x";
          screenshot-directory = "~/pictures/mpv-screenshots/";
          screenshot-format = "png";
          screenshot-png-compression = 0;
          screenshot-png-filter = 0;
          screenshot-tag-colorspace = "yes";
          screenshot-high-bit-depth = "yes";
          geometry = "50%:50%";
          autofit = "90%x90%";
          autofit-larger = "90%x90%";
          # vo-vaapi-deint-mode = "bob";
          vd-lavc-threads = 4;

          ## Audio
          ao = "pulse";
          audio-channels = "auto";
          volume = 50;
          volume-max = 200;
          alang = "en,eng,english,us";
          ad = "lavc:libdcadec";

          ## Subtitles
          sub-ass-vsfilter-color-compat = "full";
          sub-ass-force-style = "Kerning=yes";
          demuxer-mkv-subtitle-preroll = "yes";
          slang = "en,eng,english";
          sub-auto = "all";
          # osd-font = "Bitstream Vera Sans";

          ytdl-format = "(bestvideo[fps=60][height<=1080]/bestvideo[height<=1080])[vcodec!=vp9]+(bestaudio[acodec=opus]/bestaudio[ext=webm]/bestaudio)/best";
          # ytdl-format = "bestvideo[height<=?1080][vcodec!=?vp9]+bestaudio/best";
          # ytdl-format = "ytdl-format=bestvideo[height<=?1920][fps<=?30][vcodec!=?vp9]+bestaudio/best";
          # ytdl-format = "(bestvideo[vcodec=vp9]/bestvideo)+(bestaudio[acodec=opus]/bestaudio)/best"; # Prefer VP9 and Opus codecs for streams.
          # ytdl=yes
          # ytdl-format = "(bestvideo[ext=webm]/bestvideo[height>720]/bestvideo[fps=60])[tbr<13000]+(bestaudio[acodec=opus]/bestaudio[ext=webm]/bestaudio)/best"; # Always use 1080p+ or 60 fps where available. Prefer VP9 over AVC and VP8 for high-resolution streams.

          ## Script options
          script-opts = "osc-seekbarstyle=bar,osc-valign=-1,osc-hidetimeout=1000,osc-vidscale=no,osc-layout=topbar,osc-scalewindowed=2.0,osc-scalefullscreen=2.0,osc-minmousemove=1,ytdl_hook-ytdl_path=${pkgs.yt-dlp}/bin/yt-dlp";

          ## Other
          fullscreen = "yes";
          keep-open = "no"; # Keep mpv open after playback is finished.
          save-position-on-quit = true;
          # watch-later-directory = "${config.xdg.cacheHome}/mpv-watch-later/";
          cache = "auto";
          # cache-pause-initial = "yes";
          cache-pause-wait = "3";
          cache-on-disk = "yes";
          cache-dir = "~/.cache/mpv";
          demuxer-readahead-secs = 125;
          demuxer-max-bytes = "10GiB";
        };

        # bindings = {
        #   # Basics
        #   SPACE = "cycle pause";
        #   "Alt+ENTER" = "cycle fullscreen";
        #   "Alt+x" = "quit-watch-later";
        #   "1" = "cycle border";
        #   "Ctrl+a" = "cycle ontop";
        #   n = ''show-text ''${media-title}'';
        #   MBTN_LEFT = "cycle pause";
        #   MBTN_LEFT_DBL = "cycle fullscreen";
        #   MBTN_RIGHT = "ignore";
        #   z = "script-binding Blackbox";

        #   # Video
        #   v = "cycle sub-visibility";
        #   "Ctrl+LEFT" = "sub-seek -1";
        #   "Ctrl+RIGHT" = "sub-seek 1";
        #   PGUP = "playlist-next; write-watch-later-config";
        #   PGDWN = "playlist-prev; write-watch-later-config";
        #   "Alt+1" = "set window-scale 0.5";
        #   "Alt+2" = "set window-scale 1.0";
        #   "Alt+3" = "set window-scale 2.0";
        #   "Alt+i" = "screenshot";
        #   s = "ignore";

        #   # Audio
        #   UP = "add volume +5";
        #   DOWN = "add volume -5";
        #   WHEEL_UP = "add volume +5";
        #   WHEEL_DOWN = "add volume -5";
        #   "+" = "add audio-delay 0.100";
        #   "-" = "add audio-delay -0.100";
        #   a = "cycle audio";
        #   "Shift+a" = "cycle audio down";
        #   "Ctrl+M" = "cycle mute";
        #   "=" = ''af toggle "lavfi=[pan=1c|c0=0.5*c0+0.5*c1]" ; show-text "Audio mix set to Mono"'';

        #   # Frame-step
        #   ">" = "frame-step";
        #   "<" = "frame-back-step";
        # };
      };

      xdg = lib.mkIf config.ncfg.xdg.enable {
        configFile = {
          ## Add scripts distributed with mpv
          ## Commented out autocrop because it shows a text overlay the first 3 seconds of starting mpv
          # "autocrop.lua" = {
          #   source = "${pkgs.mpv-unwrapped.src.outPath}/TOOLS/lua/autocrop.lua";
          #   target = "mpv/scripts/autocrop.lua";
          # };
          "autodeint.lua" = {
            source = "${pkgs.mpv-unwrapped.src.outPath}/TOOLS/lua/autodeint.lua";
            target = "mpv/scripts/autodeint.lua";
          };
        };
        mimeApps.defaultApplications = {
          "application/mxf" = mkForce dftApp;
          "application/ogg" = mkForce dftApp;
          "application/sdp" = mkForce dftApp;
          "application/smil" = mkForce dftApp;
          "application/streamingmedia" = mkForce dftApp;
          "application/vnd.apple.mpegurl" = mkForce dftApp;
          "application/vnd.ms-asf" = mkForce dftApp;
          "application/vnd.rn-realmedia" = mkForce dftApp;
          "application/vnd.rn-realmedia-vbr" = mkForce dftApp;
          "application/x-cue" = mkForce dftApp;
          "application/x-extension-m4a" = mkForce dftApp;
          "application/x-extension-mp4" = mkForce dftApp;
          "application/x-matroska" = mkForce dftApp;
          "application/x-mpegurl" = mkForce dftApp;
          "application/x-ogg" = mkForce dftApp;
          "application/x-ogm" = mkForce dftApp;
          "application/x-ogm-audio" = mkForce dftApp;
          "application/x-ogm-video" = mkForce dftApp;
          "application/x-shorten" = mkForce dftApp;
          "application/x-smil" = mkForce dftApp;
          "application/x-streamingmedia" = mkForce dftApp;
          "audio/3gpp" = mkForce dftApp;
          "audio/3gpp2" = mkForce dftApp;
          "audio/aac" = mkForce dftApp;
          "audio/ac3" = mkForce dftApp;
          "audio/aiff" = mkForce dftApp;
          "audio/AMR" = mkForce dftApp;
          "audio/amr-wb" = mkForce dftApp;
          "audio/dv" = mkForce dftApp;
          "audio/eac3" = mkForce dftApp;
          "audio/flac" = mkForce dftApp;
          "audio/m3u" = mkForce dftApp;
          "audio/m4a" = mkForce dftApp;
          "audio/mp1" = mkForce dftApp;
          "audio/mp2" = mkForce dftApp;
          "audio/mp3" = mkForce dftApp;
          "audio/mp4" = mkForce dftApp;
          "audio/mpeg" = mkForce dftApp;
          "audio/mpeg2" = mkForce dftApp;
          "audio/mpeg3" = mkForce dftApp;
          "audio/mpegurl" = mkForce dftApp;
          "audio/mpg" = mkForce dftApp;
          "audio/musepack" = mkForce dftApp;
          "audio/ogg" = mkForce dftApp;
          "audio/opus" = mkForce dftApp;
          "audio/rn-mpeg" = mkForce dftApp;
          "audio/scpls" = mkForce dftApp;
          "audio/vnd.dolby.heaac.1" = mkForce dftApp;
          "audio/vnd.dolby.heaac.2" = mkForce dftApp;
          "audio/vnd.dts" = mkForce dftApp;
          "audio/vnd.dts.hd" = mkForce dftApp;
          "audio/vnd.rn-realaudio" = mkForce dftApp;
          "audio/vorbis" = mkForce dftApp;
          "audio/wav" = mkForce dftApp;
          "audio/webm" = mkForce dftApp;
          "audio/x-aac" = mkForce dftApp;
          "audio/x-adpcm" = mkForce dftApp;
          "audio/x-aiff" = mkForce dftApp;
          "audio/x-ape" = mkForce dftApp;
          "audio/x-m4a" = mkForce dftApp;
          "audio/x-matroska" = mkForce dftApp;
          "audio/x-mp1" = mkForce dftApp;
          "audio/x-mp2" = mkForce dftApp;
          "audio/x-mp3" = mkForce dftApp;
          "audio/x-mpegurl" = mkForce dftApp;
          "audio/x-mpg" = mkForce dftApp;
          "audio/x-ms-asf" = mkForce dftApp;
          "audio/x-ms-wma" = mkForce dftApp;
          "audio/x-musepack" = mkForce dftApp;
          "audio/x-pls" = mkForce dftApp;
          "audio/x-pn-au" = mkForce dftApp;
          "audio/x-pn-realaudio" = mkForce dftApp;
          "audio/x-pn-wav" = mkForce dftApp;
          "audio/x-pn-windows-pcm" = mkForce dftApp;
          "audio/x-realaudio" = mkForce dftApp;
          "audio/x-scpls" = mkForce dftApp;
          "audio/x-shorten" = mkForce dftApp;
          "audio/x-tta" = mkForce dftApp;
          "audio/x-vorbis" = mkForce dftApp;
          "audio/x-vorbis+ogg" = mkForce dftApp;
          "audio/x-wav" = mkForce dftApp;
          "audio/x-wavpack" = mkForce dftApp;
          "video/3gp" = mkForce dftApp;
          "video/3gpp" = mkForce dftApp;
          "video/3gpp2" = mkForce dftApp;
          "video/avi" = mkForce dftApp;
          "video/divx" = mkForce dftApp;
          "video/dv" = mkForce dftApp;
          "video/fli" = mkForce dftApp;
          "video/flv" = mkForce dftApp;
          "video/mkv" = mkForce dftApp;
          "video/mp2t" = mkForce dftApp;
          "video/mp4" = mkForce dftApp;
          "video/mp4v-es" = mkForce dftApp;
          "video/mpeg" = mkForce dftApp;
          "video/msvideo" = mkForce dftApp;
          "video/ogg" = mkForce dftApp;
          "video/quicktime" = mkForce dftApp;
          "video/vnd.divx" = mkForce dftApp;
          "video/vnd.mpegurl" = mkForce dftApp;
          "video/vnd.rn-realvideo" = mkForce dftApp;
          "video/webm" = mkForce dftApp;
          "video/x-avi" = mkForce dftApp;
          "video/x-flc" = mkForce dftApp;
          "video/x-flic" = mkForce dftApp;
          "video/x-flv" = mkForce dftApp;
          "video/x-m4v" = mkForce dftApp;
          "video/x-matroska" = mkForce dftApp;
          "video/x-mpeg2" = mkForce dftApp;
          "video/x-mpeg3" = mkForce dftApp;
          "video/x-ms-afs" = mkForce dftApp;
          "video/x-ms-asf" = mkForce dftApp;
          "video/x-ms-wmv" = mkForce dftApp;
          "video/x-ms-wmx" = mkForce dftApp;
          "video/x-ms-wvxvideo" = mkForce dftApp;
          "video/x-msvideo" = mkForce dftApp;
          "video/x-ogm" = mkForce dftApp;
          "video/x-ogm+ogg" = mkForce dftApp;
          "video/x-theora" = mkForce dftApp;
          "video/x-theora+ogg" = mkForce dftApp;
        };
      };
    };
  };
}

3 Likes

I have one file per host and one shared. I also have a small collections of functions to make nginx and systemd services less boilerplate.

3 Likes

I do the same but slightly different and less organised. Seemed like the most sensible to me.

How do you import the custom modules in the system configs? I do the equivalent of putting ../../modules into every config but I kinda don’t like the boilerplate.

2 Likes

I organise mine as well with modules and I have some configuration which the define roles for a system. E.g. desktop or headless for servers.

2 Likes

For myself I religiously mirror the canonical output schema that flakes provides in my directory structure.

(Because it’s the only “obvious” and complete, agreed upon schema that everyone needs to follow anyway. Closest thing we have to a “universal language”, so to speak.)

E.g. suppose the flake outputs looks like this

outputs = { self, ... }: {
  apps = { ... };
  bundlers = { ... };
  devShells = { ... };
  formatters = { ... };
  lib = { ... };
  nixosConfigurations = { ... };
  nixosModules = { ... };
  packages = { ... };
}

Then I’ll either do it directly in the root if it’s a nix-only project:

.
├── flake.lock
├── flake.nix
├── apps
│   └── ...
├── bundlers
│   └── ...
├── dev-shells
│   └── ...
├── formatters
│   └── ...
├── lib
│   └── ...
├── nixos-configurations
│   └── ...
├── nixos-modules
│   └── ...
└── packages
    └── ...

Or I’ll nest it under ./nix if it’s a broader project that is not specific to nix (e.g. there’s a src with a different language)

.
├── flake.lock
├── flake.nix
├── src
└── nix
    ├── apps
    │   └── ...
    ├── bundlers
    │   └── ...
    ├── dev-shells
    │   └── ...
    ├── formatters
    │   └── ...
    ├── lib
    │   └── ...
    ├── nixos-configurations
    │   └── ...
    ├── nixos-modules
    │   └── ...
    └── packages
        └── ...

If I feel like I really need to group some things together more, then I tend to create a new flake because I think it’s a strong signal that this could potentially be broken out as new a standalone git repository.

Unfortunately sub-flakes has some practical problems at the moment, but I put up with it and organize things this way:

.
├── flake.lock
├── flake.nix
├── sub-flake1
|   ├── flake.nix
|   ├── flake.lock
|   ├── nixos-configurations
|   │   └── ...
|   └── nixos-modules
|       └── ...
├── sub-flake2
|   ├── flake.nix
|   ├── flake.lock
|   ├── nixos-configurations
|   │   └── ...
|   └── nixos-modules
|       └── ...
└── dev-shells
    └── ...

(This is what it looks like in practice: GitHub - rehno-lindeque/wip: A work-in-progress, into perpetuity ❤)

4 Likes

I have a function like this that generates nixosConfigurations flake outputs:

    defaultSystem = systemModules:
      nixpkgs.lib.nixosSystem {
        inherit pkgs system;
        modules = systemModules ++ [
          (
            {modulesPath, ...}: {
              imports = (import ./modules) ++ ["${modulesPath}/installer/scan/not-detected.nix"];
            }
          )
        ];
      };

and modules/default.nix looks like this:

[
  ./bluetooth.nix
  ./common.nix
  ./dosbox.nix
  ./efi.nix
  ./graphical.nix
  ./laptop.nix
  ./mount-data.nix
  ./printing.nix
  ./security.nix
  ./server.nix
  ./ssh.nix
  ./user.nix
]
2 Likes