feat: replace impermanence with preservation (#198)

* feat: replace impermanence with preservation

* fix: This module cannot be used with scripted initrd.

* fix: permission for /var/lib/private

* chore: boot.initrd.systemd.enable

* docs: boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1

* fix: typo
This commit is contained in:
Ryan Yin
2025-06-29 13:02:36 +08:00
committed by GitHub
parent 763167ce0d
commit 8690978d7e
15 changed files with 382 additions and 219 deletions

16
flake.lock generated
View File

@@ -886,6 +886,21 @@
"type": "github"
}
},
"preservation": {
"locked": {
"lastModified": 1738541138,
"narHash": "sha256-isT+jR8P8UFh5PJDzGHYXqVEHEZa0D5WvT5kfMf14AM=",
"owner": "nix-community",
"repo": "preservation",
"rev": "2f16754f9f6b766c1429375ab7417dc81cc90a63",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "preservation",
"type": "github"
}
},
"root": {
"inputs": {
"agenix": "agenix",
@@ -912,6 +927,7 @@
"nur-ryan4yin": "nur-ryan4yin",
"polybar-themes": "polybar-themes",
"pre-commit-hooks": "pre-commit-hooks",
"preservation": "preservation",
"wallpapers": "wallpapers"
}
},

View File

@@ -67,6 +67,7 @@
};
impermanence.url = "github:nix-community/impermanence";
preservation.url = "github:nix-community/preservation";
# community wayland nixpkgs
# nixpkgs-wayland.url = "github:nix-community/nixpkgs-wayland";

View File

@@ -18,7 +18,7 @@ in {
{hardware.myapple-t2.enableAppleSetOsLoader = true;}
./hardware-configuration.nix
../idols-ai/impermanence.nix
../idols-ai/preservation.nix
];
boot.kernelModules = ["kvm-amd"];

View File

@@ -18,7 +18,7 @@ in {
./hardware-configuration.nix
./nvidia.nix
./impermanence.nix
./preservation.nix
./secureboot.nix
];

View File

@@ -1,207 +0,0 @@
{
impermanence,
pkgs,
...
}: {
imports = [
impermanence.nixosModules.impermanence
];
environment.systemPackages = [
# `sudo ncdu -x /`
pkgs.ncdu
];
# There are two ways to clear the root filesystem on every boot:
## 1. use tmpfs for /
## 2. (btrfs/zfs only)take a blank snapshot of the root filesystem and revert to it on every boot via:
## boot.initrd.postDeviceCommands = ''
## mkdir -p /run/mymount
## mount -o subvol=/ /dev/disk/by-uuid/UUID /run/mymount
## btrfs subvolume delete /run/mymount
## btrfs subvolume snapshot / /run/mymount
## '';
#
# See also https://grahamc.com/blog/erase-your-darlings/
# NOTE: impermanence only mounts the directory/file list below to /persistent
# If the directory/file already exists in the root filesystem, you should
# move those files/directories to /persistent first!
environment.persistence."/persistent" = {
# sets the mount option x-gvfs-hide on all the bind mounts
# to hide them from the file manager
hideMounts = true;
directories = [
"/etc/NetworkManager/system-connections"
"/etc/ssh"
"/etc/nix/inputs"
"/etc/secureboot" # lanzaboote - secure boot
# my secrets
"/etc/agenix/"
"/var/log"
"/var/lib"
# created by modules/nixos/misc/fhs-fonts.nix
# for flatpak apps
# "/usr/share/fonts"
# "/usr/share/icons"
];
files = [
"/etc/machine-id"
];
# the following directories will be passed to /persistent/home/$USER
users.ryan = {
directories = [
# ======================================
# System Defaults
# ======================================
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
# ======================================
# Codes / Work / Playground
# ======================================
"codes" # for personal code
"work" # for work, contains a .gitconfig with my work email.
"nix-config"
"tmp"
# ======================================
# IDE / Editors
# ======================================
# neovim plugins(wakatime & copilot)
".wakatime"
".config/github-copilot"
# vscode
".vscode"
".config/Code"
".vscode-insiders"
".config/Code - Insiders"
# cursor ai editor
".cursor"
".config/Cursor"
# zed editor
".config/zed"
# doom-emacs
".config/emacs"
"org" # org files
# Joplin
".config/joplin" # tui client
".config/Joplin" # joplin-desktop
# ======================================
# Cloud Native
# ======================================
{
# pulumi - infrastructure as code
directory = ".pulumi";
mode = "0700";
}
{
directory = ".aws";
mode = "0700";
}
{
directory = ".aliyun";
mode = "0700";
}
{
directory = ".config/gcloud";
mode = "0700";
}
{
directory = ".docker";
mode = "0700";
}
{
directory = ".kube";
mode = "0700";
}
".terraform.d/plugin-cache" # terraform's plugin cache
# ======================================
# language package managers
# ======================================
".npm" # typsescript/javascript
"go"
".cargo" # rust
".m2" # java maven
".gradle" # java gradle
".conda" # python, generated by `conda-shell`
# ======================================
# Security
# ======================================
{
directory = ".gnupg";
mode = "0700";
}
{
directory = ".ssh";
mode = "0700";
}
".pki"
# ======================================
# Games / Media
# ======================================
".steam" # steam games
".config/blender"
".config/LDtk"
# ======================================
# Instant Messaging
# ======================================
".config/QQ"
".xwechat"
# ======================================
# Meeting / Remote Desktop
# ======================================
".config/remmina"
".config/freerdp"
".zoom"
# ======================================
# browsers
# ======================================
".mozilla"
".config/google-chrome"
# ======================================
# xdg data home & state home
# Used by:
# neovim, flatpak, autin, fcitx5, etc...
# ======================================
".local/share"
".local/state"
# ======================================
# Misc
# ======================================
".config/pulse"
".var" # flatpak app's data
];
files = [
".wakatime.cfg"
".config/nushell/history.txt"
".config/zoomus.conf"
".config/zoom.conf"
];
};
};
}

View File

@@ -0,0 +1,348 @@
{
preservation,
pkgs,
myvars,
...
}: let
inherit (myvars) username;
in {
imports = [
preservation.nixosModules.default
];
preservation.enable = true;
# pverservation required initrd using systemd.
boot.initrd.systemd.enable = true;
environment.systemPackages = [
# `sudo ncdu -x /`
pkgs.ncdu
];
# There are two ways to clear the root filesystem on every boot:
## 1. use tmpfs for /
## 2. (btrfs/zfs only)take a blank snapshot of the root filesystem and revert to it on every boot via:
## boot.initrd.postDeviceCommands = ''
## mkdir -p /run/mymount
## mount -o subvol=/ /dev/disk/by-uuid/UUID /run/mymount
## btrfs subvolume delete /run/mymount
## btrfs subvolume snapshot / /run/mymount
## '';
#
# See also https://grahamc.com/blog/erase-your-darlings/
# NOTE: impermanence only mounts the directory/file list below to /persistent
# If the directory/file already exists in the root filesystem you should
# move those files/directories to /persistent first!
preservation.preserveAt."/persistent" = {
directories = [
"/etc/NetworkManager/system-connections"
"/etc/ssh"
"/etc/nix/inputs"
"/etc/secureboot" # lanzaboote - secure boot
# my secrets
"/etc/agenix/"
"/var/log"
# system-core
"/var/lib/nixos"
"/var/lib/systemd"
{
directory = "/var/lib/private";
mode = "0700";
}
# containers
# "/var/lib/docker"
"/var/lib/cni"
"/var/lib/containers"
# other data
"/var/lib/flatpak"
# virtualisation
"/var/lib/libvirt"
"/var/lib/lxc"
"/var/lib/lxd"
"/var/lib/qemu"
# "/var/lib/waydroid"
# network
"/var/lib/tailscale"
"/var/lib/bluetooth"
"/var/lib/NetworkManager"
];
files = [
# auto-generated machine ID
{
file = "/etc/machine-id";
inInitrd = true;
}
];
# the following directories will be passed to /persistent/home/$USER
users.${username} = {
commonMountOptions = [
"x-gvfs-hide"
];
directories = [
# ======================================
# XDG Directories
# ======================================
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
# ======================================
# Codes / Work / Playground
# ======================================
"codes" # for personal code
"work" # for work contains a .gitconfig with my work email.
"nix-config"
"tmp"
# ======================================
# Nix / Home Manager Profiles
# ======================================
".local/state/home-manager"
".local/state/nix/profiles"
".local/share/nix"
# ======================================
# IDE / Editors
# ======================================
# neovim plugins(wakatime & copilot)
".wakatime"
".config/github-copilot"
# vscode
".vscode"
".config/Code"
".vscode-insiders"
".config/Code - Insiders"
# cursor ai editor
".cursor"
".config/Cursor"
# zed editor
".config/zed"
".local/share/zed"
# nvim
".local/share/nvim"
".local/state/nvim"
# doom-emacs
# "org" # org files
# ".config/emacs"
# ".local/share/doom"
# ".local/share/emacs"
# Joplin
".config/joplin" # tui client
".config/Joplin" # joplin-desktop
# ".local/share/jupyter"
# ======================================
# Cloud Native
# ======================================
{
# pulumi - infrastructure as code
directory = ".pulumi";
mode = "0700";
}
{
directory = ".aws";
mode = "0700";
}
{
directory = ".aliyun";
mode = "0700";
}
{
directory = ".config/gcloud";
mode = "0700";
}
{
directory = ".docker";
mode = "0700";
}
{
directory = ".kube";
mode = "0700";
}
".terraform.d/plugin-cache" # terraform's plugin cache
# ======================================
# language package managers
# ======================================
".npm" # typsescript/javascript
"go"
".cargo" # rust
".m2" # java maven
".gradle" # java gradle
".conda" # python generated by `conda-shell`
# ======================================
# Security
# ======================================
{
directory = ".gnupg";
mode = "0700";
}
{
directory = ".ssh";
mode = "0700";
}
{
directory = ".pki";
mode = "0700";
}
".local/share/password-store"
# gnmome keyrings
".local/share/keyrings"
# ======================================
# Games / Media
# ======================================
".steam"
".config/blender"
".config/LDtk"
".local/share/Steam"
".local/share/PrismLauncher"
".local/share/tiled"
".local/share/GOG.com"
".local/share/StardewValley"
".local/share/feral-interactive"
# ======================================
# Instant Messaging
# ======================================
".config/QQ"
".xwechat"
".local/share/TelegramDesktop"
# ======================================
# Meeting / Remote Desktop
# ======================================
".config/remmina"
".config/freerdp"
".zoom"
".local/share/remmina"
# ======================================
# browsers
# ======================================
".mozilla"
".config/google-chrome"
# ======================================
# CLI data
# ======================================
".local/share/atuin"
".local/share/zoxide"
".local/share/direnv"
".local/share/k9s"
# ======================================
# Containers
# ======================================
".local/share/containers"
".local/share/flatpak"
# flatpak app's data
".var"
# ======================================
# Misc
# ======================================
# Audio
".config/pulse"
".local/state/wireplumber"
# Digital Painting
".local/share/krita"
];
files = [
{
file = ".wakatime.cfg";
how = "symlink";
}
{
file = ".config/nushell/history.txt";
how = "symlink";
# create parent directory automatically
configureParent = true;
}
{
file = ".config/zoomus.conf";
how = "symlink";
}
{
file = ".config/zoom.conf";
how = "symlink";
}
];
};
};
# Create some directories with custom permissions.
#
# In this configuration the path `/home/butz/.local` is not an immediate parent
# of any persisted file so it would be created with the systemd-tmpfiles default
# ownership `root:root` and mode `0755`. This would mean that the user `butz`
# could not create other files or directories inside `/home/butz/.local`.
#
# Therefore systemd-tmpfiles is used to prepare such directories with
# appropriate permissions.
#
# Note that immediate parent directories of persisted files can also be
# configured with ownership and permissions from the `parent` settings if
# `configureParent = true` is set for the file.
systemd.tmpfiles.settings.preservation = let
permission = {
user = username;
group = "users";
mode = "0755";
};
in {
"/home/${username}/.config".d = permission;
"/home/${username}/.local".d = permission;
"/home/${username}/.local/share".d = permission;
"/home/${username}/.local/state".d = permission;
"/home/${username}/.terraform.d".d = permission;
};
# systemd-machine-id-commit.service would fail but it is not relevant
# in this specific setup for a persistent machine-id so we disable it
#
# see the firstboot example below for an alternative approach
systemd.suppressedSystemUnits = ["systemd-machine-id-commit.service"];
# let the service commit the transient ID to the persistent volume
systemd.services.systemd-machine-id-commit = {
unitConfig.ConditionPathIsMountPoint = [
""
"/persistent/etc/machine-id"
];
serviceConfig.ExecStart = [
""
"systemd-machine-id-setup --commit --root /persistent"
];
};
}

View File

@@ -3,6 +3,11 @@
pkgs,
...
}: {
# TODO: migrate from impermanence to preservation.
# Currently initrd do not support read password from devices:
# boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1.
#
# https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/boot/luksroot.nix
imports = [
impermanence.nixosModules.impermanence
];

View File

@@ -22,7 +22,7 @@
# Status Data:
# `journalctl -u tailscaled` shows tailscaled's logs
# logs indicate that tailscale store its data in /var/lib/tailscale
# which is already persistent across reboots(via impermanence.nix)
# which is already persistent across reboots(via preservation)
#
# References:
# https://github.com/NixOS/nixpkgs/blob/nixos-25.05/nixos/modules/services/networking/tailscale.nix

View File

@@ -34,7 +34,7 @@
../modules/nixos/base/networking.nix
../hosts/idols-ai/hardware-configuration.nix
../hosts/idols-ai/impermanence.nix
../hosts/idols-ai/preservation.nix
];
};
@@ -64,7 +64,7 @@
../modules/nixos/base/networking.nix
../hosts/12kingdoms-shoukei/hardware-configuration.nix
../hosts/idols-ai/impermanence.nix
../hosts/idols-ai/preservation.nix
];
};
};

View File

@@ -34,7 +34,7 @@
{
modules.desktop.wayland.enable = true;
modules.secrets.desktop.enable = true;
modules.secrets.impermanence.enable = true;
modules.secrets.preservation.enable = true;
}
]
++ base-modules.nixos-modules;

View File

@@ -39,7 +39,7 @@
modules.desktop.fonts.enable = true;
modules.desktop.wayland.enable = true;
modules.secrets.desktop.enable = true;
modules.secrets.impermanence.enable = true;
modules.secrets.preservation.enable = true;
}
]
++ base-modules.nixos-modules;

View File

@@ -26,7 +26,7 @@
++ [
{
modules.secrets.server.kubernetes.enable = true;
modules.secrets.impermanence.enable = true;
modules.secrets.preservation.enable = true;
}
];
# home-modules = map mylib.relativeToRoot [

View File

@@ -26,7 +26,7 @@
++ [
{
modules.secrets.server.kubernetes.enable = true;
modules.secrets.impermanence.enable = true;
modules.secrets.preservation.enable = true;
}
];
};

View File

@@ -26,7 +26,7 @@
++ [
{
modules.secrets.server.kubernetes.enable = true;
modules.secrets.impermanence.enable = true;
modules.secrets.preservation.enable = true;
}
];
};

View File

@@ -45,7 +45,7 @@ in {
server.webserver.enable = mkEnableOption "NixOS Secrets for Web Servers(contains tls cert keys)";
server.storage.enable = mkEnableOption "NixOS Secrets for HDD Data's LUKS Encryption";
impermanence.enable = mkEnableOption "whether use impermanence and ephemeral root file system";
preservation.enable = mkEnableOption "whether use impermanence and ephemeral root file system";
};
config = mkIf (cfg.desktop.enable || enabledServerSecrets) (mkMerge [
@@ -56,7 +56,7 @@ in {
# if you changed this key, you need to regenerate all encrypt files from the decrypt contents!
age.identityPaths =
if cfg.impermanence.enable
if cfg.preservation.enable
then [
# To decrypt secrets on boot, this key should exists when the system is booting,
# so we should use the real key file path(prefixed by `/persistent/`) here, instead of the path mounted by impermanence.