diff --git a/README.md b/README.md index 154ad70..78eff92 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,32 @@ also disables the default shell-oriented `amazon-init` service and enables a small generic service that treats EC2 user-data as `/etc/nixos/flake.nix`, then runs `nixos-rebuild switch --flake /etc/nixos`. +## First-Boot Build Cache Hints + +The first user-data switch is built by the AMI's existing Nix configuration, +before the target NixOS system has activated its own `nix.settings`. A caller +flake can optionally expose `quixosEarlyBuildNixConfig` to make that first +build use known binary caches without baking any project-specific cache into +the AMI: + +```nix +{ + quixosEarlyBuildNixConfig = { + hosts = { + "2600:..." = [ "nix-cache.internal.example.org" ]; + }; + substituters = [ "http://nix-cache.internal.example.org/cache" ]; + trustedPublicKeys = [ "nix-cache.internal.example.org-1:..." ]; + }; +} +``` + +The AMI runner evaluates this output after writing `/etc/nixos/flake.nix`, +appends the host hints to `/etc/hosts`, and passes the substituters and trusted +public keys through `NIX_CONFIG` only for the first `nixos-rebuild switch`. +The target NixOS configuration should still declare the same cache in +`nix.settings` for all later rebuilds. + ## Publish The normal flow builds on a temporary EC2 instance, so your local machine does diff --git a/user-data.sh.tftpl b/user-data.sh.tftpl index 5a309d5..68c0995 100644 --- a/user-data.sh.tftpl +++ b/user-data.sh.tftpl @@ -75,7 +75,7 @@ write_config() { wantedBy = [ "multi-user.target" ]; after = [ "fetch-ec2-metadata.service" "network-online.target" "nix-daemon.socket" ]; wants = [ "fetch-ec2-metadata.service" "network-online.target" "nix-daemon.socket" ]; - path = [ pkgs.coreutils pkgs.git pkgs.systemd ]; + path = [ pkgs.coreutils pkgs.git pkgs.jq pkgs.nix pkgs.systemd ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; @@ -102,11 +102,41 @@ write_config() { install -m 0644 "$user_data" /etc/nixos/flake.nix + early_build_config=/run/nixos-user-data-flake-early-build-config.json + early_build_error=/run/nixos-user-data-flake-early-build-config.err + early_nix_config=/run/nixos-user-data-flake-early-nix.conf + : > "$early_nix_config" + + if nix --extra-experimental-features 'nix-command flakes' \ + eval --json /etc/nixos#quixosEarlyBuildNixConfig \ + > "$early_build_config" 2> "$early_build_error"; then + jq -r '(.hosts // {}) | to_entries[] | select(.key != "" and (.value | length > 0)) | [.key, (.value | join(" "))] | @tsv' "$early_build_config" | + while IFS="$(printf '\t')" read -r address names; do + if [ -n "$address" ] && [ -n "$names" ]; then + printf '%s %s # quixos early build nix cache\n' "$address" "$names" >> /etc/hosts + fi + done + + substituters="$(jq -r '(.substituters // []) | join(" ")' "$early_build_config")" + trusted_public_keys="$(jq -r '(.trustedPublicKeys // []) | join(" ")' "$early_build_config")" + if [ -n "$substituters" ]; then + printf 'extra-substituters = %s\n' "$substituters" >> "$early_nix_config" + fi + if [ -n "$trusted_public_keys" ]; then + printf 'extra-trusted-public-keys = %s\n' "$trusted_public_keys" >> "$early_nix_config" + fi + else + echo "No quixosEarlyBuildNixConfig flake output found; first switch will use the AMI Nix configuration." + fi + runner=/run/nixos-user-data-flake-apply cat > "$runner" < "$applied_hash_file"