Compared to the default configuration approach of NixOS, Flakes provide better reproducibility and a clearer package structure that is easier to maintain. Therefore, it is recommended to manage NixOS with Flakes.
However, as Flakes is still an experimental feature currently, it's not enabled by default yet, we need to enable it manually by modifying `/etc/nixos/configuration.nix`, example as follows:
```nix
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running 'nixos-help').
After enabling `flakes`, `sudo nixos-rebuild switch` will try to read`/etc/nixos/flake.nix` first every time you run it, if not found, it will fallback to `/etc/nixos/configuration.nix`.
Now to learn how to write a flake, let's take a look at the official flake templates provided by Nix. First, check which templates are available:
# It is said to be partial because the documentation is not complete, only some simple introductions
# (such is the current state of Nix documentation...)
# A Nix Module can be an attribute set, or a function that returns an attribute set.
# If a Module is a function, this function can only have the following parameters:
#
# lib: the nixpkgs function library, which provides many useful functions for operating Nix expressions
# https://nixos.org/manual/nixpkgs/stable/#id-1.4
# config: all config options of the current flake
# options: all options defined in all NixOS Modules in the current flake
# pkgs: a collection of all packages defined in nixpkgs.
# you can assume its default value is `nixpkgs.legacyPackages."${system}"` for now.
# can be customed by `nixpkgs.pkgs` option
# modulesPath: the default path of nixpkgs's builtin modules folder,
# used to import some extra modules from nixpkgs.
# this parameter is rarely used, you can ignore it for now.
#
# Only these parameters can be passed by default.
# If you need to pass other parameters, you must use `specialArgs` by uncomment the following line
# specialArgs = {...} # pass custom arguments into sub module.
modules = [
# Import the configuration.nix we used before, so that the old configuration file can still take effect.
# Note: /etc/nixos/configuration.nix itself is also a Nix Module, so you can import it directly here
./configuration.nix
];
};
};
};
}
```
Here we defined a NixOS system called `nixos-test`, whose configuration file is `./configuration.nix`, which is the classic configuration we modified before, so we can still make use of it.
Now run `sudo nixos-rebuild switch` to apply the configuration, and no changes will be made to the system, because we imported the old configuration file in `/etc/nixos/flake.nix`, so the actual state we declared remains unchanged.
After the switch, we can now manage the system through Flakes. The most common requirement for managing a system is to install packges. We have seen how to install packages through `environment.systemPackages` before, and these packages are all from the official nixpkgs repository.
Now let's learn how to install packages from other sources through Flakes. This is much more flexible than installing from nixpkgs directly. The most obvious benefit is that you can easily set the version of the software.
With the NixOS's classic configuration method, other cache sources can be added by using `nix-channel`, but Flakes avoids using any system-level configuration and environment variables to ensure that its build results are not affected by the environment(so the build results are reproducible).
Therefore, to customize the cache source, we must add the related configuration in `flake.nix` by using the parameter `nixConfig`. An example is as follows:
```nix
{
description = "NixOS configuration of Ryan Yin";
# 1. To ensure purity, Flakes does not rely on the system's `/etc/nix/nix.conf`, so we have to set related configuration here.
# 2. To ensure security, flake allows only a few nixConfig parameters to be set directly by default.
# you need to add `--accept-flake-config` when executing the nix command,
# otherwise all other parameters will be ignored, and an warning will printed by nix.