From 32062d18edfcec983e630d49aab46a0644619643 Mon Sep 17 00:00:00 2001 From: Artem Sheremet Date: Tue, 29 Sep 2026 16:41:23 +0000 Subject: [PATCH] Use proper impermanence in LXC --- flake.lock | 79 +++++++++++++++++++++++++++++++------ flake.nix | 6 +++ modules/nixos/linux-lxc.nix | 79 ++++++++++++++++--------------------- 3 files changed, 107 insertions(+), 57 deletions(-) diff --git a/flake.lock b/flake.lock index 1904c79..6790490 100644 --- a/flake.lock +++ b/flake.lock @@ -113,11 +113,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1788380457, - "narHash": "sha256-U/nnAFAoT5qZz65hUTxQnhfhTyNcsIKsO16aU5S569w=", + "lastModified": 1790698990, + "narHash": "sha256-3O+rVesesqOhcqbRbBsCKN5Nf+mrQCsF6pIsm7MYORs=", "ref": "refs/heads/main", - "rev": "d14e934cb708f3c7fe9f84ad0d22f904e7b2da24", - "revCount": 51, + "rev": "23b03a94448be4392b747436b9dc675204dd9f13", + "revCount": 56, "type": "git", "url": "https://github.com/futureware-tech/nix.git" }, @@ -135,11 +135,11 @@ ] }, "locked": { - "lastModified": 1787424939, - "narHash": "sha256-O2tBn84NNuHrnqNVxx/XqsXwfYvS1YwBh+7CBnbCYsk=", + "lastModified": 1790500375, + "narHash": "sha256-XN3sDtn8TU9hAc9xqZ+SqRB/HHm2wjxM6aSWYLJU+oo=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "809414f0cdadf82cf11b06c2b29ba9b3168b3297", + "rev": "a0e4241b51206fbcbf52fd322eb5f0cd80f153c4", "type": "github" }, "original": { @@ -210,6 +210,46 @@ "type": "github" } }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ + "impermanence", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768598210, + "narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "c47b2cc64a629f8e075de52e4742de688f930dc6", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "impermanence": { + "inputs": { + "home-manager": "home-manager_2", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1769548169, + "narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "7b1d382faf603b6d264f58627330f9faa5cba149", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "jail-nix": { "locked": { "lastModified": 1783532714, @@ -245,11 +285,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1788179007, - "narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=", + "lastModified": 1768564909, + "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9", + "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", "type": "github" }, "original": { @@ -290,6 +330,22 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1788179007, + "narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "crush-src": "crush-src", @@ -298,9 +354,10 @@ "git-hooks": "git-hooks_2", "home-manager": "home-manager", "home-manager-mars": "home-manager-mars", + "impermanence": "impermanence", "jail-nix": "jail-nix", "nix-homebrew": "nix-homebrew", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "nixpkgs-mars": "nixpkgs-mars", "systems": "systems_2", "vscode-server": "vscode-server" diff --git a/flake.nix b/flake.nix index 7e7a875..3dfdf1a 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,7 @@ inputs.nixpkgs.follows = "nixpkgs-mars"; }; nix-homebrew.url = "github:zhaofengli/nix-homebrew"; + impermanence.url = "github:nix-community/impermanence"; git-hooks = { url = "github:cachix/git-hooks.nix"; inputs.nixpkgs.follows = "nixpkgs"; @@ -142,6 +143,10 @@ system = "x86_64-linux"; specialArgs = { primaryUser = homeManagerUser; + + # /home is the only data-storing mountpoint besides /nix. + persistenceCommon = "/home/persistent"; + inherit (inputs) jail-nix; }; modules = [ @@ -149,6 +154,7 @@ self.nixosModules.linux-headless self.nixosModules.linux-lxc self.nixosModules.jailed-agy + inputs.impermanence.nixosModules.impermanence inputs.fw_nix.nixosModules.nix-gc inputs.fw_nix.nixosModules.nix-settings inputs.fw_nix.nixosModules.tools diff --git a/modules/nixos/linux-lxc.nix b/modules/nixos/linux-lxc.nix index cd6df7e..09868f2 100644 --- a/modules/nixos/linux-lxc.nix +++ b/modules/nixos/linux-lxc.nix @@ -2,6 +2,7 @@ modulesPath, pkgs, lib, + persistenceCommon, ... }: { @@ -11,59 +12,45 @@ # Disable legacy channel behavior that lxc-container brings in via installer/cd-dvd/channel.nix. system.installer.channel.enable = false; - # Impermanence setup: - # 1. There's no initrd/stage 1 in LXC container; /sbin/init is invoked after - # LXC finishes setting up special and user-configured filesystems. Any - # options in boot.initrd, as well as neededForBoot fileSystems won't be - # respected. - # 2. Non-boot fileSystems (aka systemd) mount too late for systemd or nixos - # persistence to be instantiated, so we have to create this script below. - # 3. The expectation from host is to mount /home and /nix. Root filesystem - # will also be a disk, as that's Incus requirement; the host should clean - # it up periodically using: "incus rebuild --empty ". - # 4. Since rootfs will be empty after rebuild, you have to point LXC at the - # current init (instead of /sbin/init), by adding to the "config:" section - # in "incus config edit ": - # raw.lxc: lxc.init.cmd = /nix/var/nix/profiles/system/init - system.activationScripts.persistence = { + environment.persistence.${persistenceCommon} = { + directories = [ + "/var/lib/systemd" + "/var/lib/nixos" + "/var/lib/docker" + ]; + files = [ + "/etc/machine-id" + ]; + }; + systemd.tmpfiles.rules = [ + "d ${persistenceCommon}/etc/ssh 0755 root root -" + ]; + services.openssh.hostKeys = [ + { + path = "${persistenceCommon}/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + ]; + # rootfs on LXC is also a persistent mountpoint (requirement from Incus), so + # the host should periodically clean it up using: incus rebuild --empty . + # + # Since rootfs is empty after container rebuild, for the first boot you have + # to point LXC at the current init (instead of /sbin/init), by adding to the + # "config:" section in "incus config edit ": + # raw.lxc: lxc.init.cmd = /nix/var/nix/profiles/system/init + # + # The bootloader installer will later try to symlink it into /sbin/init (which + # is what the next script prepares for), but that will be erased on the next + # container rebuild anyway. + system.activationScripts.bootloader-patch = { deps = [ "specialfs" ]; text = '' - persist() { - local item="$1" - local constructor="''${item%%:*}" - local target="''${item#*:}" - - mkdir -p "$(dirname "$target")" - $constructor "$target" - - if ! mountpoint -q "$target"; then - local source="/home/persistent/$target" - - mkdir -p "$(dirname "$source")" - $constructor "$source" - - mount --bind "$source" "$target" - fi - } - - for item in \ - "mkdir -p:/var/lib/nixos" \ - "mkdir -p:/var/lib/systemd" \ - "mkdir -p:/var/lib/docker" \ - "touch:/etc/machine-id" \ - "touch:/etc/ssh/ssh_host_ed25519_key" \ - ; do - persist "$item" - done - - chmod 0600 /etc/ssh/ssh_host_ed25519_key - # lxc-container.nix installBootloader/installInitScript will attempt to # symlink /sbin/init, so we have to create the parent directory. mkdir -p /sbin ''; }; - system.activationScripts.users.deps = [ "persistence" ]; + system.activationScripts.users.deps = [ "bootloder-patch" ]; # This is supposed to persist machine-id, but fails. systemd.services.systemd-machine-id-commit.enable = false;