Modularity The NixOS configuration mechanism is modular. If your configuration.nix becomes too big, you can split it into multiple files. Likewise, if you have multiple NixOS configurations (e.g. for different computers) with some commonality, you can move the common configuration into a shared file. Modules have exactly the same syntax as configuration.nix. In fact, configuration.nix is itself a module. You can use other modules by including them from configuration.nix, e.g.: { config, pkgs, ... }: { imports = [ ./vpn.nix ./kde.nix ]; services.httpd.enable = true; environment.systemPackages = [ pkgs.emacs ]; ... } Here, we include two modules from the same directory, vpn.nix and kde.nix. The latter might look like this: { config, pkgs, ... }: { services.xserver.enable = true; services.xserver.displayManager.sddm.enable = true; services.xserver.desktopManager.plasma5.enable = true; } Note that both configuration.nix and kde.nix define the option . When multiple modules define an option, NixOS will try to merge the definitions. In the case of , that’s easy: the lists of packages can simply be concatenated. The value in configuration.nix is merged last, so for list-type options, it will appear at the end of the merged list. If you want it to appear first, you can use mkBefore: boot.kernelModules = mkBefore [ "kvm-intel" ]; This causes the kvm-intel kernel module to be loaded before any other kernel modules. For other types of options, a merge may not be possible. For instance, if two modules define , nixos-rebuild will give an error: The unique option `services.httpd.adminAddr' is defined multiple times, in `/etc/nixos/httpd.nix' and `/etc/nixos/configuration.nix'. When that happens, it’s possible to force one definition take precedence over the others: services.httpd.adminAddr = pkgs.lib.mkForce "bob@example.org"; When using multiple modules, you may need to access configuration values defined in other modules. This is what the config function argument is for: it contains the complete, merged system configuration. That is, config is the result of combining the configurations returned by every moduleIf you’re wondering how it’s possible that the (indirect) result of a function is passed as an input to that same function: that’s because Nix is a “lazy” language — it only computes values when they are needed. This works as long as no individual configuration value depends on itself.. For example, here is a module that adds some packages to only if is set to true somewhere else: { config, pkgs, ... }: { environment.systemPackages = if config.services.xserver.enable then [ pkgs.firefox pkgs.thunderbird ] else [ ]; } With multiple modules, it may not be obvious what the final value of a configuration option is. The command allows you to find out: $ nixos-option services.xserver.enable true $ nixos-option boot.kernelModules [ "tun" "ipv6" "loop" ... ] Interactive exploration of the configuration is possible using nix-repl, a read-eval-print loop for Nix expressions. It’s not installed by default; run nix-env -i nix-repl to get it. A typical use: $ nix-repl '<nixpkgs/nixos>' nix-repl> config.networking.hostName "mandark" nix-repl> map (x: x.hostName) config.services.httpd.virtualHosts [ "example.org" "example.gov" ]