How to build, install and update a nixos iso/system from a single (remote) flake?

Hello community,

i have reached my pain tolerance, which is why I am turning to you with my first and quite broad question. Sorry.

My goals are:

  • Have a central github repository with a flake that defines all nixos systems of my devices, starting with “laptop2” for now.
  • Generate an installation ISO from that flake that installs the customized nixos according to nixosConfiguration.laptop2 on target device.
  • This nixos system refers itself to github:repo#laptop2 to fetch and build future updates.

Right now, I have not succeeded past point 2.

Building and installing an ISO from output.nixosConfiguration.laptop2 with a simple imported nix/common.nix module is quite easy. Code below.

The problems I face:

  • nixos-install and nixos-generate-config ignore the configuration of the otherwise correct live-nixos, resulting in a blank nixos installation, nullifying the purpose of the flake.
  • nixos-install –flake “github:repo#laptop2” is redundant, because network access is required for the installation of stuff, that is already known at ISO build time. Furthermore, the resulting nixos is broken, /boot is missing and besides /nix/store, not a single *.nix file exists in /mnt after installation.
  • I have no clue where to start to get a self/remote referential nixosConfiguration.
  • All tutorials begin with a fresh nixos install and edit /etc/nixos/{configuraion.nix,flake.nix}, my starting point is a remote flake without manual changes.

Somewhere in the chain flake nixosConfiguration -> nixos live-installer -> target nixos is a break.
I could continue to investigate why the last step, the nixos-install –flake is incomplete but I have growing suspicion, that somewhere my workflow is fundamentally broken.
I am grateful for any new direction to try or examples to copy.
Thank you for your time.

My code (simplified and unpolished):

flake.nix

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };

  outputs = inputs@{ self, nixpkgs, ... }: {

    nixosConfigurations = {
      laptop2 = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel-no-zfs.nix"
          # TODO reactivate hardened kernel and fix missing ext4 support in live-nixos
          ${nixpkgs}/nixos/modules/profiles/hardened.nix"
          nix/common.nix
        ];
      };
   };
}

nix/common.nix

{ config, lib, pkgs, ... }:                                                                  
{                                                                                                      
  nix.settings.system-features = [ "nix-command" "flakes" "big-parallel" "kvm" ];                
  nix.extraOptions = "experimental-features = nix-command flakes";                               
                                                                                                     
  boot= {                                                                                        
    supportedFilesystems = [ "ext4" ];                                                     
    loader.grub.device = "/dev/sda";
  };

  environment.systemPackages = with pkgs; [
    git
    tmux
    neovim
  ];

  # not supported by flakes. will copy to /etc/nixos/configuration.nix 
  #system.copySystemConfiguration = true;

  isoImage = {
    edition = lib.mkForce "laptop2-nixos";
    isoBaseName = "laptop2-nixos";
    volumeID = "laptop2-nixos";
    contents = [
      { source = pkgs.writeText "install.sh" ''
#!/usr/bin/bash
set -xeuo pipefail

# partitioning primary boot and swap on sda1 and sda2, mounting
# ...

# sudo nixos-generate-config --root /mnt
# sudo nixos-install
sudo nixos-install --flake "github:repo#laptop2" # finds this exact flake
'';
                          target = "install.sh";
      }
    ];
  };
}

gen-iso.sh

nixos-generate --show-trace --flake .#laptop2 --format iso --out-link result
ISO=results/.../file.iso
sudo dd if="$ISO" of=/dev/sda status=progress
sync

Perhaps GitHub - nix-community/nixos-anywhere: install nixos everywhere via ssh [maintainer=@numtide] could help?

1 Like

That you for that suggestion @mightyiam. I took a look at nixos-anywhere and it looks promising but unfortunately only offers remote installation over ssh and i am not willing to compromise on my envisioned self contained install image yet.

I might have some progress about that installer ISO soon, since i just found outputs.nixosConfiguration.laptop2.installer.cloneConfigIncludes = [ ./common.nix ]; which previously only included [ "<nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-minimal-new-kernel-no-zfs.nix>" ] which explains my blank nixos installation.

I will post replies/edits to my question while i am chewing my way through.

1 Like

I managed to reach my goal of a self updating nixos laptop but I failed with the self contained installation medium.

The auto update mechanism works via a systemd service nixos-upgrade.service that can be configured with

system.autoUpgrade = {
  enable = true;
  allowReboot = true;
  dates = "03:00";
  flake = "github:<user>/<flake-repo>";
  flags = [ "-L" "--verbose" "--show-trace" ]; # to get extended build logs
  randomizedDelaySec = "30min";
};

The nixos hostname (networking.hostname = "laptop2") and the flake outputs target name (outputs.nixosConfigurations.laptop2) should be in identical, otherwise the flake = "github..." parameter needs an appended #<target name>, to tell nixos-rebuild the desired nixos configuration. This flake parameter connects a local nixos instance to the central repository.

My attempts at a self contained offline installer ran into several problems:

  • You cannot build a target nixos and its nixos live-usb installer from the same flake output, since there are conflicting boot settings. You need 2 flake outputs laptop2 and laptop2-installer.

  • To copy all the sources of the flakes repository, the best I could do was

    isoImage.contents = [{
      source = self.sourceInfo.outPath;
      target = "/flake_source";
    }];
    

    This copies all git-tracked files into the ISO but unfortunately not the .git directory itself, making it only a current snapshot without any remote ties. This is fine for now and still good progress towards a complete offline installer but works against the spirit of having a complete backup installation medium.

  • In any case, nixos-install tries to resolve https://cache.nixos.org/nix-cache-info and fails with with error: unable to download 'https://.../nixpkgs/archive/...tar.gz'.
    At this point, I am not sure if its even possible to create a fully offline nixos installer.

  • When network access is given, nixos-install copies artifacts from local as from the remote nixos cache. To reduce these remote cache hits, I tried isoImage.storeContents = [ outputs.nixosConfigurations.laptop2 ]; which failed, because JSON was expected.

I didn’t try an further. Maybe some day I will have another shot at the installer.