Commit 973fbf96 authored by Vincent Haupert's avatar Vincent Haupert
Browse files

nixos/image/repart: allow replacing `/nix/store`

parent 92127274
Loading
Loading
Loading
Loading
+46 −4
Original line number Diff line number Diff line
@@ -40,12 +40,18 @@ An example of how to build an image:
}
```

## Nix Store Partition {#sec-image-repart-store-partition}
## Nix Store Paths {#sec-image-repart-store-paths}

If you want to rewrite Nix store paths, e.g., to remove the `/nix/store` prefix
or to nest it below a parent path, you can do that through the
`nixStorePrefix` option.

### Nix Store Partition {#sec-image-repart-store-partition}

You can define a partition that only contains the Nix store and then mount it
under `/nix/store`. Because the `/nix/store` part of the paths is already
determined by the mount point, you have to set `stripNixStorePrefix = true;` so
that the prefix is stripped from the paths before copying them into the image.
determined by the mount point, you have to set `nixStorePrefix = "/"` so
that `/nix/store` is stripped from the paths before copying them into the image.

```nix
{
@@ -54,7 +60,7 @@ that the prefix is stripped from the paths before copying them into the image.
  image.repart.partitions = {
    "store" = {
      storePaths = [ config.system.build.toplevel ];
      stripNixStorePrefix = true;
      nixStorePrefix = "/";
      repartConfig = {
        Type = "linux-generic";
        Label = "nix-store";
@@ -65,6 +71,42 @@ that the prefix is stripped from the paths before copying them into the image.
}
```

### Nix Store Subvolume {#sec-image-repart-store-subvolume}

Alternatively, you can create a Btrfs subvolume `/@nix-store` containing the
Nix store and mount it on `/nix/store`:

```nix
{
  fileSystems."/" = {
    device = "/dev/disk/by-partlabel/root";
    fsType = "btrfs";
    options = [ "subvol=/@" ];
  };

  fileSystems."/nix/store" = {
    device = "/dev/disk/by-partlabel/root";
    fsType = "btrfs";
    options = [ "subvol=/@nix-store" ];
  };

  image.repart.partitions = {
    "root" = {
      storePaths = [ config.system.build.toplevel ];
      nixStorePrefix = "/@nix-store";
      repartConfig = {
        Type = "root";
        Label = "root";
        Format = "btrfs";
        Subvolumes = "/@ /@nix-store";
        MakeDirectories = "/@ /@nix-store";
        # ...
      };
    };
  };
}
```

## Appliance Image {#sec-image-repart-appliance}

The `image/repart.nix` module can also be used to build self-contained [software
+6 −0
Original line number Diff line number Diff line
@@ -287,9 +287,15 @@
  "sec-image-repart": [
    "index.html#sec-image-repart"
  ],
  "sec-image-repart-store-paths": [
    "index.html#sec-image-repart-store-paths"
  ],
  "sec-image-repart-store-partition": [
    "index.html#sec-image-repart-store-partition"
  ],
  "sec-image-repart-store-subvolume": [
    "index.html#sec-image-repart-store-subvolume"
  ],
  "sec-image-repart-appliance": [
    "index.html#sec-image-repart-appliance"
  ],
+9 −7
Original line number Diff line number Diff line
@@ -36,11 +36,11 @@ def add_contents_to_definition(


def add_closure_to_definition(
    definition: Path, closure: Path | None, strip_nix_store_prefix: bool | None
    definition: Path, closure: Path | None, nix_store_prefix: str | None
) -> None:
    """Add CopyFiles= instructions to a definition for all paths in the closure.

    If strip_nix_store_prefix is True, `/nix/store` is stripped from the target path.
    Replace `/nix/store` with the value of nix_store_prefix.
    """
    if not closure:
        return
@@ -52,10 +52,12 @@ def add_closure_to_definition(
                continue

            source = Path(line.strip())
            target = str(source.relative_to("/nix/store/"))
            target = f":/{target}" if strip_nix_store_prefix else ""
            option = f"CopyFiles={source}"
            if nix_store_prefix:
                target = nix_store_prefix / source.relative_to("/nix/store/")
                option = f"{option}:{target}"

            copy_files_lines.append(f"CopyFiles={source}{target}\n")
            copy_files_lines.append(f"{option}\n")

    with open(definition, "a") as f:
        f.writelines(copy_files_lines)
@@ -102,8 +104,8 @@ def main() -> None:
        add_contents_to_definition(definition, contents)

        closure = config.get("closure")
        strip_nix_store_prefix = config.get("stripNixStorePrefix")
        add_closure_to_definition(definition, closure, strip_nix_store_prefix)
        nix_store_prefix = config.get("nixStorePrefix")
        add_closure_to_definition(definition, closure, nix_store_prefix)

    print(target_dir.absolute())

+87 −70
Original line number Diff line number Diff line
@@ -15,7 +15,9 @@ let

  inherit (utils.systemdUtils.lib) GPTMaxLabelLength;

  partitionOptions = {
  partitionOptions =
    { config, ... }:
    {
      options = {
        storePaths = lib.mkOption {
          type = with lib.types; listOf path;
@@ -23,13 +25,21 @@ let
          description = "The store paths to include in the partition.";
        };

        # Superseded by `nixStorePrefix`. Unfortunately, `mkChangedOptionModule`
        # does not support submodules.
        stripNixStorePrefix = lib.mkOption {
        type = lib.types.bool;
        default = false;
          default = "_mkMergedOptionModule";
          visible = false;
        };

        nixStorePrefix = lib.mkOption {
          type = lib.types.path;
          default = "/nix/store";
          description = ''
          Whether to strip `/nix/store/` from the store paths. This is useful
          when you want to build a partition that only contains store paths and
          is mounted under `/nix/store`.
            The prefix to use for store paths. Defaults to `/nix/store`. This is
            useful when you want to build a partition that only contains store
            paths and is mounted under `/nix/store` or if you want to create the
            store paths below a parent path (e.g., `/@nix/nix/store`).
          '';
        };

@@ -77,6 +87,10 @@ let
          '';
        };
      };

      config = lib.mkIf (config.stripNixStorePrefix == true) {
        nixStorePrefix = "/";
      };
    };

  mkfsOptionsToEnv =
@@ -350,7 +364,7 @@ in
          }
        ) cfg.partitions;

        warnings = lib.filter (v: v != null) (
        warnings = lib.flatten (
          lib.mapAttrsToList (
            fileName: partitionConfig:
            let
@@ -358,8 +372,7 @@ in
              suggestedMaxLabelLength = GPTMaxLabelLength - 2;
              labelLength = builtins.stringLength repartConfig.Label;
            in
            if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
              ''
            lib.optional (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) ''
              The partition label '${repartConfig.Label}'
              defined for '${fileName}' is ${toString labelLength} characters long.
              The suggested maximum label length is ${toString suggestedMaxLabelLength}.
@@ -370,8 +383,12 @@ in
              ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
              you're at version 9, you cannot increment this to 10.
            ''
            else
              null
            ++ lib.optional (partitionConfig.stripNixStorePrefix != "_mkMergedOptionModule") ''
              The option definition `image.repart.paritions.${fileName}.stripNixStorePrefix`
              has changed to `image.repart.paritions.${fileName}.nixStorePrefix` and now
              accepts the path to use as prefix directly. Use `nixStorePrefix = "/"` to
              achieve the same effect as setting `stripNixStorePrefix = true`.
            ''
          ) cfg.partitions
        );
      };