1
0

impermanence.nix 3.3 KB

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