Installing NixOS (Encrypted BTRFS Impermanance)
Intro
This details the steps to create a LUKS encrypted BTRFS NixOS installation with impermanance, only keeping the settings that are required in between reboots.
Features:
- UEFI Boot w/GRUB
- Encrypted Btrfs File System with hourly snapshots on
/home
and/var/local
- Swap File instead of Swap Partition w/Hibernate Support
- Works on Single Disk and RAID setups
Disk Setup
Setup Variables
DISK_EFI_SIZE_MB=512
DISK_SWAP_SIZE_GB=4
DISK=/dev/vda
PASSWORD=password
If setting up RAID1:
DISK2=/dev/vdb
Parition Disk
Choose one, Swap parititon needs to be used if using any sort of RAID
Wipe and Partition Disk (Swap Partition)
wipefs "${DISK}" -a -f
sgdisk --zap-all "${DISK}"
sgdisk --clear \
--new=1:0:+"${DISK_EFI_SIZE_MB}"MiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:+"${DISK_SWAP_SIZE_GB}"GiB --typecode=2:8200 --change-name=2:swap \
--new=3:0:0 --typecode=3:8300 --change-name=3:pool0_0 \
"${DISK}"
EFI_PARTITION=1
SWAP_PARTITION=2
CRYPT_PARTITION=3
Wipe and Partition Disk (Swap File)
wipefs "${DISK}" -a -f
sgdisk --zap-all "${DISK}"
sgdisk --clear \
--new=1:0:+"${DISK_EFI_SIZE_MB}"MiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:0 --typecode=2:8200 --change-name=2:pool0_0 \
"${DISK}"
EFI_PARTITION=1
CRYPT_PARTITION=2
Create Encrypted Disk and Initial Filesystems
echo "${PASSWORD}" | cryptsetup --verify-passphrase -v luksFormat "${DISK}""${CRYPT_PARTITION}"
echo "${PASSWORD}" | cryptsetup open "${DISK}""${CRYPT_PARTITION}" pool0_0
mkfs.btrfs -f /dev/mapper/pool0_0
Raid 1
echo "${PASSWORD}" | cryptsetup --verify-passphrase -v luksFormat "${DISK}""${CRYPT_PARTITION}"
echo "${PASSWORD}" | cryptsetup open "${DISK}""${CRYPT_PARTITION}" pool0_0
echo "${PASSWORD}" | cryptsetup --verify-passphrase -v luksFormat "${DISK2}"
echo "${PASSWORD}" | cryptsetup open "${DISK2}" pool0_1
mkfs.btrfs -f -m raid1 -d raid1 /dev/mapper/pool0_[01]
Create BTRFS Subvolumes
mount -t btrfs /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt
btrfs subvolume create /mnt/root
mkdir -p /mnt/home
btrfs subvolume create /mnt/home/active
btrfs subvolume create /mnt/home/snapshots
btrfs subvolume create /mnt/nix
btrfs subvolume create /mnt/persist
btrfs subvolume create /mnt/swap
mkdir -p /mnt/var_local
btrfs subvolume create /mnt/var_local/active
btrfs subvolume create /mnt/var_local/snapshots
btrfs subvolume create /mnt/var_log
Take a readonly snapshot of the root subvolume, which will get rolled back to on every boot.
btrfs subvolume snapshot -r /mnt/root /mnt/root-blank
Mount Subvolumes and Partitions
umount /mnt
mount -o subvol=root,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt
mkdir -p /mnt/home
mount -o subvol=home/active,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/home
mkdir -p /mnt/home/.snapshots
mount -o subvol=home/snapshots,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/home/.snapshots
mkdir -p /mnt/nix
mount -o subvol=nix,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/nix
mkdir -p /mnt/persist
mount -o subvol=persist,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/persist
mkdir -p /mnt/var/local
mount -o subvol=var_local/active,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/var/local
mkdir -p /mnt/var/local/.snapshots
mount -o subvol=var_local/snapshots,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/var/local/.snapshots
mkdir -p /mnt/var/log
mount -o subvol=var_log,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/var/log
mkdir -p /mnt/boot
mount -o defaults,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro /dev/disk/by-partlabel/EFI /mnt/boot/
Setup Swap
Parition
mkswap "${DISK}""${SWAP_PARTITION}"
swapon
File Based Swap
- You can only use File Based swap on non RAID drives.
mkdir -p /mnt/swap
mount -o subvol=swap,compress=zstd,noatime /dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2) /mnt/swap
mkdir -p /mnt/swap
touch /mnt/swap/swapfile
chattr +C /mnt/swap/swapfile
dd if=/dev/zero of=mnt/swap/swapfile bs=1024 count=0 seek=$(expr "$(cat /proc/meminfo | grep "MemTotal:" | awk '{$2=$2/(1024^2); print $2;}' | awk '{print ($0-int($0)>0)?int($0)+1:int($0)}')" + 2))G
mkswap /mnt/swap/swapfile
chmod 600 /mnt/swap/swapfile
swapon /mnt/swap/swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=1048576
# Other way of forcing it in
sed -i '/boot = {/i \
## Swap File \
swapDevices = [{\
device = \"\/swap\/swapfile\";\
size = (1024 * 16) + (1024 * 2); # RAM size + 2 GB\
}];\
fileSystems."/wap".options = [ "subvol=swap" "compress=none" "noatime" ];\
' /mnt/etc/nixos/configuration.nix
OR
sed -i '1h;1!H;$!d;x;s/.*fileSystems.*};\n[^\n]*/& \
swapDevices = [{\
device = \"\/swap\/swapfile\";\
size = $(expr "$(cat /proc/meminfo | grep "MemTotal:" | awk '{$2=$2/(1024^2); print $2;}' | awk '{print ($0-int($0)>0)?int($0)+1:int($0)}')" + 2)); # RAM size + 2 GB\
}];\n/' /mnt/etc/nixos/hardware-configuration.nix
## Hibernation
sed -i '/boot = {/i \
boot.kernelParams = [ \"resume_offset=$(btrfs inspect-internal map-swapfile -r /mnt/swap/swapfile)\" ];\n\
boot.resumeDevice = \"/dev/disk/by-uuid/$(blkid | grep '/dev/mapper/pool0_0' | awk '{print $2}' | cut -d '"' -f 2)\" ;" /mnt/etc/nixos/configuration.nix
Generate NixOS Hardware Configuration
nixos-generate-config --root /mnt
Force BTRFS mount options
Nixos-generate config isn't smart enough to add options as per here.
sed -i '/boot = {/i \
# Set Filesystms to noatime and zstd compression
fileSystems."/".options = [ "subvol=root" "compress=zstd" "noatime" ]; \
fileSystems."/home".options = [ "subvol=home/active" "compress=zstd" "noatime" ]; \
fileSystems."/home/.snapshots".options = [ "subvol=home/snapshot" "compress=zstd" "noatime" ]; \
fileSystems."/nix".options = [ "subvol=nix" "compress=zstd" "noatime" ]; \
fileSystems."/persist".options = [ "subvol=persist" "compress=zstd" "noatime" ]; \
filesystems."/persist".neededForBoot = true ; \
fileSystems."/var/local".options = [ "subvol=var_local/active" "compress=zstd" "noatime" ]; \
fileSystems."/var/local/.snapshots".options = [ "subvol=var_local/snapshot" "compress=zstd" "noatime" ]; \
fileSystems."/var/log".options = [ "subvol=var_log" "compress=zstd" "noatime" ]; \
filesystems."/var/log".neededForBoot = true ; \
\n' /mnt/etc/nixos/configuration.nix
Allow discards to encrypted root
sed -i '/boot = {/a\ boot.initrd.luks.devices."pool0_0".allowDiscards = true;' /mnt/etc/nixos/configuration.nix
Raid1
sed -i '/boot = {/a\ boot.initrd.luks.devices."pool0_0".allowDiscards = true;' /mnt/etc/nixos/configuration.nix
sed -i '/boot = {/a\ boot.initrd.luks.devices."pool0_1".allowDiscards = true;' /mnt/etc/nixos/configuration.nix
Configure Install
Choose either the configuration.nix
track or Nix Flakes
configuration.nix
/etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
imports =
[
./hardware-configuration.nix
];
boot = {
loader = {
efi = {
canTouchEfiVariables = false;
};
grub = {
enable = true;
device = "nodev"; # No device for EFI
efiSupport = true;
enableCryptodisk = false;
useOSProber = false;
efiInstallAsRemovable = true; # in case canTouchEfiVariables doesn't work for your system
};
};
supportedFilesystems = [
"btrfs"
"fat" "vfat" "exfat" "ntfs" # Microsoft
"cifs" # Windows Network Share
];
};
fileSystems."/".options = [ "subvol=root" "compress=zstd" "noatime" ];
fileSystems."/home".options = [ "subvol=home/active" "compress=zstd" "noatime" ];
fileSystems."/home/.snapshots".options = [ "subvol=home/snapshot" "compress=zstd" "noatime" ];
fileSystems."/nix".options = [ "subvol=nix" "compress=zstd" "noatime" ];
fileSystems."/persist".options = [ "subvol=persist" "compress=zstd" "noatime" ];
filesystems."/persist".neededForBoot = true ;
fileSystems."/var/local".options = [ "subvol=var_local/active" "compress=zstd" "noatime" ];
fileSystems."/var/local/.snapshots".options = [ "subvol=var_local/snapshot" "compress=zstd" "noatime" ];
fileSystems."/var/log".options = [ "subvol=var_log" "compress=zstd" "noatime" ];
filesystems."/var/log".neededForBoot = true ;
networking = {
hostName = "beef";
networkmanager.enable = true;
};
i18n.defaultLocale = "en_US.UTF-8";
security.sudo.extraConfig = ''
Defaults lecture = never
'';
services = {
cinnamon.apps.enable = false ;
btrbk = {
instances."btrbak" = {
onCalendar = "*-*-* *:00:00";
settings = {
timestamp_format = "long";
snapshot_preserve_min = "2d";
preserve_day_of_week = "sunday" ;
preserve_hour_of_day = "0" ;
target_preserve = "48h 10d 4w 12m 10y" ;
volume."/home" = {
snapshot_create = "always";
subvolume = ".";
snapshot_dir = ".snapshots";
};
volume."/var/local" = {
snapshot_create = "always";
subvolume = ".";
snapshot_dir = ".snapshots";
};
};
};
};
openssh = {
enable = true;
settings = {
PermitRootLogin = "yes" ;
};
};
xserver = {
enable = true;
layout = "us";
libinput.enable = true;
displayManager.gdm.enable = true ;
desktopManager.cinnamon.enable = true;
};
};
system.stateVersion = "23.05";
systemd.services = {
create-swapfile = {
serviceConfig.Type = "oneshot";
wantedBy = [ "swap-swapfile.swap" ];
script = ''
swapfile="/swap/swapfile"
if [ -f "$swapfile" ]; then
echo "Swap file $swapfile already exists, taking no action"
else
echo "Setting up swap file $swapfile"
${pkgs.coreutils}/bin/truncate -s 0 "$swapfile"
${pkgs.e2fsprogs}/bin/chattr +C "$swapfile"
fi
'';
};
};
time.timeZone = "America/Vancouver";
users.users.dave = {
description = "Dave Conroy";
createHome = true ;
home = "/home/dave" ;
shell = "/bin/sh" ; # This is actually bash
group = "users" ;
extraGroups = [ "wheel" "docker" ];
uid = 2323;
isNormalUser = true;
hashedPassword = "$y$j9T$jEYLXjGrR06/tp76fxyDq/$mX4GTWL7CjVXgAcS5nAHEiT6WIH8uD/IfXj16fuTRQ1";
packages = with pkgs; [
];
};
}
Flakes
Create Flake
{
description = "Tired of I.T! NixOS configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
impermanence.url = "github:nix-community/impermanence";
};
outputs = { nixpkgs ... }: {
nixosConfigurations = {
beef = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({ config, pkgs, ... }:
imports =
[
./hardware-configuration.nix
./configuration.nix
];
}
)
];
};
};
};
}
Install
configuration.nix
nixos-install
Flake
Need to fetch Nix Flakes and git to clone your repository
cp -R /mnt/etc/nixos /tmp/
nix-shell -p git nixFlakes
git clone https://github.com/tiredofit/sys.git /mnt/etc/nixos
Need to then activate the flake. The trailer of the line is the hostname
nixos-install --root /mnt --flake /mnt/etc/nixos#beef
Configuring Impermanence
Boot into new system make any changes. Afterwords, run script below to detect changes in snapshot vs root since boot and start working on maping files that need to be available upon each reboot.
Create differences script
#!/usr/bin/env bash
_tmp_root=$(mktemp -d)
mkdir -p "${_tmp_root}"
mount -o subvol=/ /dev/mapper/pool0_0 "${_tmp_root}" > /dev/null 2>&1
set -euo pipefail
OLD_TRANSID=$(sudo btrfs subvolume find-new ${_tmp_root}/root-blank 9999999)
OLD_TRANSID=${OLD_TRANSID#transid marker was }
sudo btrfs subvolume find-new "${_tmp_root}/root" "$OLD_TRANSID" | sed '$d' | cut -f17- -d' ' | sort | uniq |
while read path; do
path="/$path"
if [ -L "$path" ]; then
: # The path is a symbolic link, so is probably handled by NixOS already
elif [ -d "$path" ]; then
: # The path is a directory, ignore
else
echo "$path"
fi
done
umount "${_tmp_root}"
rm -rf "${_tmp_root}"
Create Configuration to reset root on each reboot
boot.initrd.postDeviceCommands = pkgs.lib.mkBefore ''
mkdir -p /mnt
mount -o subvol=/ /dev/mapper/pool0_0 /mnt
btrfs subvolume list -o /mnt/root | cut -f9 -d' ' |
while read subvolume; do
echo "Deleting /$subvolume subvolume"
btrfs subvolume delete "/mnt/$subvolume"
done &&
echo "Deleting /root subvolume" &&
btrfs subvolume delete /mnt/root
echo "Restoring blank /root subvolume"
btrfs subvolume snapshot /mnt/root-blank /mnt/root
umount /mnt
'';
RAID1
- Instead of relying on
postDeviceCommands
you can create a systemD service in initrd. This could also be used on single drive as well.
systemd = {
enable = true;
services.rollback = {
description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [
"initrd.target"
];
after = [
"systemd-cryptsetup@pool0_0.service"
];
before = [
"sysroot.mount"
];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /mnt
mount -o subvol=/ /dev/mapper/pool0_0 /mnt
btrfs subvolume list -o /mnt/root | cut -f9 -d' ' |
while read subvolume; do
echo "Deleting /$subvolume subvolume"
btrfs subvolume delete "/mnt/$subvolume"
done &&
echo "Deleting /root subvolume" &&
btrfs subvolume delete /mnt/root
echo "Restoring blank /root subvolume"
btrfs subvolume snapshot /mnt/root-blank /mnt/root
umount /mnt
'';
};
};
Configuring Impermanence
{ config, pkgs, ... }:
let
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in
{
imports = [ "${impermanence}/nixos.nix" ];
# this folder is where the files will be stored (don't put it in tmpfs)
environment.persistence."/persist" = {
hideMounts = true ;
directories = [
"/etc/nixos" # NixOS
"/etc/NetworkManager" # NetworkManager
{ directory = "/var/lib/colord";
user = "colord";
group = "colord";
mode = "u=rwx,g=rx,o="; } # Colord Profiles
"/var/lib/bluetooth" # Bluetooth
"/var/lib/docker" # Docker
];
files = [
"/etc/ssh/ssh_host_*" # SSH
"/etc/machine-id" # Needed for SystemD Journal
"/var/lib/NetworkManager/secret_key" # Network Manager
"/var/lib/NetworkManager/seen-bssids" # Network Manager
"/var/lib/NetworkManager/timestamps" # Network Manager
{ file = "/etc/nix/id_rsa"; parentDirectory = { mode = "u=rwx,g=,o="; }; } # Nix
];
};cnn.com
Persistence Rules
Bluetooth
systemd.tmpfiles.rules = [
"L /var/lib/bluetooth - - - - /persist/var/lib/bluetooth"
];
##
sudo mkdir -p /persist/var/lib/bluetooth
Docker
virtualisation = {
docker.enable = true;
};
systemd.tmpfiles.rules = [
"L /var/lib/docker - - - - /persist/var/lib/docker"
];
##
sudo systemctl stop docker
sudo mkdir -p /persist/var/lib/
sudo cp -r {,/persist}/var/lib/docker
Network Manager
environment.etc."NetworkManager/system-connections" = {
source = "/persist/etc/NetworkManager/system-connections/";
};
systemd.tmpfiles.rules = [
"L /var/lib/NetworkManager/secret_key - - - - /persist/var/lib/NetworkManager/secret_key"
"L /var/lib/NetworkManager/seen-bssids - - - - /persist/var/lib/NetworkManager/seen-bssids"
"L /var/lib/NetworkManager/timestamps - - - - /persist/var/lib/NetworkManager/timestamps"
];
##
sudo mkdir -p /persist/etc/NetworkManager
sudo cp -r {,/persist}/etc/NetworkManager/system-connections
sudo mkdir -p /persist/var/lib/NetworkManager
sudo cp /var/lib/NetworkManager/{secret_key,seen-bssids,timestamps} /persist/var/lib/NetworkManager/
NixOS
environment.etc = {
nixos.source = "/persist/etc/nixos";
NIXOS.source = "/persist/etc/NIXOS";
machine-id.source = "/persist/etc/machine-id";
};
sudo cp -R {,/persist}/etc/nixos
sudo cp -R {,/persist}/etc/NIXOS
sudo cp {,/persist}/etc/machine-id
OpenSSH
services.openssh = {
enable = true;
hostKeys = [
{
path = "/persist/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
{
path = "/persist/etc/ssh/ssh_host_rsa_key";
type = "rsa";
bits = 4096;
}
];
};
##
sudo mkdir -p /persist/etc/ssh
sudo cp -R /etc/ssh/ssh_host* /persist/etc/ssh/
Keeping up to date
Normal
sudo nix-channel --update
sudo nixos-rebuild switch
Flakes
sudo nix flake update /etc/nixos/
sudo nixos-rebuild switch --flake /etc/nixos/#beef
Scratchpad
Unmount commands
umount /mnt/boot
umount /mnt/home
umount /mnt/nix
umount /mnt/persist
umount /mnt/var/local
umount /mnt/var/log
umount /mnt
Restart Install over and over
umount /mnt/boot
umount /mnt/home/.snapshots
umount /mnt/home
umount /mnt/nix
umount /mnt/persist
umount /mnt/swap
umount /mnt/var/local/.snapshots
umount /mnt/var/local
umount /mnt/var/log
umount /mnt/boot/efi
umount /mnt
PASSWORD=password
DISK_SWAP_SIZE_GB=4
DISK_EFI_SIZE_MB=512
DISK=/dev/vda
## Swap Partition
wipefs "${DISK}" -a -f
sgdisk --zap-all "${DISK}"
sgdisk --clear \
--new=1:0:+"${DISK_EFI_SIZE_MB}"MiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:+"${DISK_SWAP_SIZE_GB}"GiB --typecode=2:8200 --change-name=2:swap \
--new=3:0:0 --typecode=3:8300 --change-name=3:pool0_0 \
"${DISK}"
EFI_PARTITION=1
SWAP_PARTITION=2
CRYPT_PARTITION=3
## Swap File
wipefs "${DISK}" -a -f
sgdisk --zap-all "${DISK}"
sgdisk --clear \
--new=1:0:+"${DISK_EFI_SIZE_MB}"MiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:0 --typecode=2:8300 --change-name=2:pool0_0 \
"${DISK}"
EFI_PARTITION=1
CRYPT_PARTITION=2
echo "${PASSWORD}" | cryptsetup luksFormat "${DISK}""${CRYPT_PARTITION}"
echo "${PASSWORD}" | cryptsetup open "${DISK}""${CRYPT_PARTITION}" pool0_0
mkfs.btrfs -f /dev/mapper/pool0_0
mkfs.vfat "${DISK}""${EFI_PARTITION}"
mount -t btrfs /dev/mapper/pool0_0 /mnt
btrfs subvolume create /mnt/root
mkdir -p /mnt/home
btrfs subvolume create /mnt/home/active
btrfs subvolume create /mnt/home/snapshots
btrfs subvolume create /mnt/nix
btrfs subvolume create /mnt/persist
btrfs subvolume create /mnt/swap
mkdir -p /mnt/var_local
btrfs subvolume create /mnt/var_local/active
btrfs subvolume create /mnt/var_local/snapshots
btrfs subvolume create /mnt/var_log
btrfs subvolume snapshot -r /mnt/root /mnt/root-blank
umount /mnt
mount -o subvol=root,compress=zstd,noatime /dev/mapper/pool0_0 /mnt
mkdir -p /mnt/home
mount -o subvol=home/active,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/home
mkdir -p /mnt/home/.snapshots
mount -o subvol=home/snapshots,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/home/.snapshots
mkdir -p /mnt/nix
mount -o subvol=nix,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/nix
mkdir -p /mnt/persist
mount -o subvol=persist,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/persist
mkdir -p /mnt/swap
mount -o subvol=swap,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/swap
mkdir -p /mnt/var/local
mount -o subvol=var_local/active,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/var/local
mkdir -p /mnt/var/local/.snapshots
mount -o subvol=var_local/snapshots,compress=zstd,noatime /dev/mapper/pool0_0 /mnt/var/local/.snapshots
mkdir -p /mnt/boot
mount -o defaults,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro /dev/disk/by-partlabel/EFI /mnt/boot
mkdir -p /mnt/swap
touch /mnt/swap/swapfile
chattr +C /mnt/swap/swapfile
dd if=/dev/zero of=mnt/swap/swapfile bs=1024 count=0 seek=1G
mkswap /mnt/swap/swapfile
chmod 600 /mnt/swap/swapfile
swapon /mnt/swap/swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=1048576
mkswap "${SWAP_PARTITION}
swapon "${SWAP_PARTITION}"
nixos-generate-config --root /mnt
sed -i '/boot = {/i \
# Set Filesystms to noatime and zstd compression
fileSystems."/".options = [ "subvol=root" "compress=zstd" "noatime" ]; \
fileSystems."/home".options = [ "subvol=home/active" "compress=zstd" "noatime" ]; \
fileSystems."/home/.snapshots".options = [ "subvol=home/snapshot" "compress=zstd" "noatime" ]; \
fileSystems."/nix".options = [ "subvol=nix" "compress=zstd" "noatime" ]; \
fileSystems."/persist".options = [ "subvol=persist" "compress=zstd" "noatime" ]; \
filesystems."/persist".neededForBoot = true ; \
fileSystems."/var/local".options = [ "subvol=var_local/active" "compress=zstd" "noatime" ]; \
fileSystems."/var/local/.snapshots".options = [ "subvol=var_local/snapshot" "compress=zstd" "noatime" ]; \
fileSystems."/var/log".options = [ "subvol=var_log" "compress=zstd" "noatime" ]; \
filesystems."/var/log".neededForBoot = true ; \
\n' /mnt/etc/nixos/configuration.nix
rm -rf /mnt/etc/nixos/configuration.nix
nano /mnt/etc/nixos/configuration.nix
Full configuration.nix
{ config, lib, modulesPath, pkgs, ... }:
let
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
"${impermanence}/nixos.nix"
];
boot = {
extraModulePackages = [ ];
extraFiles = {}
initrd = {
availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sr_mod" "virtio_blk" ];
luks = {
devices = {
"pool0_0" = {
allowDiscards = true;
bypassWorkqueues = true;
device = "/dev/disk/by-uuid/2e2cef00-ed7a-41fa-b173-b1ff9bdcbfeb";
};
"pool0_1" = {
allowDiscards = true;
bypassWorkqueues = true;
device = "/dev/disk/by-uuid/722ec824-876d-471b-ae30-5107f1e8264d";
};
};
};
supportedFilesystems = [
"btrfs"
"vfat"
];
};
systemd = {
enable = true;
services.rollback = {
description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [
"initrd.target"
];
after = [
"systemd-cryptsetup@enc.service"
];
before = [
"sysroot.mount"
];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /mnt
mount -o subvol=/ /dev/mapper/pool0_0 /mnt
btrfs subvolume list -o /mnt/root | cut -f9 -d' ' |
while read subvolume; do
echo "Deleting /$subvolume subvolume"
btrfs subvolume delete "/mnt/$subvolume"
done &&
echo "Deleting /root subvolume" &&
btrfs subvolume delete /mnt/root
echo "Restoring blank /root subvolume"
btrfs subvolume snapshot /mnt/root-blank /mnt/root
umount /mnt
'';
};
services.persisted-files = {
description = "Hard-link persisted files from /persist";
wantedBy = [
"initrd.target"
];
after = [
"sysroot.mount"
];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /sysroot/etc/
ln -snfT /persist/etc/machine-id /sysroot/etc/machine-id
'';
};
};
kernelModules = [ ];
loader = {
efi = {
canTouchEfiVariables = false;
};
grub = {
enable = true;
device = "nodev";
efiSupport = true;
enableCryptodisk = false;
useOSProber = false;
efiInstallAsRemovable = true;
};
};
};
environment = {
persistence = {
"/persist" = {
hideMounts = true ;
directories = [
"/etc/nixos"
"/etc/NetworkManager"
"/root"
"/var/lib/NetworkManager"
];
};
};
systemPackages = with pkgs; [
curl
nano
wget
];
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
options = [ "subvol=root" "compress=zstd" "noatime" ];
};
"/boot" = {
device = "/dev/disk/by-uuid/6DD4-987C";
fsType = "vfat";
};
"/home" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
neededForBoot = true;
options = [ "subvol=home/active" "compress=zstd" "noatime" ];
};
"/home/.snapshots" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
options = [ "subvol=home/snapshots" "compress=zstd" "noatime" ];
};
"/nix" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
options = [ "subvol=nix" "compress=zstd" "noatime" ];
};
"/persist" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
neededForBoot = true;
options = [ "subvol=persist" "compress=zstd" "noatime" ];
};
"/var/local" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
options = [ "subvol=var_local/active" "compress=zstd" "noatime" ];
};
"/var/local/.snapshots" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
options = [ "subvol=var_local/snapshots" "compress=zstd" "noatime" ];
};
"/var/log" = {
device = "/dev/disk/by-uuid/1b864b78-f28a-4730-8efd-759b895b567e";
fsType = "btrfs";
neededForBoot = true;
options = [ "subvol=var_log" "compress=zstd" "noatime" ];
};
};
networking = {
hostName = "nixos";
networkmanager.enable = true ;
useDHCP = lib.mkDefault true;
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
security.sudo = {
extraConfig = ''
Defaults lecture = never
'';
wheelNeedsPassword = false ;
};
services.openssh.enable = true;
swapDevices = [
{ device = "/dev/disk/by-uuid/c179fdb0-c9c2-42d8-bf12-8da81fdbef30"; }
];
system.stateVersion = "23.05";
time.timeZone = "America/Vancouver";
users.users.dave = {
createHome = true ;
description = "Dave";
extraGroups = [ "wheel" ];
group = "users" ;
hashedPassword = "hashed password";
home = "/home/dave" ;
isNormalUser = true;
shell = "/bin/sh" ;
uid = 2323;
};
}
Setup Impermanance
nixos-rebuild boot
sudo mkdir -p /persist/var/lib/
sudo cp -r {,/persist}/var/lib/docker
nano /etc/nixos/configuration.nix
sudo mkdir -p /persist/etc/NetworkManager
sudo cp -r {,/persist}/etc/NetworkManager/system-connections
sudo mkdir -p /persist/var/lib/NetworkManager
sudo cp /var/lib/NetworkManager/{secret_key,seen-bssids,timestamps} /persist/var/lib/NetworkManager/
sudo mkdir -p /persist/etc/ssh
sudo cp -R /etc/ssh/ssh_host* /persist/etc/ssh/
sudo cp -R {,/persist}/etc/nixos
sudo cp -R {,/persist}/etc/NIXOS
sudo cp {,/persist}/etc/machine-id
reboot
Errors
Encrypted /boot
When mounting the ESP partition at /boot/efi
instead of /boot
grub picks the mounted pool0_0 filesystem and puts it in its configuration.
Idea - Creating a seperate LUKS boot partition??