I found a working approach to make a flake-based NixOS configuration self-contained (being able to rebuild offline as much as possible). Comparing with expensive system.includeBuildDependencies(https://discourse.nixos.org/t/rebuild-nixos-offline/3679/11), which can make the system derivation to take up a very large amount of space, this approach preserves minimal dependencies needed to build OS derivation when there are only minor changes in the system configuration, and no new packages are introduced.
In a NixOS flake, pass all flake inputs into the NixOS module as a flake-inputs specialArg:
{
inputs = { ... };
outputs =
{ ... }@inputs:
{
nixosConfigurations.xxx = nixpkgs.lib.nixosSystem {
...
specialArgs = {
flake-inputs = inputs;
};
};
};
}
Recursively collect flake inputs (and inputs of inputs, inputs of inputs of inputs, …) into allFlakeInputsClosure and add their store paths to system.extraDependencies:
{
config,
lib,
pkgs,
flake-inputs,
...
}:
{
system.extraDependencies =
let
filterValidInputs = lib.filterAttrs (n: v: n != "self" && builtins.isAttrs v && v ? outPath);
startSet = lib.mapAttrsToList (name: input: {
key = input.outPath;
value = input;
}) (filterValidInputs flake-inputs);
allFlakeInputsClosure = builtins.genericClosure {
inherit startSet;
operator =
{ value, ... }:
lib.mapAttrsToList (name: input: {
key = input.outPath;
value = input;
}) (filterValidInputs (value.inputs or { }));
};
allFlakeInputs = builtins.map (node: node.value) allFlakeInputsClosure;
in
allFlakeInputs;
}
Enable keep-derivations and keep-outputs so the GC will retain build-time derivations and their outputs that are referenced by the system derivation but not directly referenced by system closure. This prevents transient evaluation/build-time dependencies from being garbage-collected and therefore prevents unexpected refetches during an offline nixos-rebuild:
nix.settings.keep-derivations = true;
nix.settings.keep-outputs = true;
Add --offline and --option substitute false when building OS to force the rebuild to use only local store paths and to avoid contacting binary caches for substitutes:
sudo nixos-rebuild switch --offline --option substitute false
Now, with the closure of flake inputs recorded and these Nix settings enabled, the system’s build-time dependencies that were previously realized will survive GC, minor parameter tweaks won’t pull new network artifacts, which makes offline nixos-rebuild practical for the common, small-change workflows (while still requiring an initial online bootstrap or an explicit store/cache copy when new inputs are introduced).
Hope this helps.