1
0

impermanence.nix 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. {
  2. config,
  3. inputs,
  4. pkgs,
  5. ...
  6. }:
  7. {
  8. imports = [
  9. inputs.impermanence.nixosModules.impermanence
  10. ];
  11. boot.initrd.systemd.services.rollback-root = {
  12. description = "Rollback Btrfs root filesystem to a pristine state";
  13. wantedBy = [ "initrd.target" ];
  14. # Run after the physical storage device node exists
  15. after = [ "block-device-systemd-sysext.target" ];
  16. # Run strictly before the root partition gets mounted by stage 1
  17. before = [ "sysroot.mount" ];
  18. unitConfig.DefaultDependencies = "no";
  19. # We explicitly include packages needed inside the initrd environment
  20. path = with pkgs; [
  21. btrfs-progs
  22. coreutils
  23. util-linux
  24. gawk
  25. gnugrep
  26. ];
  27. serviceConfig = {
  28. Type = "oneshot";
  29. RemainAfterExit = true;
  30. ExecStart = pkgs.writeShellScript "rollback-root-script" ''
  31. set -e
  32. DEVICE="${config.disko.devices.disk.main.device}-part2"
  33. # Mount Btrfs root
  34. mkdir -p /mnt
  35. if ! mount -o subvol=/ "$DEVICE" /mnt; then
  36. echo "Failed to mount $DEVICE at /mnt"
  37. exit 1
  38. fi
  39. # Create directory for old roots
  40. mkdir -p /mnt/old-roots
  41. # Move current root to old-roots with current timestamp
  42. if [[ -e /mnt/@root ]]; then
  43. timestamp=$(date +%Y-%m-%d_%H:%M:%S)
  44. if ! btrfs subvolume snapshot -r /mnt/@root "/mnt/old-roots/@root-$timestamp"; then
  45. echo "Failed to move /mnt/@root to old-roots"
  46. umount /mnt
  47. exit 1
  48. fi
  49. fi
  50. # Function to recursively delete subvolumes
  51. delete_subvolume_recursively() {
  52. for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
  53. delete_subvolume_recursively "/mnt/$i"
  54. done
  55. if ! btrfs subvolume delete "$1"; then
  56. echo "Failed to delete subvolume $1"
  57. fi
  58. }
  59. # Delete old roots more than 5
  60. index=0
  61. for i in $(btrfs subvolume list /mnt | grep 'old-roots/@root-' | cut -f 9- -d ' ' | sort -r); do
  62. if [[ $index -ge 5 ]]; then
  63. delete_subvolume_recursively "/mnt/$i"
  64. fi
  65. index=$((index + 1))
  66. done
  67. # Create or restore fresh root
  68. if [[ -e /mnt/@root-blank ]]; then
  69. delete_subvolume_recursively /mnt/@root
  70. if ! btrfs subvolume snapshot /mnt/@root-blank /mnt/@root; then
  71. echo "Failed to snapshot @root-blank to @root"
  72. umount /mnt
  73. exit 1
  74. fi
  75. else
  76. if ! btrfs subvolume create /mnt/@root-blank; then
  77. echo "Failed to create @root-blank"
  78. umount /mnt
  79. exit 1
  80. fi
  81. if ! btrfs subvolume create /mnt/@root; then
  82. echo "Failed to create @root"
  83. umount /mnt
  84. exit 1
  85. fi
  86. fi
  87. # Unmount
  88. if ! umount /mnt; then
  89. echo "Failed to unmount /mnt"
  90. exit 1
  91. fi
  92. '';
  93. };
  94. };
  95. # Persistent directories for impermanence
  96. fileSystems."/persist".neededForBoot = true;
  97. fileSystems."/var/lib".neededForBoot = true;
  98. environment.persistence."/persist" = {
  99. hideMounts = true;
  100. directories = [
  101. "/etc/ssh"
  102. "/var/cache"
  103. ];
  104. files = [
  105. "/etc/machine-id"
  106. ];
  107. users.thomas.directories = [
  108. ".ssh"
  109. ".local/share"
  110. ];
  111. };
  112. }