├── .envrc ├── .gitignore ├── COPYING ├── README.md ├── base.nix ├── buildHost.nix ├── flake.lock ├── flake.nix ├── modules ├── filesystems.nix ├── generic.nix ├── minimize.nix ├── network.nix ├── partitions.nix └── sysupdate.nix ├── version-17.nix └── version-18.nix /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /result* 2 | /.direnv 3 | /.*.img 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Cyberus Technology GmbH 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # systemd-repart / systemd-sysupdate Example 2 | 3 | This repository contains an example of how to use 4 | [systemd-repart](https://www.freedesktop.org/software/systemd/man/latest/systemd-repart.html#) 5 | and 6 | [systemd-sysupdate](https://www.freedesktop.org/software/systemd/man/latest/systemd-sysupdate.html) 7 | to build immutable system images that can be updated over the 8 | Internet. 9 | 10 | See [x86.lol](https://x86.lol/) for blog posts about this setup: 11 | 12 | 1. [Immutable Systems: NixOS + systemd-repart + systemd-sysupdate](https://x86.lol/generic/2024/08/28/systemd-sysupdate.html) 13 | 1. [Immutable Systems: Cross-Compiling for RISC-V using Flakes](https://x86.lol/generic/2024/09/21/cross-compile-riscv.html) 14 | 15 | ## How to Build the System Image 16 | 17 | You need [Nix](https://nixos.org/) with enabled 18 | [Flakes](https://wiki.nixos.org/wiki/Flakes). After that, you can 19 | build the disk image: 20 | 21 | ```console 22 | $ nix build . 23 | ``` 24 | 25 | After the build process, there will be a QEMU disk image `disk.qcow2` 26 | in `result/`. 27 | 28 | ## Running the Demo 29 | 30 | You can boot `result/disk.qcow2` produced in the previous step in any 31 | virtualization solution that supports UEFI. For simplicity, you can 32 | enter a development shell and boot it with the convenience script 33 | `qemu-efi`: 34 | 35 | ```console 36 | # This will take some time, because it compiles quite a bunch of stuff for 37 | # RISC-V and ARM AArch64. 38 | $ nix develop 39 | 40 | $ qemu-efi x86_64 result/disk.qcow2 41 | ``` 42 | 43 | ## Experimenting with the Demo 44 | 45 | Once you have the demo running, you can see systemd-sysupdate and sysupdate-repart in action: 46 | 47 | (I have removed parts of the console output for readability.) 48 | 49 | ```console 50 | # List the update files. 51 | % ls -lh /var/updates/ 52 | total 324M 53 | -r--r--r-- 1 root root 43M Aug 11 15:47 appliance_18.efi.xz 54 | -r--r--r-- 1 root root 282M Aug 11 15:47 store_18.img.xz 55 | 56 | # See that systemd-sysupdate has found an update. 57 | % systemd-sysupdate 58 | VERSION INSTALLED AVAILABLE ASSESSMENT 59 | ↻ 18 ✓ candidate 60 | ● 17 ✓ current 61 | 62 | # Apply the update. 63 | % systemd-sysupdate update 64 | Selected update '18' for install. 65 | Making room for 1 updates… 66 | Removed no instances. 67 | ⤵️ Acquiring /var/updates/appliance_18.efi.xz → /boot/EFI/Linux/appliance_18.efi... 68 | Importing '/var/updates/appliance_18.efi.xz', saving as '/boot/EFI/Linux/.#sysupdateappliance_18.efifce0abb2fdba79a5'. 69 | [...] 70 | Successfully acquired '/var/updates/appliance_18.efi.xz'. 71 | ⤵️ Acquiring /var/updates/store_18.img.xz → /proc/self/fd/3p2... 72 | Importing '/var/updates/store_18.img.xz', saving at offset 269484032 in '/dev/sda'. 73 | [...] 74 | Successfully acquired '/var/updates/store_18.img.xz'. 75 | Successfully installed '/var/updates/appliance_18.efi.xz' (regular-file) as '/boot/EFI/Linux/appliance_18.efi' (regular-file). 76 | Successfully installed '/var/updates/store_18.img.xz' (regular-file) as '/proc/self/fd/3p2' (partition). 77 | ✨ Successfully installed update '18'. 78 | 79 | # Reboot into the new version. 80 | % reboot 81 | ``` 82 | 83 | Once the system is back up, you can remove the last version. This would also happen automatically when the next version is installed. 84 | 85 | ```console 86 | % systemd-sysupdate vacuum -m 1 87 | ``` 88 | 89 | Looking at the partition layout using `parted` before and after the update is also interesting! 90 | -------------------------------------------------------------------------------- /base.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | imports = [ 3 | ./modules/minimize.nix 4 | ./modules/generic.nix 5 | ./modules/filesystems.nix 6 | ./modules/partitions.nix 7 | ./modules/network.nix 8 | ./modules/sysupdate.nix 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /buildHost.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | let 3 | # A helper script to run the disk images above. 4 | qemu-efi = pkgs.writeShellApplication { 5 | name = "qemu-efi"; 6 | 7 | runtimeInputs = [ pkgs.qemu ]; 8 | 9 | text = '' 10 | if [ $# -lt 2 ]; then 11 | echo "Usage: qemu-efi ARCH disk-image [qemu-args...]" >&2 12 | exit 1 13 | fi 14 | 15 | ARCH="$1" 16 | DISK="$2" 17 | shift; shift 18 | 19 | 20 | case "$ARCH" in 21 | x86_64) 22 | qemu-system-x86_64 \ 23 | -smp 2 -m 2048 -machine q35,accel=kvm \ 24 | -bios "${pkgs.OVMF.fd}/FV/OVMF.fd" \ 25 | -snapshot \ 26 | -serial stdio -hda "$DISK" "$@" 27 | ;; 28 | aarch64) 29 | if [ ! -f .aarch-efi.img ]; then 30 | cat "${pkgs.pkgsCross.aarch64-multiplatform.OVMF.fd}/FV/QEMU_EFI.fd" > .aarch-efi.img 31 | truncate -s 64m .aarch-efi.img 32 | fi 33 | 34 | if [ ! -f .aarch-var.img ]; then 35 | cat "${pkgs.pkgsCross.aarch64-multiplatform.OVMF.fd}/FV/QEMU_VARS.fd" > .aarch-var.img 36 | truncate -s 64m .aarch-var.img 37 | fi 38 | 39 | # See https://ubuntu.com/server/docs/boot-arm64-virtual-machines-on-qemu 40 | qemu-system-aarch64 -machine virt -smp 2 -m 2048 -cpu max \ 41 | -serial stdio \ 42 | -drive if=pflash,format=raw,file=.aarch-efi.img,readonly=on \ 43 | -drive if=pflash,format=raw,file=.aarch-var.img \ 44 | -drive if=none,file="$DISK",id=hd,snapshot=on \ 45 | -device virtio-blk-device,drive=hd -device VGA \ 46 | "$@" 47 | ;; 48 | riscv64) 49 | if [ ! -f .riscv-efi.img ]; then 50 | cat "${pkgs.pkgsCross.riscv64.OVMF.fd}/FV/RISCV_VIRT_CODE.fd" > .riscv-efi.img 51 | truncate -s 32m .riscv-efi.img 52 | fi 53 | 54 | if [ ! -f .riscv-var.img ]; then 55 | cat "${pkgs.pkgsCross.riscv64.OVMF.fd}/FV/RISCV_VIRT_VARS.fd" > .riscv-var.img 56 | truncate -s 32m .riscv-var.img 57 | fi 58 | 59 | # See https://github.com/tianocore/edk2/blob/master/OvmfPkg/RiscVVirt/README.md 60 | qemu-system-riscv64 \ 61 | -M virt,pflash0=pflash0,pflash1=pflash1,acpi=off \ 62 | -m 4096 -smp 2 \ 63 | -serial stdio \ 64 | -device virtio-gpu-pci \ 65 | -device qemu-xhci \ 66 | -device usb-kbd \ 67 | -device virtio-rng-pci \ 68 | -blockdev node-name=pflash0,driver=file,read-only=on,filename=.riscv-efi.img \ 69 | -blockdev node-name=pflash1,driver=file,filename=.riscv-var.img \ 70 | -netdev user,id=net0 \ 71 | -device virtio-net-pci,netdev=net0 \ 72 | -device virtio-blk-device,drive=hd0 \ 73 | -drive if=none,file="$DISK",id=hd0,snapshot=on \ 74 | "$@" 75 | ;; 76 | *) 77 | echo "Unknown architecture: $ARCH" >&2 78 | exit 1 79 | ;; 80 | esac 81 | ''; 82 | }; 83 | in 84 | { 85 | devShells.default = pkgs.mkShell { 86 | packages = [ 87 | qemu-efi 88 | ]; 89 | }; 90 | 91 | packages = { 92 | inherit qemu-efi; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1729413321, 24 | "narHash": "sha256-I4tuhRpZFa6Fu6dcH9Dlo5LlH17peT79vx1y1SpeKt0=", 25 | "owner": "nixos", 26 | "repo": "nixpkgs", 27 | "rev": "1997e4aa514312c1af7e2bda7fad1644e778ff26", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "nixos", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "systemd-sysupdate / systemd-repart Example"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { self, nixpkgs, flake-utils, ... }: 10 | let 11 | inherit (nixpkgs) lib; 12 | 13 | # The platform we want to build on. This should ideally be configurable. 14 | buildPlatform = "x86_64-linux"; 15 | 16 | # We use this to build derivations for the build platform. 17 | buildPkgs = nixpkgs.legacyPackages."${buildPlatform}"; 18 | in 19 | (flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" "riscv64-linux" ] (system: 20 | let 21 | # We use this later to add some extra outputs for the build system. 22 | isBuildPlatform = system == buildPlatform; 23 | 24 | # We treat everything as cross-compilation without a special 25 | # case for isBuildSystem. Nixpkgs will do the right thing. 26 | crossPkgs = import nixpkgs { localSystem = buildPlatform; crossSystem = system; }; 27 | 28 | # A convenience wrapper around lib.nixosSystem that configures 29 | # cross-compilation. 30 | crossNixos = module: lib.nixosSystem { 31 | modules = [ 32 | module 33 | 34 | { 35 | # We could also use these to trigger cross-compilation, 36 | # but we already have the ready-to-go crossPkgs. 37 | # 38 | # nixpkgs.buildPlatform = buildSystem; 39 | # nixpkgs.hostPlatform = system; 40 | nixpkgs.pkgs = crossPkgs; 41 | } 42 | ]; 43 | }; 44 | in 45 | # Some outputs only make sense for the build system, e.g. the development shell. 46 | (lib.optionalAttrs isBuildPlatform (import ./buildHost.nix { pkgs = buildPkgs; })) 47 | // 48 | { 49 | packages = 50 | let 51 | appliance_17 = crossNixos { 52 | imports = [ 53 | ./base.nix 54 | ./version-17.nix 55 | ]; 56 | 57 | # To avoid having to prepare an update server, we just drop 58 | # an update into the filesystem. 59 | systemd.services.update-prepare-debug = { 60 | description = "Prepare a fake update"; 61 | wantedBy = [ "multi-user.target" ]; 62 | after = [ "local-fs.target" ]; # Ensures the script runs after file systems are mounted. 63 | requires = [ "local-fs.target" ]; # Ensure file systems are mounted. 64 | 65 | script = '' 66 | # We configured systemd-sysupdate to look for updates here. 67 | mkdir /var/updates 68 | 69 | # We can't symlink the update package. systemd-sysupdate doesn't like that. 70 | cp ${self.packages."${system}".appliance_18_update}/* /var/updates 71 | ''; 72 | 73 | serviceConfig = { 74 | Type = "oneshot"; # Ensures the service runs once and then exits. 75 | }; 76 | }; 77 | 78 | system.image.version = "17"; 79 | }; 80 | 81 | appliance_18 = crossNixos { 82 | imports = [ 83 | ./base.nix 84 | ./version-18.nix 85 | ]; 86 | 87 | system.image.version = "18"; 88 | }; 89 | in 90 | { 91 | default = self.packages."${system}".appliance_17_image; 92 | 93 | appliance_17_image = self.lib.mkInstallImage appliance_17; 94 | appliance_17_update = self.lib.mkUpdate appliance_17; 95 | 96 | appliance_18_image = self.lib.mkInstallImage appliance_18; 97 | appliance_18_update = self.lib.mkUpdate appliance_18; 98 | }; 99 | })) // { 100 | lib = { 101 | # Prepare an update package for the system. 102 | mkUpdate = nixos: 103 | let 104 | config = nixos.config; 105 | in 106 | buildPkgs.runCommand "update-${config.system.image.version}" 107 | { 108 | nativeBuildInputs = with buildPkgs; [ xz ]; 109 | } '' 110 | mkdir -p $out 111 | xz -1 -cz ${config.system.build.uki}/${config.system.boot.loader.ukiFile} \ 112 | > $out/${config.system.boot.loader.ukiFile}.xz 113 | xz -1 -cz ${config.system.build.image}/${config.boot.uki.name}_${config.system.image.version}.store.raw \ 114 | > $out/store_${config.system.image.version}.img.xz 115 | ''; 116 | 117 | # Prepare a ready-to-boot disk image. 118 | mkInstallImage = nixos: 119 | let 120 | config = nixos.config; 121 | in 122 | buildPkgs.runCommand "image-${config.system.image.version}" 123 | { 124 | nativeBuildInputs = with buildPkgs; [ qemu ]; 125 | } '' 126 | mkdir -p $out 127 | qemu-img convert -f raw -O qcow2 \ 128 | -C ${config.system.build.image}/${config.boot.uki.name}_${config.system.image.version}.raw \ 129 | $out/disk.qcow2 130 | ''; 131 | }; 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /modules/filesystems.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: { 2 | 3 | zramSwap = { 4 | # TODO zram-generator fails to compile for ARM. 5 | enable = false; 6 | 7 | algorithm = "zstd"; 8 | memoryPercent = 20; 9 | }; 10 | 11 | fileSystems = { 12 | "/" = { 13 | fsType = "tmpfs"; 14 | options = [ 15 | "size=20%" 16 | ]; 17 | }; 18 | 19 | "/var" = 20 | let 21 | partConf = config.image.repart.partitions."var".repartConfig; 22 | in 23 | { 24 | device = "/dev/disk/by-partuuid/${partConf.UUID}"; 25 | fsType = partConf.Format; 26 | }; 27 | 28 | "/boot" = 29 | let 30 | partConf = config.image.repart.partitions."esp".repartConfig; 31 | in 32 | { 33 | device = "/dev/disk/by-partuuid/${partConf.UUID}"; 34 | fsType = partConf.Format; 35 | }; 36 | 37 | "/nix/store" = 38 | let 39 | partConf = config.image.repart.partitions."store".repartConfig; 40 | in 41 | { 42 | device = "/dev/disk/by-partlabel/${partConf.Label}"; 43 | fsType = partConf.Format; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /modules/generic.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: { 2 | 3 | boot.uki.name = "appliance"; 4 | boot.kernelParams = [ "console=ttyS0" ]; 5 | 6 | # TODO Is there a way to override these? 7 | #system.nixos.release = "2024-08"; 8 | #system.nixos.codeName = "Babylon"; 9 | 10 | system.nixos.distroId = "applianceos"; 11 | system.nixos.distroName = "ApplianceOS"; 12 | 13 | # Make the current system version visible in the prompt. 14 | programs.bash.promptInit = '' 15 | export PS1="\u@\h (version ${config.system.image.version}) $ " 16 | ''; 17 | 18 | # Not compatible with system.etc.overlay.enable yet. 19 | # users.mutableUsers = false; 20 | 21 | services.getty.autologinUser = "root"; 22 | 23 | boot.initrd.systemd.enable = true; 24 | 25 | # Don't accumulate crap. 26 | boot.tmp.cleanOnBoot = true; 27 | services.journald.extraConfig = '' 28 | SystemMaxUse=10M 29 | ''; 30 | 31 | # Debugging 32 | environment.systemPackages = with pkgs; [ 33 | parted 34 | (runCommand "systemd-sysupdate" { } '' 35 | mkdir -p $out/bin 36 | ln -s ${config.systemd.package}/lib/systemd/systemd-sysupdate $out/bin 37 | '') 38 | ]; 39 | 40 | system.stateVersion = "24.11"; 41 | } 42 | -------------------------------------------------------------------------------- /modules/minimize.nix: -------------------------------------------------------------------------------- 1 | { modulesPath, ... }: { 2 | imports = [ 3 | "${modulesPath}/profiles/minimal.nix" 4 | ]; 5 | 6 | boot.loader.grub.enable = false; 7 | 8 | system.switch.enable = false; 9 | nix.enable = false; 10 | 11 | system.etc.overlay.enable = true; 12 | systemd.sysusers.enable = true; 13 | 14 | system.disableInstallerTools = true; 15 | programs.less.lessopen = null; 16 | programs.command-not-found.enable = false; 17 | boot.enableContainers = false; 18 | environment.defaultPackages = [ ]; 19 | } 20 | -------------------------------------------------------------------------------- /modules/network.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: { 2 | networking = { 3 | useNetworkd = true; 4 | 5 | # Easy debugging. 6 | firewall.enable = false; 7 | }; 8 | 9 | # Faster boot. 10 | systemd.network.wait-online.enable = false; 11 | } 12 | -------------------------------------------------------------------------------- /modules/partitions.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, modulesPath, ... }: { 2 | 3 | imports = [ 4 | "${modulesPath}/image/repart.nix" 5 | ]; 6 | 7 | image.repart = 8 | let 9 | efiArch = pkgs.stdenv.hostPlatform.efiArch; 10 | in 11 | { 12 | name = config.boot.uki.name; 13 | split = true; 14 | 15 | partitions = { 16 | "esp" = { 17 | contents = { 18 | "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source = 19 | "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi"; 20 | 21 | "/EFI/Linux/${config.system.boot.loader.ukiFile}".source = 22 | "${config.system.build.uki}/${config.system.boot.loader.ukiFile}"; 23 | 24 | # systemd-boot configuration 25 | "/loader/loader.conf".source = (pkgs.writeText "$out" '' 26 | timeout 3 27 | ''); 28 | }; 29 | repartConfig = { 30 | Type = "esp"; 31 | UUID = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; # Well known 32 | Format = "vfat"; 33 | SizeMinBytes = "256M"; 34 | SplitName = "-"; 35 | }; 36 | }; 37 | "store" = { 38 | storePaths = [ config.system.build.toplevel ]; 39 | stripNixStorePrefix = true; 40 | repartConfig = { 41 | Type = "linux-generic"; 42 | Label = "store_${config.system.image.version}"; 43 | Format = "squashfs"; 44 | Minimize = "off"; 45 | ReadOnly = "yes"; 46 | 47 | SizeMinBytes = "1G"; 48 | SizeMaxBytes = "1G"; 49 | SplitName = "store"; 50 | }; 51 | }; 52 | 53 | # Placeholder for the second installed Nix store. 54 | "store-empty" = { 55 | repartConfig = { 56 | Type = "linux-generic"; 57 | Label = "_empty"; 58 | Minimize = "off"; 59 | SizeMinBytes = "1G"; 60 | SizeMaxBytes = "1G"; 61 | SplitName = "-"; 62 | }; 63 | }; 64 | 65 | # Persistent storage 66 | "var" = { 67 | repartConfig = { 68 | Type = "var"; 69 | UUID = "4d21b016-b534-45c2-a9fb-5c16e091fd2d"; # Well known 70 | Format = "xfs"; 71 | Label = "nixos-persistent"; 72 | Minimize = "off"; 73 | 74 | # Has to be large enough to hold update files. 75 | SizeMinBytes = "2G"; 76 | SizeMaxBytes = "2G"; 77 | SplitName = "-"; 78 | 79 | # Wiping this gives us a clean state. 80 | FactoryReset = "yes"; 81 | }; 82 | }; 83 | }; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /modules/sysupdate.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: { 2 | systemd.sysupdate = { 3 | enable = true; 4 | 5 | transfers = { 6 | "10-uki" = { 7 | Source = { 8 | MatchPattern = [ 9 | "${config.boot.uki.name}_@v.efi.xz" 10 | ]; 11 | 12 | # We could fetch updates from the network as well: 13 | # 14 | # Path = "https://download.example.com/"; 15 | # Type = "url-file"; 16 | Path = "/var/updates/"; 17 | Type = "regular-file"; 18 | }; 19 | Target = { 20 | InstancesMax = 2; 21 | MatchPattern = [ 22 | "${config.boot.uki.name}_@v.efi" 23 | ]; 24 | 25 | Mode = "0444"; 26 | Path = "/EFI/Linux"; 27 | PathRelativeTo = "boot"; 28 | 29 | Type = "regular-file"; 30 | }; 31 | Transfer = { 32 | ProtectVersion = "%A"; 33 | }; 34 | }; 35 | 36 | "20-store" = { 37 | Source = { 38 | MatchPattern = [ 39 | "store_@v.img.xz" 40 | ]; 41 | # Path = "https://download.example.com/"; 42 | # Type = "url-file"; 43 | Path = "/var/updates/"; 44 | Type = "regular-file"; 45 | }; 46 | 47 | Target = { 48 | InstancesMax = 2; 49 | 50 | # "auto" doesn't work, because / is a tmpfs and the 51 | # heuristic is not that smart. So we hardcode the device 52 | # here for the different platforms. 53 | # 54 | # Path = "auto"; 55 | Path = { 56 | x86_64-linux = "/dev/sda"; 57 | aarch64-linux = "/dev/vda"; 58 | riscv64-linux = "/dev/vda"; 59 | }."${pkgs.stdenv.system}"; 60 | 61 | MatchPattern = "store_@v"; 62 | 63 | Type = "partition"; 64 | ReadOnly = "yes"; 65 | }; 66 | 67 | Transfer = { 68 | ProtectVersion = "%A"; 69 | }; 70 | }; 71 | }; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /version-17.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | # Nothing here. 3 | } 4 | -------------------------------------------------------------------------------- /version-18.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | # Nothing here. 3 | } 4 | --------------------------------------------------------------------------------