So The problem is it nukes everything on reboot even nukes /etc/nixos/* and so on. So what Impermanence does is to have the directory or maybe recreates it but no directory had any files it was all empty directory.
here is my config.
copyfile /etc/nixos/system/hardware/impermanence.nix.bak
{
inputs,
username,
...
}: {
# Enable FUSE for impermanence
programs.fuse.userAllowOther = true;
# Add user to 'fuse' group
users.users.${username}.extraGroups = ["fuse"];
# π₯ Nuke root AND home subvolumes on every boot
boot.initrd.postDeviceCommands = ''
mkdir -p /btrfs_tmp
mount /dev/mapper/cryptroot /btrfs_tmp
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
# Nuke and recreate root
delete_subvolume_recursively /btrfs_tmp/root
btrfs subvolume create /btrfs_tmp/root
# Nuke and recreate home (wipes /home β but we restore via impermanence)
delete_subvolume_recursively /btrfs_tmp/home
btrfs subvolume create /btrfs_tmp/home
umount /btrfs_tmp
'';
# System persistence β survives reboots
environment.persistence."/persist" = {
enable = true;
hideMounts = true;
directories = [
# System state
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/var/lib/bluetooth"
"/var/lib/NetworkManager"
# NetworkManager connections
"/etc/NetworkManager/system-connections"
# SSH host keys (critical!)
{
directory = "/etc/ssh";
mode = "0755";
}
# Keep your NixOS config
"/etc/nixos"
];
files = [
"/etc/machine-id"
# "/etc/hosts" # uncomment if needed
];
};
# Prevent sudo lecture after reboot
security.sudo.extraConfig = "Defaults lecture = never";
# β
CRITICAL: Ensure both persisted AND ephemeral home dirs exist
systemd.tmpfiles.rules = [
# Persisted storage (source of truth)
"d /persist/home 0755 root root -"
"d /persist/home/${username} 0750 ${username} ${username} -"
# Ephemeral home directory β MUST exist for bind mounts to work
"d /home/${username} 0750 ${username} ${username} -"
];
}
copyfile /etc/nixos/home/hardware/impermanence.nix
{username, ...}: {
home.persistence."/persist/home/${username}" = {
allowOther = true;
directories = [
# Standard user directories
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
"Desktop"
"Public"
"Templates"
# Security
".gnupg"
".local/share/keyrings"
# Development & Config
".config/git"
# Browsers
".zen"
".cache/zen"
# Media
#".config/mpv"
#".local/share/vlc"
# System
#".local/state/wireplumber"
#".config/pulse"
# Shell
#".bash_history"
# Desktop & Apps
".local/share/applications"
".local/share/icons"
".config/fontconfig"
# Your custom game/mod dirs
"Games"
"Game-Mods"
"Appimages"
];
files = [
#".bashrc"
#".profile"
#".bash_history"
#".config/mimeapps.list"
#".config/user-dirs.dirs"
#".config/user-dirs.locale"
];
};
}
Also here is the disko config.
{inputs, ...}: {
imports = [
inputs.disko.nixosModules.disko
];
disko.devices = {
disk = {
nvme0n1 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
label = "boot";
name = "ESP";
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"defaults"
];
};
};
luks = {
size = "100%";
label = "luks";
content = {
type = "luks";
name = "cryptroot";
extraOpenArgs = [
"--allow-discards"
"--perf-no_read_workqueue"
"--perf-no_write_workqueue"
];
content = {
type = "btrfs";
extraArgs = ["-L" "nixos" "-f"];
subvolumes = {
"/root" = {
mountpoint = "/";
mountOptions = ["subvol=root" "compress=zstd:1" "noatime" "ssd" "discard=async" "space_cache=v2" "commit=120"];
};
"/home" = {
mountpoint = "/home";
mountOptions = ["subvol=home" "compress=zstd:1" "noatime" "ssd" "discard=async" "space_cache=v2" "commit=120" "autodefrag"];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = ["subvol=nix" "compress=zstd:1" "noatime" "ssd" "discard=async" "space_cache=v2" "commit=120" "nodatacow" "nodatasum"];
};
"/persist" = {
mountpoint = "/persist";
mountOptions = ["subvol=persist" "compress=zstd:1" "noatime" "ssd" "discard=async" "space_cache=v2" "commit=120"];
};
"/log" = {
mountpoint = "/var/log";
mountOptions = ["subvol=log" "compress=zstd:1" "noatime" "ssd" "discard=async" "space_cache=v2" "commit=120" "nodatacow" "nodatasum"];
};
"/swap" = {
mountpoint = "/swap";
swap.swapfile.size = "4G";
};
};
};
};
};
};
};
};
};
};
fileSystems."/persist".neededForBoot = true;
fileSystems."/var/log".neededForBoot = true;
}
My guess would be that wiping β/β also removes the folder where you mount the other subvolumes to. Have you tried recreating those folder Γ€s after creating the new root subvolume?
I recreated /home/ which even did not existed and my username also i recreated /etc/nixos/ and copied my backup files into it. the directory /etc/nixos existed but it was empty. All what i want to do is i want my system to nuke everything except which is declared with impermanence.
Did you recreate it in this script? Otherwise when delete you recreate the root subvolume the folder /etc/nixos does not exist hence the other subvolume cannot be mounted.
In my last message i did that time manually by commands. And i updated the script little bit.
copyfile /etc/nixos/system/hardware/impermanence.nix
{username, ...}: {
# Enable FUSE for impermanence
programs.fuse.userAllowOther = true;
# Add user to 'fuse' group
users.users.${username}.extraGroups = ["fuse"];
boot.initrd.postDeviceCommands = ''
echo "=== IMPERMANENCE: NUKE SCRIPT STARTING ===" > /dev/kmsg
mkdir -p /btrfs_tmp
if mount /dev/mapper/cryptroot /btrfs_tmp; then
echo "Mounted cryptroot successfully" > /dev/kmsg
# --- NUKE ROOT ---
if btrfs subvolume delete /btrfs_tmp/root 2>/dev/null; then
echo "Deleted root subvolume" > /dev/kmsg
else
echo "Root subvolume already gone or error (normal on first boot)" > /dev/kmsg
fi
btrfs subvolume create /btrfs_tmp/root
echo "Created fresh root subvolume" > /dev/kmsg
# --- NUKE HOME ---
if btrfs subvolume delete /btrfs_tmp/home 2>/dev/null; then
echo "Deleted home subvolume" > /dev/kmsg
else
echo "Home subvolume already gone or error (normal on first boot)" > /dev/kmsg
fi
btrfs subvolume create /btrfs_tmp/home
echo "Created fresh home subvolume" > /dev/kmsg
# --- NUKE USER PERSIST DATA ---
if [ -d "/btrfs_tmp/persist/home" ]; then
echo "Deleting ALL persisted home data..." > /dev/kmsg
find /btrfs_tmp/persist/home -mindepth 1 -delete
echo "β
All persisted home data deleted" > /dev/kmsg
else
echo "No persist/home directory found (first boot?)" > /dev/kmsg
fi
umount /btrfs_tmp
echo "Unmounted btrfs_tmp" > /dev/kmsg
else
echo "β ERROR: Failed to mount cryptroot!" > /dev/kmsg
echo "Available devices:" > /dev/kmsg
ls -la /dev/mapper/ > /dev/kmsg
exit 1
fi
echo "=== IMPERMANENCE: NUKE SCRIPT COMPLETE ===" > /dev/kmsg
'';
# System persistence β survives reboots
environment.persistence."/persist" = {
enable = true;
hideMounts = true;
directories = [
# System state
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/var/lib/bluetooth"
"/var/lib/NetworkManager"
# NetworkManager connections
"/etc/NetworkManager/system-connections"
# SSH host keys (critical!)
{
directory = "/etc/ssh";
mode = "0755";
}
# Keep your NixOS config
"/etc/nixos"
];
files = [
"/etc/machine-id"
# "/etc/hosts" # uncomment if needed
];
};
# Prevent sudo lecture after reboot
security.sudo.extraConfig = "Defaults lecture = never";
# β
CRITICAL: Ensure both persisted AND ephemeral home dirs exist
systemd.tmpfiles.rules = [
# Persisted storage (source of truth)
"d /persist 0755 root root -"
"d /persist/home 0755 root root -"
"d /persist/home/${username} 0750 ${username} ${username} -"
# Ephemeral home directory β MUST exist for bind mounts to work
"d /home/${username} 0750 ${username} ${username} -"
];
}
copyfile /etc/nixos/home/hardware/impermanence.nix
{username, ...}: {
home.persistence."/persist/home/${username}" = {
allowOther = true;
removePrefixDirectory = true; # β CRITICAL: Only mount specific directories
directories = [
# Standard user directories
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
"Desktop"
"Public"
"Templates"
# Security
".gnupg"
".local/share/keyrings"
# Development & Config
".config/git"
# Browsers
".zen"
".cache/zen"
# Media
#".config/mpv"
#".local/share/vlc"
# System
#".local/state/wireplumber"
#".config/pulse"
# Shell
#".bash_history"
# Desktop & Apps
".local/share/applications"
".local/share/icons"
".config/fontconfig"
# Your custom game/mod dirs
"Games"
"Game-Mods"
"Appimages"
];
files = [
#".bashrc"
#".profile"
#".bash_history"
#".config/mimeapps.list"
#".config/user-dirs.dirs"
#".config/user-dirs.locale"
];
};
}
And this time the script does nothing at all IMO, As i can not see deleting any file from /home directory. Such as random-test-file.txt This file exist in my home directory which is not managed by impermanence but it does not get deleted it just exist.
I suspect you are using an LLM? Generally the way using tmpfiles to creat folders is correct but I doubt in this case. I meant literally just adding mkdir -p /what/ever in the first version of the script.
Iβm using zfs so I followed this guide: Erase your darlings: immutable infrastructure for mutable systems - Graham Christensen which you could adapt.
I also found: Encypted Btrfs Root with Opt-in State on NixOS.
I think before going into a setup like this it is helpful to do some more research on how it generally works.
Yes, I am using LLM even the LLM is getting failed in this case.
So i found my issue about why the second script and now the third script never runs, The issue is likely that /dev/mapper/cryptroot isnβt available yet when postDeviceCommands runs.
boot.initrd.postDeviceCommands or boot.initrd.postMountCommands is not running after luks decrypt, or maybe it is running before the luks decryption.
Again I would like to highlight you are probably best of looking at the links I provided. I can give you an example that works for me for zfs but that would probably not help (it is also quite close to the links I provided)
You are right but the script does not run no matter what i try. So i tried this config to verify if the script actually runs.
{
username,
pkgs,
...
}: {
# Enable FUSE for impermanence
programs.fuse.userAllowOther = true;
# Add user to 'fuse' group
users.users.${username}.extraGroups = ["fuse"];
boot.initrd = {
# Ensure Btrfs support
supportedFilesystems = ["btrfs"];
# Include necessary kernel modules
kernelModules = ["btrfs" "dm-mod" "dm-crypt"];
# Add btrfs-progs to initrd (other utilities should already be available)
extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs
'';
# MINIMAL TEST - Just check if the script runs at all
postDeviceCommands = ''
echo "=== IMPERMANENCE TEST STARTED ===" > /dev/kmsg
echo "Script is running during boot" > /dev/kmsg
echo "=== IMPERMANENCE TEST FINISHED ===" > /dev/kmsg
'';
};
# System persistence configuration
environment.persistence."/persist" = {
enable = true;
hideMounts = true;
directories = [
"/etc/nixos"
"/etc/NetworkManager/system-connections"
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/var/lib/bluetooth"
"/var/lib/NetworkManager"
{
directory = "/etc/ssh";
mode = "0755";
}
];
files = [
"/etc/machine-id"
];
};
systemd.tmpfiles.rules = [
"d /persist 0755 root root -"
"d /persist/home 0755 root root -"
"d /persist/home/${username} 0700 ${username} users -"
];
fileSystems."/persist".neededForBoot = true;
fileSystems."/var/log".neededForBoot = true;
security.sudo.extraConfig = "Defaults lecture = never";
}
And the result is no output.
β― sudo dmesg | grep -i impermanence
[sudo] password for Linux-DADDY:
[ble: exit 1]
β― sudo dmesg | grep -i IMPERMANENCE
[ble: exit 1]
As i said before The issue is likely that /dev/mapper/cryptroot isnβt available yet when postDeviceCommands runs.
boot.initrd.postDeviceCommands or boot.initrd.postMountCommands is not running after luks decrypt, or maybe it is running before the luks decryption. If the boot.initrd.* does not run then no other good or bad script will also not run.
@eblechschmidt Hey! So i found the issue which was with home manager impermanence, It was the cause for bricking my whole impermanence config And i removed impermanence from home manager now it works fine. Anyway thank you.
2 Likes
Also if someone really needs the nuke script here it is.
{
pkgs,
lib,
declarative,
inputs,
...
}: let
persistPath = declarative.persistPath;
initrdNukeScript = ''
echo "π Initrd: Starting btrfs impermanence cleanup..."
if [ ! -e /dev/mapper/cryptroot ]; then
echo "β LUKS device not found - trying to open..."
cryptsetup luksOpen /dev/disk/by-label/luks cryptroot || {
echo "β Failed to open LUKS device"
exit 1
}
fi
mkdir -p /tmp/mnt-btrfs-root
if ! mount -t btrfs /dev/mapper/cryptroot /tmp/mnt-btrfs-root -o subvolid=5,compress=zstd:1; then
echo "β Failed to mount btrfs root (subvolid=5)"
exit 1
fi
cd /tmp/mnt-btrfs-root
timestamp=$(date +%Y%m%d-%H%M%S)
if [ ! -d "@nix" ] || [ ! -d "@persist" ]; then
echo "β CRITICAL: @nix or @persist missing! Aborting."
cd /
umount /tmp/mnt-btrfs-root
exit 1
fi
delete_subvolume_recursively() {
local subvol_path="$1"
btrfs subvolume list -o "$subvol_path" 2>/dev/null | cut -f9 -d' ' | while read -r nested; do
btrfs subvolume delete "$nested" 2>/dev/null || true
done
btrfs subvolume delete "$subvol_path" 2>/dev/null || true
}
if [ -e "@root" ]; then
btrfs subvolume snapshot "@root" "@root-old-$timestamp" 2>/dev/null || true
delete_subvolume_recursively "@root"
fi
btrfs subvolume create "@root"
mkdir -p "@root"/{etc,var,tmp,usr}
if [ -e "@home" ]; then
btrfs subvolume snapshot "@home" "@home-old-$timestamp" 2>/dev/null || true
delete_subvolume_recursively "@home"
fi
btrfs subvolume create "@home"
# β
FIXED: Use $base (no braces) to avoid editor warnings
for base in "@root" "@home"; do
find . -maxdepth 1 -name "$base-old-*" -type d 2>/dev/null | sort -r | tail -n +4 | while IFS= read -r old; do
btrfs subvolume delete "./$old" 2>/dev/null || true
done
done
cd /
umount /tmp/mnt-btrfs-root
rmdir /tmp/mnt-btrfs-root 2>/dev/null || true
echo "β
Impermanence cleanup completed"
'';
in {
imports = [
inputs.impermanence.nixosModules.impermanence
];
boot.initrd = {
supportedFilesystems = ["btrfs" "vfat"];
availableKernelModules = ["btrfs" "dm-mod" "dm-crypt"];
kernelModules = ["btrfs" "dm-mod" "dm-crypt"];
extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfs
copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup
'';
postDeviceCommands = lib.mkAfter initrdNukeScript;
};
fileSystems.${persistPath}.neededForBoot = true;
}
1 Like