11 KiB
Installation
This installation guide will walk you through the process of setting up NixOS, going from the minimal ISO to a fully configured system.
This guide is written primarily as a reference for myself, but it can certainly be a useful resource for you if you're new to NixOS, and you want to set everything up yourself. I have purposefully not used my flake here, and I instead show a setup that follows a fairly minimal configuration, to allow anyone to use this guide as a starting step to creating their own configuration.
This guide is split into multiple files, some of which are skippable/optional. This file will show you how to get from the minimal ISO to a very simple almost blank NixOS system. This configuration will involve setting up LUKS encryption with BTRFS file-system, and assumes a single drive. However, it should be fairly easy to adjust things, set up additional drives, etc.
Partitioning
First thing we will need to do is set up partitions. To do so, I recommend using fdisk
.
Assuming you have a single-disk system, you will want to create 3 partitions:
- EFI (1 GB)
- Swap (same size as your RAM, or more)
- Data (rest)
The swap partition is optional, however I do recommend creating it (instead of using a swap file), as it will allow you to hibernate your machine.
Important
Don't forget to also set the type for these partitions (
t
command infdisk
). Most importantly for the EFI partition, as NixOS will fail to install if your boot partition doesn't have the EFI type. Although it is generally a good idea to also set a type for all of your partitions anyway.
- EFI partition type: EFI System (1)
- Swap partition type: Linux swap (19)
- Data partition type: Linux filesystem (20)
File-Systems
Now we'll to create file systems on these partitions, and give them disk labels:
mkfs.fat -F 32 /dev/sdX1
fatlabel /dev/sdX1 NIXBOOT
mkswap -L SWAP /dev/diskX2
cryptsetup luksFormat /dev/sdX3 --label NIXCRYPTFS
cryptsetup open /dev/disk/by-label/NIXCRYPTFS crypfs
mkfs.btrfs -L NIXFS /dev/mapper/cryptfs
Note
For the LUKS encrypted partitions, I'd heavily recommend that you back up the LUKS headers in case of a partial drive failure, so that you're still able to recover your remaining data. To do this, you can use the following command:
cryptsetup luksHeaderBackup /dev/device --header-backup-file /mnt/backup/file.img
BTRFS Subvolumes
Now we will split our btrfs partition into the following subvolumes:
- root: The subvolume for
/
, which can be cleared on every boot. - home: The subvolume for
/home
, which should be persisted across reboots and get backed up (snapshotting). - nix: The subvolume for
/nix
, which needs to be persistent, but not worth snapshotting, as it's trivial to reconstruct. - log: The subvolume for
/var/log
, which should be persisted, and optionally backed up. - persist: The subvolume for
/persist
, containing system-wide state, which should be persisted and backed up. - data: The subvolume for
/data
, containing my personal files, which should be persisted and backed up.
Tip
If you do not wish to set up impermanence (wiping root partition after every boot), you won't need the persist subvolume. Depending on your preferences, you can also get rid of the
/var/log
subvolume.It is very easy to add new BTRFS subvolumes later on, or adjust existing ones (even removing is usually quite straightforward), so don't be too afraid if you don't yet know if the structure you go with will meet your needs.
mount /dev/mapper/crypfs /mnt
btrfs subvolume create /mnt/root
btrfs subvolume create /mnt/home
btrfs subvolume create /mnt/nix
btrfs subvolume create /mnt/log
btrfs subvolume create /mnt/persist
btrfs subvolume create /mnt/data
umount /mnt
Mount the partitions and subvolumes
Note
Even though we're specifying the
compress
flag in the mount options of each btrfs subvolume, somewhat misleadingly, you can't actually use different compression levels for different subvolumes. Btrfs will share the same compression level across the whole partition, so it's pointless to attempt to set different values here.
Note
You may have seen others use btrfs options such as
ssd
,discard=async
andspace_cache=v2
. These are all default (with thessd
being auto-detected), so specifying them is pointless now.
mount -o subvol=root,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt
mount --mkdir -o subvol=home,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt/home
mount --mkdir -o subvol=nix,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt/nix
mount --mkdir -o subvol=log,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt/var/log
mount --mkdir -o subvol=persist,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt/persist
mount --mkdir -o subvol=data,compress=zstd:3,noatime /dev/mapper/cryptfs /mnt/data
mount --mkdir /dev/disk/by-label/NIXBOOT /mnt/boot
swapon /dev/disk/by-label/SWAP
Generate hardware configuration
NixOS can now automatically figure out the system configuration for you:
nixos-generate-config --root /mnt
This should result with /mnt/etc/nixos/hardware-configuration.nix
being created.
We will now want to make some adjustments to this file. Let's first install neovim, because the minimal nix iso only
provides nano
, and I simply refuse to use that software:
nix-env -iA nixos.neovim
nvim /mnt/etc/nixos/hardware-configuration.nix
Disk labels
In here, you will notice that NixOS is using UUIDs instead of disk labels for mounting. You will want to adjust this, as labels are more reliable, since they won't change if you move the disks around (like changing the sata ports). It also makes the configuration much more readable.
You will see something like this:
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-uuid/08047b54-10af-4579-bb58-6af549b5c13e";
Which you will want to change to:
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-label/NIXCRYPTFS";
A bunch of entries for our btrfs partition:
fileSystems."/" =
{ device = "/dev/disk/by-uuid/61b2d710-2508-4849-9613-b52fbc62bcf5";
fsType = "btrfs";
options = [ "subvol=root" ];
};
Where you will change the device
like so:
fileSystems."/" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=root" ];
};
Do this for all BTRFS entries.
Note
If you see the root file system (or any other) declared multiple times, it is safe to remove the duplicate definitions.
Now change the /boot
partition entry from:
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/6383-E5C1";
fsType = "vfat";
};
To:
fileSystems."/boot" =
{ device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
};
And finally the swap partition from:
swapDevices =
[ { device = "/dev/disk/by-uuid/cb8cd9b7-8824-4a59-9249-89b5b2df0dbc"; }
];
To:
swapDevices =
[ { device = "/dev/disk/by-label/SWAP"; }
];
BTRFS options
You may notice that your mount options were not automatically picked up by the automatic config generation. That's because NixOS hardware scanner isn't capable of detecting these. That means you will want to specify these options for each BTRFS subvolume yourself. Let's add them:
fileSystems."/" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=root" ];
};
To the following:
fileSystems."/" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=root" "noatime" "compress=zstd:3" ];
};
(Make sure to not overwrite the subvol
though, if you're copy-pasting)
Subvolumes needed for boot
In order to correctly persist /var/log
, the respective subvolume need to be mounted early enough in
the boot process. To do this, we will want to add neededForBoot = true;
. Additionally, if you will be
following up with impermanence, you will also need to add this parameter for our /persist
subvolume.
This is because we will be storing the user password (including root password) in a password file there
(mentioned later on, in the impermanence guide).
So the entries will look like this:
fileSystems."/var/log" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=log" "noatime" "compress=zstd:3" ];
neededForBoot = true;
};
fileSystems."/persist" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=persist" "noatime" "compress=zstd:3" ];
neededForBoot = true;
};
Minimal config
Although it is possible to customize /etc/nixos/configuration.nix
at this point to set up all the things you need in
one fell swoop, I recommend starting out with a relatively minimal config, to make sure everything works ok. I went with
something like this, with a user called itsdrike
:
{ config, lib, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
boot.supportedFilesystems = [ "btrfs" ];
hardware.enableAllFirmware = true;
nixpkgs.config.allowUnfree = true;
# Use the systemd-boot EFI boot loader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "pc"; # Define your hostname
networking.networkmanager.enable = true;
# Define a user account.
users.users.itsdrike = {
isNormalUser = true;
extraGroups = [ "wheel" ]; # Enable 'sudo' for the user.
};
# Install an actually usable editor
programs.neovim = {
enable = true;
defaultEditor = true;
vimAlias = true;
viAlias = true;
};
# Enable SSH daemon
# (uncomment if you want SSH immediately)
#services.openssh = {
# enable = true;
# settings.PermitRootLogin = "yes";
#};
# Set this to the auto-generated value originally present in this file
system.stateVersion = "23.11";
}
Installation
Take a deep breath.
nixos-install
reboot
(Note: You will be asked for the root password at the end of nixos-install
)
If all goes well, we'll be prompted for the passphrase to decrypt our disk, and then be greeted with the usual TTY login
screen. Log in as root, set your password (passwd itsdrike
), log out and re-login as your unprivileged user.
You can now move on to the next file: IMPERMANENCE.