SkillAgentSearch skills...

Impermanence

Modules to help you handle persistent state on systems with ephemeral root storage [maintainer=@talyz]

Install / Use

/learn @nix-community/Impermanence
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

#+TITLE: Impermanence

Lets you choose what files and directories you want to keep between reboots - the rest are thrown away.

Why would you want this?

  • It keeps your system clean by default.

  • It forces you to declare settings you want to keep.

  • It lets you experiment with new software without cluttering up your system.

There are a few different things to set up for this to work:

  • A root filesystem which somehow gets wiped on reboot. There are a few ways to achieve this. See the [[#system-setup][System setup]] section for more info.

  • At least one mounted volume where the files and directories you want to keep are stored permanently.

  • At least one of the modules in this repository, which take care of linking or bind mounting files between the persistent storage mount point and the root file system. See the [[#module-usage][Module usage]] section for more info.

  • Contact

    Join the [[https://matrix.to/#/#impermanence:nixos.org][matrix room]] to chat about the project.

  • System setup

    There are many ways to wipe your root partition between boots. This section lists a few common ways to accomplish this, but is by no means an exhaustive list.

*** tmpfs

The easiest method is to use a tmpfs filesystem for the
root. This is the easiest way to set up impermanence on systems
which currently use a traditional filesystem (ext4, xfs, etc) as
the root filesystem, since you don't have to repartition.

All data stored in tmpfs only resides in system memory, not on
disk. This automatically takes care of cleaning up between boots,
but also comes with some pretty significant drawbacks:

- Downloading big files or trying programs that generate large
  amounts of data can easily result in either an out-of-memory or
  disk-full scenario.

- If the system crashes or loses power before you've had a chance
  to move files you want to keep to persistent storage, they're
  gone forever.

Using tmpfs as the root filesystem, the filesystem setup would
look something like this:

#+begin_src nix
  {
    fileSystems."/" = {
      device = "none";
      fsType = "tmpfs";
      options = [ "defaults" "size=25%" "mode=755" ];
    };

    fileSystems."/persistent" = {
      device = "/dev/root_vg/root";
      neededForBoot = true;
      fsType = "btrfs";
      options = [ "subvol=persistent" ];
    };

    fileSystems."/nix" = {
      device = "/dev/root_vg/root";
      fsType = "btrfs";
      options = [ "subvol=nix" ];
    };

    fileSystems."/boot" = {
      device = "/dev/disk/by-uuid/XXXX-XXXX";
      fsType = "vfat";
    };
  }
#+end_src

where the ~size~ option determines how much system memory is allowed
to be used by the filesystem.

*** BTRFS subvolumes

A more advanced solution which doesn't have the same drawbacks as
using tmpfs is to use a regular filesystem, but clean it up
between boots. A relatively easy way to do this is to use BTRFS
and create a new subvolume to use as root on boot. This also
allows you to keep a number of old roots around, in case of
crashes, power outages or other accidents.

A setup which would automatically remove roots that are
older than 30 days could look like this:

#+begin_src nix
  {
    fileSystems."/" = {
      device = "/dev/root_vg/root";
      fsType = "btrfs";
      options = [ "subvol=root" ];
    };

    boot.initrd.postResumeCommands = lib.mkAfter ''
      mkdir /btrfs_tmp
      mount /dev/root_vg/root /btrfs_tmp
      if [[ -e /btrfs_tmp/root ]]; then
          mkdir -p /btrfs_tmp/old_roots
          timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%-d_%H:%M:%S")
          mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
      fi

      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"
      }

      for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
          delete_subvolume_recursively "$i"
      done

      btrfs subvolume create /btrfs_tmp/root
      umount /btrfs_tmp
    '';

    fileSystems."/persistent" = {
      device = "/dev/root_vg/root";
      neededForBoot = true;
      fsType = "btrfs";
      options = [ "subvol=persistent" ];
    };

    fileSystems."/nix" = {
      device = "/dev/root_vg/root";
      fsType = "btrfs";
      options = [ "subvol=nix" ];
    };

    fileSystems."/boot" = {
      device = "/dev/disk/by-uuid/XXXX-XXXX";
      fsType = "vfat";
    };
  }
#+end_src

This assumes the BTRFS filesystem can be found in an LVM volume
group called ~root_vg~. Adjust the path as necessary.
  • Module usage

    There are currently two modules: one for NixOS and one for Home Manager.

*** NixOS

To use the module, import it into your configuration with

#+begin_src nix
  {
    imports = [ /path/to/impermanence/nixos.nix ];
  }
#+end_src

or use the provided ~nixosModules.impermanence~ flake output:

#+begin_src nix
  {
    inputs = {
      impermanence.url = "github:nix-community/impermanence";
    };

    outputs = { self, nixpkgs, impermanence, ... }:
      {
        nixosConfigurations.sythe = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            impermanence.nixosModules.impermanence
            ./machines/sythe/configuration.nix
          ];
        };
      };
  }
#+end_src

This adds the ~environment.persistence~ option, which is an
attribute set of submodules, where the attribute name is the path
to persistent storage.

Note that this project has ~nixpkgs~ and ~home-manager~ as dependencies for
development, but these are not needed when using the module. If these extra
dependencies appearing in your ~flake.lock~ bother you, you can remove them
like this:

#+begin_src nix
inputs = {
  impermanence.url = "github:nix-community/impermanence";
  impermanence.inputs.nixpkgs.follows = "";
  impermanence.inputs.home-manager.follows = "";
};
#+end_src

Usage is shown best with an example:

#+begin_src nix
  {
    environment.persistence."/persistent" = {
      enable = true;  # NB: Defaults to true, not needed
      hideMounts = true;
      directories = [
        "/var/log"
        "/var/lib/bluetooth"
        "/var/lib/nixos"
        "/var/lib/systemd/coredump"
        "/etc/NetworkManager/system-connections"
        { directory = "/var/lib/colord"; user = "colord"; group = "colord"; mode = "u=rwx,g=rx,o="; }
      ];
      files = [
        "/etc/machine-id"
        { file = "/var/keys/secret_file"; parentDirectory = { mode = "u=rwx,g=,o="; }; }
      ];
      users.bird = {
        directories = [
          "Downloads"
          "Music"
          "Pictures"
          "Documents"
          "Videos"
          "VirtualBox VMs"
          { directory = ".gnupg"; mode = "0700"; }
          { directory = ".ssh"; mode = "0700"; }
          { directory = ".nixops"; mode = "0700"; }
          { directory = ".local/share/keyrings"; mode = "0700"; }
          ".local/share/direnv"
        ];
        files = [
          ".screenrc"
        ];
      };
    };
  }
#+end_src

***** ~"/persistent"~

  The path to your persistent storage location.

  This allows for multiple different persistent storage
  locations. If you, for example, have one location you back up
  and one you don't, you can use both by defining two separate
  attributes under ~environment.persistence~.

  - ~enable~ determines whether the persistent storage location should
    be enabled or not. Useful when sharing configurations between
    systems with and without impermanence setups. Defaults to ~true~.

  - ~hideMounts~ allows you to specify whether to hide the
    bind mounts from showing up as mounted drives in the file
    manager. If enabled, it sets the mount option ~x-gvfs-hide~
    on all the bind mounts.

  - ~allowTrash~ allows you to specify whether to allow apps that
    use certain GTK-based technologies such as GIO to put items in
    the trash. If enabled, it sets the mount option ~x-gvfs-trash~
    on all the bind mounts.

****** ~directories~

   All directories you want to bind mount to persistent storage. A
   directory can be represented either as a string, simply denoting its
   path, or as a submodule. The submodule representation is useful when
   the default assumptions, mainly regarding permissions, are
   incorrect. The available options are:

   - ~directory~, the path to the directory you want to bind mount to
     persistent storage. Only setting this option is equivalent to
     the string representation.

   - ~persistentStoragePath~, the path to persistent
     storage. Defaults to the ~environment.persistence~ submodule
     name, i.e. ~"/persistent"~ in the example. This should most
     likely be left to its default value - don't change it unless
     you're certain you really need to.

   - ~user~, the user who should own the directory. If the directory
     doesn't already exist in persistent storage, it will be
     created and this user will be its owner. This also applies to
     any parent directories which don't yet exist. Changing this
View on GitHub
GitHub Stars1.7k
CategoryDevelopment
Updated12h ago
Forks122

Languages

Nix

Security Score

100/100

Audited on Mar 29, 2026

No findings