NixOS Installation Guide: ZFS + LUKS + zram + Separate Partitions

This guide will walk you through installing NixOS with:

Warning: This process will completely erase your target disk. Make sure to backup any important data before proceeding.

Step 1: Boot from NixOS Installation Media

Boot from the NixOS installation media (USB drive or DVD) and open a terminal.

Step 2: Partition the Disk

We'll use sgdisk to partition the disk. Replace /dev/sda with your actual disk (find it with lsblk):

# Create EFI boot partition (512MB)
sgdisk -n 1:1M:+512M -t 1:EF00 /dev/sda

# Create main partition for LUKS container (rest of disk)
sgdisk -n 2:0:0 -t 2:8300 /dev/sda

Step 3: Set Up LUKS Encryption

# Format the second partition as LUKS container
cryptsetup --type luks2 luksFormat /dev/sda2

# Open the LUKS container
cryptsetup luksOpen /dev/sda2 encrypted_root

Step 4: Create Inner Partitions in LUKS Container

# Create ZFS partition (everything except space for swap)
# First, let's check the size of the LUKS container
blockdev --getsize64 /dev/mapper/encrypted_root

# Create swap partition (8GB) and ZFS partition (remaining space)
# Using sgdisk on the mapped device
echo -e "start=\$(sgdisk -E /dev/mapper/encrypted_root)\nsgdisk -n 1:0:+8G /dev/mapper/encrypted_root\nsgdisk -n 2:0:\${start} /dev/mapper/encrypted_root" | bash

# Format swap
mkswap -L swap /dev/mapper/encrypted_root1
swapon /dev/mapper/encrypted_root1

Step 5: Create ZFS Pool and Datasets

# Create ZFS pool on the second inner partition
zpool create -f -O encryption=on -O keyformat=passphrase -O keylocation=prompt \
    -O compression=lz4 -O atime=off -O normalization=formD -O xattr=sa -O acltype=posixacl \
    -m none -R /mnt rpool /dev/mapper/encrypted_root2

# Create ZFS datasets for root and home
zfs create -o mountpoint=legacy rpool/ROOT
zfs create -o mountpoint=legacy rpool/ROOT/root
zfs create -o mountpoint=legacy rpool/ROOT/nix
zfs create -o mountpoint=legacy rpool/home

# Create a blank snapshot for potential rollbacks
zfs snapshot rpool/ROOT/root@blank

Step 6: Mount Filesystems

# Mount the ZFS datasets
mount -t zfs rpool/ROOT/root /mnt
mkdir -p /mnt/nix /mnt/home
mount -t zfs rpool/ROOT/nix /mnt/nix
mount -t zfs rpool/home /mnt/home

# Mount the boot partition
mkdir -p /mnt/boot
mount /dev/sda1 /mnt/boot

Step 7: Generate Base Configuration

nixos-generate-config --root /mnt

Step 8: Configure NixOS

Edit /mnt/etc/nixos/configuration.nix to include the following settings:

{ config, pkgs, ... }:

{
  imports = [
    # Include the results of the hardware scan
    ./hardware-configuration.nix
  ];

  # Bootloader configuration
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  
  # Enable ZFS support
  boot.supportedFilesystems = [ "zfs" ];
  
  # ZFS auto-snapshot service
  services.zfs.autoSnapshot.enable = true;
  
  # ZFS auto-trim service
  services.zfs.trim.enable = true;

  # Filesystem mappings
  fileSystems."/" = {
    device = "rpool/ROOT/root";
    fsType = "zfs";
  };
  
  fileSystems."/nix" = {
    device = "rpool/ROOT/nix";
    fsType = "zfs";
  };
  
  fileSystems."/home" = {
    device = "rpool/home";
    fsType = "zfs";
  };
  
  fileSystems."/boot" = {
    device = "/dev/disk/by-uuid/$(blkid -s UUID -o value /dev/sda1)";
    fsType = "vfat";
  };

  # LUKS encryption settings
  boot.initrd.luks.devices."encrypted_root".device = "/dev/disk/by-uuid/$(blkid -s UUID -o value /dev/sda2)";

  # Enable zram swap (instead of disk swap)
  zramSwap = {
    enable = true;
    memoryPercent = 100;  # Use up to 100% of RAM (will be limited to ~8GB by default)
    priority = 100;
    algorithm = "zstd";   # Efficient compression algorithm
  };

  # Additional settings (customize as needed)
  networking.hostName = "nixos"; # Define your hostname here
  networking.networkmanager.enable = true;
  
  # Select internationalisation properties
  i18n.defaultLocale = "en_US.UTF-8";
  console.keyMap = "us";

  # Enable the OpenSSH daemon
  services.openssh.enable = true;

  # Define a user account
  users.users.yourusername = {
    isNormalUser = true;
    description = "Your Name";
    extraGroups = [ "networkmanager" "wheel" ];
    packages = with pkgs; [
      # Add packages for the user
    ];
  };

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It's perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "24.05"; # Change this when first installing NixOS
}

Step 9: Install NixOS

nixos-install

Step 10: Set Root Password

passwd

Step 11: Reboot

reboot

Alternative: Automated Installation Script

As an alternative, you can use this automated script that performs most of the above steps:

#!/usr/bin/env bash
# NixOS install with ZFS on LUKS encryption and zram swap
#
# sda
# ├─sda1 BOOT
# └─sda2 LINUX (LUKS CONTAINER)
#   ├─encrypted_root1 SWAP
#   └─encrypted_root2 ZFS

set -e

pprint () {
    local cyan="\e[96m"
    local default="\e[39m"
    # ISO8601 timestamp + ms
    local timestamp
    timestamp=$(date +%FT%T.%3NZ)
    echo -e "${cyan}${timestamp} $1${default}" 1>&2
}

# Set DISK - adjust this to your disk
DISK="/dev/sda"

read -p "> Do you want to wipe all data on $DISK ?" -n 1 -r
echo # move to a new line
if [[ "$REPLY" =~ ^[Yy]$ ]]
then
    # Clear disk
    wipefs -af "$DISK"
    sgdisk -Zo "$DISK"
fi

pprint "Creating boot (EFI) partition"
sgdisk -n 1:1M:+512M -t 1:EF00 "$DISK"
BOOT="$DISK""1"

pprint "Creating Linux partition"
sgdisk -n 2:0:0 -t 2:8300 "$DISK"
LINUX="$DISK""2"

# Inform kernel
partprobe "$DISK"
sleep 1

pprint "Format BOOT partition $BOOT"
mkfs.vfat "$BOOT"

pprint "Creating LUKS container on $LINUX"
cryptsetup --type luks2 luksFormat "$LINUX"

LUKS_DEVICE_NAME=encrypted_root
cryptsetup luksOpen "$LINUX" "$LUKS_DEVICE_NAME"
LUKS_DISK="/dev/mapper/$LUKS_DEVICE_NAME"

# SWAP partition (8GB)
sgdisk -n 1:0:+8G -t 1:8200 $LUKS_DISK
SWAP="${LUKS_DISK}1"

# ZFS partition (remaining space)
sgdisk -n 2:0:0 -t 2:8300 $LUKS_DISK
ZFS="${LUKS_DISK}2"

# Inform kernel
partprobe "$LUKS_DISK"
sleep 1

pprint "Enable SWAP on $SWAP"
mkswap $SWAP
swapon $SWAP

pprint "Create ZFS pool on $ZFS"
zpool create -f -O encryption=on -O keyformat=passphrase -O keylocation=prompt \
    -O compression=lz4 -O atime=off -O normalization=formD -O xattr=sa -O acltype=posixacl \
    -m none -R /mnt rpool "$ZFS"

pprint "Create ZFS datasets"
zfs create -o mountpoint=legacy rpool/ROOT
zfs create -o mountpoint=legacy rpool/ROOT/root
zfs create -o mountpoint=legacy rpool/ROOT/nix
zfs create -o mountpoint=legacy rpool/home
zfs snapshot rpool/ROOT/root@blank

pprint "Mount ZFS datasets"
mount -t zfs rpool/ROOT/root /mnt
mkdir -p /mnt/nix
mount -t zfs rpool/ROOT/nix /mnt/nix
mkdir -p /mnt/home
mount -t zfs rpool/home /mnt/home
mkdir -p /mnt/boot
mount "$BOOT" /mnt/boot

pprint "Generate NixOS configuration"
nixos-generate-config --root /mnt

# Add LUKS configuration to hardware-configuration.nix
LINUX_DISK_UUID=$(blkid --match-tag UUID --output value "$LINUX")
HARDWARE_CONFIG=$(mktemp)
cat < "$HARDWARE_CONFIG"
boot.initrd.luks.devices."$LUKS_DEVICE_NAME".device = "/dev/disk/by-uuid/$LINUX_DISK_UUID";
boot.zfs.devNodes = "/dev/disk/by-partuuid/";
CONFIG

pprint "Append configuration to hardware-configuration.nix"
sed -i "\$e cat $HARDWARE_CONFIG" /mnt/etc/nixos/hardware-configuration.nix

pprint "Installation prepared. Now edit /mnt/etc/nixos/configuration.nix to add zram swap and other settings, then run nixos-install"

Troubleshooting

Unlocking LUKS during boot

If you encounter issues unlocking the LUKS container during boot, you may need to enter the passphrase at boot time. The system will prompt you for the encryption passphrase before mounting the root filesystem.

ZFS Pool Import Issues

If the ZFS pool doesn't import correctly at boot, you can manually import it:

zpool import -f rpool

Checking zram Status

After installation, you can verify zram is working:

cat /proc/swaps
zcat /sys/block/zram*/mm_stat

Maintenance Tips

For more information about NixOS installation, refer to the official NixOS manual.