Rewrite installation guide

This commit is contained in:
ItsDrike 2024-04-08 04:30:24 +02:00
parent 718a55f595
commit 9d867d56bd
Signed by: ItsDrike
GPG key ID: FA2745890B7048C0
5 changed files with 865 additions and 815 deletions

View file

@ -1,815 +0,0 @@
# Installation
This will walk you through the installation process from the minimal NixOS ISO to a system configured to use my flake.
This guide will walk you through setting up LUKS encryption with BTRFS filesystem.
The system can optionally have the root directory wiped after every reboot. Such a setup is possible because
NixOS only needs `/boot` and `/nix` in order to boot, all other system files are simply links to files in `/nix`.
## 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 in `fdisk`).
> 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:
```shell
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
```
### BTRFS Subvolumes
Now we will split our btrfs partition into the following subvolumes:
- root: The subvolume for `/`, which will 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.
```shell
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` and `space_cache=v2`.
> These are all default (with the `ssd` being auto-detected), so specifying them is pointless now.
```shell
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:
```shell
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:
```shell
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:
```nix
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-uuid/08047b54-10af-4579-bb58-6af549b5c13e";
```
Which you will want to change to:
```nix
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-label/NIXCRYPTFS";
```
A bunch of entries for our btrfs partition:
```nix
fileSystems."/" =
{ device = "/dev/disk/by-uuid/61b2d710-2508-4849-9613-b52fbc62bcf5";
fsType = "btrfs";
options = [ "subvol=root" ];
};
```
Where you will change the `device` like so:
```nix
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:
```nix
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/6383-E5C1";
fsType = "vfat";
};
```
To:
```nix
fileSystems."/boot" =
{ device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
};
```
And finally the swap partition from:
```nix
swapDevices =
[ { device = "/dev/disk/by-uuid/cb8cd9b7-8824-4a59-9249-89b5b2df0dbc"; }
];
```
To:
```nix
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:
```nix
fileSystems."/" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=root" ];
};
```
To the following:
```nix
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, we will also
need to add this parameter for our `/persisr` subvolume. This is because we will be storing the user
password (including root password) in a password file there (mentioned later on).
So the entries will look like this:
```nix
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`:
```nix
{ 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.
```shell
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.
## Automatic root subvolume wiping
This is an optional step, if you don't want your root partition to get auto-reset on each boot, you can simply skip this.
### Auto-wipe root partition
To reset the root subvolume on every boot, we can simply delete it and create a new one in its place. We will be doing
this from initrd, which runs in a temporary file-system, before the actual file-system is properly mounted (following
fstab). This makes it a perfect place to run a script, which will wipe the root subvolume before each boot.
I will set this up using a systemd-based initrd, because I will need systemd for TPM unlocking later on. If you don't
care about that, it is also possible to do this without systemd. You can a guide for such setup
[here](https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html#darling-erasure). That said, I find this to be
a cleaner setup than the non-systemd one anyway, so it might be worth it for you to follow this regardless. However, do
note that using systemd in initrd may result in slightly slower boot times.
To achieve this, let's add the following to our `configuration.nix`:
```nix
boot.initrd.systemd = {
enable = true; # This enables systemd support in stage 1 - required for below setup
services.rollback = {
description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ];
# make sure it's done after decryption (i.e. LUKS/TPM process)
after = [ "systemd-cryptsetup@cryptfs.service" ];
# mount the root fs before clearing
before = [ "sysroot.mount" ];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
# Mount the BTRFS root to /mnt so we can manipulate btrfs subvolumes
mount --mkdir /dev/mapper/cryptfs /mnt
# Simply deleting a subvolume with btrfs subvolume delete will not work,
# if that subvolume contains other btrfs subvolumes. Because of that, we
# instead use this function to delete subvolumes, whihc will first perform
# a recursive deletion of any nested subvolumes.
#
# This is necessary, because the root subvolume will actually usually contain
# other subvolumes, even if the user haven't created those explicitly. It seems
# that NixOS creates these automatically. Namely, I observed these in root subvol:
# - root/srv
# - root/var/lib/portables
# - root/var/lib/machines
# - root/var/tmp
delete_subvolume_recursively() {
IFS=$'\n'
for x in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/mnt/$x"
done
echo "Deleting subvolume $1"
btrfs subvolume delete "$1"
}
# Recreate the root subvolume
delete_subvolume_recursively "/mnt/root"
echo "Re-creating root subvolume"
btrfs subvolume create "/mnt/root"
# we can now unmount /mnt and continue on the boot process.
umount /mnt
'';
};
};
```
### Impermanence
What this implies is that certain files, such as saved networks for network-manager will be deleted on each reboot.
While a little clunky, [Impermanence](https://github.com/nix-community/impermanence) is a great solution to our problem.
Impermanence adds a `environment.persistence."<dirName>"` option, that we can use to make certain directories or files
permanent. A sample configuration module for this can look like so:
```nix
{ config, pkgs, ... }:
let
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in
{
imports = [ "${impermanence}/nixos.nix" ];
# Some people use /nix/persist/system instead, leaving the persistent files in /nix subvolume
# I much prefer using a standalone subvolume for this though.
environment.persistence."/persist/system" = {
hideMounts = true;
directories = [
"/etc/nixos" # nixos configuration source
"/etc/NetworkManager/system-connections" # saved network connections
"/var/db/sudo" # keeps track of who got the sudo lecture already
"/var/lib/systemd/coredump" # recorded coredumps
];
files = [
"/etc/machine-id"
];
};
# For some reason, NetworkManager needs this instead of the impermanence mode to not get screwed up
systemd.tmpfiles.rules = [
"L /var/lib/NetworkManager/secret_key - - - - /persist/system/var/lib/NetworkManager/secret_key"
"L /var/lib/NetworkManager/seen-bssids - - - - /persist/system/var/lib/NetworkManager/seen-bssids"
"L /var/lib/NetworkManager/timestamps - - - - /persist/system/var/lib/NetworkManager/timestamps"
];
# Define host key paths in the persistent mount point instead of using impermanence for these.
# This works better, because these keys also get auto-created if they don't already exist.
services.openssh.hostKeys = mkForce [
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
}
```
You can put this module in `/etc/nixos/impermanence.nix`, and add it to your `imports` in `configuration.nix`.
Additionally, you may also want to move the `boot.initrd.systemd` configuration to this file.
Alternatively, you can of course also extend your `configuration.nix` adding this in directly, and keeping
everything in the same place.
#### User configuration
Note that with impermanence, your user passwords will get erased too (with the `/etc/shadow` file). To avoid this,
you can create password files, which will contain the password hashes for each user:
```shell
mkdir -p /persist/passwords
chmod 700 /persist/passwords
mkpasswd -m sha-512 > /persist/passwords/root
mkpasswd -m sha-512 > /persist/passwords/itsdrike
chmod 600 /persist/passwords/*
```
And declare these in our `configuration.nix` or `impermanence.nix`
```nix
users = {
# This option makes it that users are not mutable outside of our configuration.
# If you're using root impermanence, this will actually be the case regardless
# of this setting, however, setting this explicitly is a good idea, because nix
# will warn us if our users don't have passwords set, preventing lock outs.
mutableUsers = false;
# Each existing user needs to have a password file defined here, otherwise
# they will not be available to login. These password files can be generated with:
# mkpasswd -m sha-512 > /persist/passwords/myuser
users = {
root = {
# password file needs to be in a volume marked `neededForBoot = true`
hashedPasswordFile = "/persist/passwords/root";
};
itsdrike = {
hashedPasswordFile = "/persist/passwords/itsdrike";
};
};
};
```
#### Rebuild
Once you have declared all the files that you wish to persist, you can now rebuild your configuration for the next boot:
```shell
nixos-rebuild boot
```
While NixOS will take care of creating the specified symlinks, you will want to move the relevant files and directories
to where the symlinks are pointing at before rebooting.
```shell
mkdir -p /persist/system
mkdir -p /persist/system/etc/NetworkManager
cp -r {,/persist/system}/etc/NetworkManager/system-connections
sudo mkdir -p /persist/system/var/lib/NetworkManager
sudo cp /var/lib/NetworkManager/{secret_key,seen-bssids,timestamps}
... # Copy any other files/dirs you have configured
```
> [!NOTE]
> In case `/var/lib/NetworkManager/seen-bssids` doesn't (yet) exist, you can just create a file
> like this in it's place:
> `echo "[seen-bssids]" > /persist/system/var/lib/NetworkManager/seen-bssids`
Once you have copied all the files and directories that you wish to persist, we're ready. Brace yourself, and
```shell
reboot
```
### Why?
Honestly, why not?
Automatic root partition wiping will force you into declaring all of your files which you actually care about
persisting, which allows you to create incredibly small backups of only those files which actually matter. No more
creating backups of the entire file-system for absolutely no reason.
Additionally, doing this is just a great practice in general, as it will mean recreating your entire system from a clean
slate, from an immutable `/nix/store`, which means even in the unlikely case, that your system got affected by some kind
of malware, it will simply be gone after the next reboot. (Unless it affected the images in `/boot`, at which point all
bets are off.)
## Integrating my flake
Well, that was fun!
### Clonning
Now, let's move this config over to my flake, creating a new host machine there. Unless you're me, you will want to fork
my nix flake repository so that you can actually push to it before continuing.
First, you will need to git clone the flake. However, in the base system, there is no git, so let's first add this
to our `configuration.nix`:
```nix
programs.git.enable = true;
```
Let's also enable flake support, so we can use `nix flake` command:
```nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];
```
and run `nixos-rebuild switch`
Once done, log in as your unprivileged user, and clone my flake: `git clone https://github.com/ItsDrike/nixdots ~/dots`.
### Setting up git for new commits
In order to make any extra commits, you will need to set up a git user now, and log in to github. For a quick and
dirty way to achieve this, I'd recommend just setting a local git config for the `~/dots` repository. This is enough
for now, as my flake will introduce proper git setup once cloned anyway:
```shell
cd ~/dots
git config --local user.name ItsDrike
git config --local user.email itsdrike@protonmail.com
```
If you also need commit signing, you can set it up by adding `gnupg` package, importing your keys and setting a signing
key here too, however, I'd recommend against that. Instead, you can just rebase and sign the commits afterwards, once
you have my flake set up, as it already contains support for this. Similarly, setting up authorization to allow you to
push to github with your account is also something you can do after my flake is set up.
For now, let's just work on a temporary branch:
```shell
git checkout -b temp
```
### Moving config over
At this point, we're ready to move our configuration over to my flake, by declaring a new host machine. To do this, first,
let's create a directory in `~/dots/hosts`, with the same name as you're machines hostname (you can call it something else
too if you like, but this is the naming convention I follow) (for some reason, the naming scheme for my machines follow
the names of famous sword from Lord of the Rings).
```shell
mkdir ~/dots/hosts/anduril
```
Now declare this host in `~/dots/hosts/default.nix`:
```nix
anduril = lib.nixosSystem {
system = "x86_64-linux";
modules = [
./anduril
inputs.home-manager.nixosModules.home-manager
] ++ shared;
};
```
And copy the current files in `/etc/nixos/` to `~/dots/hosts/anduril`, renaming `configuration.nix` to `default.nix`
```shell
cp /etc/nixos/* ~/dots/hosts/anduril
mv ~/dots/hosts/anduril/{configuration.nix,default.nix}
```
### Adjusting some things
Once moved, assuming you get rid of most of the settings in `default.nix`, as my flake will
handle setting almost everything up for you. Instead, you can use my custom options to declare almost everything.
The resulting file should then look something like this:
```nix
{ lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
];
boot.supportedFilesystems = [ "btrfs" ];
hardware.enableAllFirmware = true;
# My flake disables password-based SSH authentication.
# either set up a key based auth, or uncomment this
#services.openssh.settings.PasswordAuthentication = lib.mmkForce true;
nix.settings = {
max-jobs = 6;
cores = 6;
};
# NixOS release from which this machine was first installed.
# (for stateful data, like file locations and db versions)
# Leave this alone!
system.stateVersion = lib.mkForce "23.11";
myOptions = {
system = {
hostname = "anduril";
username = "itsdrike";
impermanence = {
root = {
enable = true;
# Some people use /nix/persist/system for this, leaving persistent files in /nix subvolume
# I much prefer using a standalone subvolume for this though.
persistentMountPoint = "/persist";
};
# Configure automatic root subvolume wiping on boot from initrd
autoWipeBtrfs = {
enable = true;
devices."/dev/disk/by-label/NIXROOT".subvolumes = [ "root" ];
};
};
};
device = {
virtual-machine = false;
cpu.type = "intel";
};
home-manager = {
enabled = true;
stateVersion = "23.11";
git = {
userName = "ItsDrike";
userEmail = "itsdrike@protonmail.com";
signing = {
enabled = true;
key = "FA2745890B7048C0";
};
};
};
};
}
```
> [!NOTE]
> You may notice that this configuration also includes custom options for impermanence,
> and that the impermanence.nix is no longer declared in imports. This is because my
> flake already contains a fully custom support (mostly similar to what I've shown here)
> to handle impermanence. This allows me to re-use this impermanence across multiple
> machines very easily.
>
> You can now therefore delete the `impermanence.nix` file.
### Commit and switch
Once you've declared everything, make a commit and run `nix flake check` to make sure you everything checks out,
and you didn't make any typos or other issues.
```shell
git add hosts
git commit -m "Add anduril host"
nix flake check .
```
Finally, you should now be ready to switch:
```shell
sudo nixos-rebuild switch --flake .
```
If everything went well, you should now be left with a system configured to my specification.
### Last steps
Now that you've managed set up my flake, there are a few last steps to take.
First, you will probably now still be in a bash shell, I however use zsh, so you will want to re-login.
### Neovim
> [!WARNING]
> If you're logged in through SSH, you will need to set up a key based authentication,
> since password auth for SSH is disabled in my flake.
Once in zsh, another important step to set up neovim. Since I use a custom configuration, which relies on a lot of
plugins and other utilities, you will want to run neovim in headless mode for the first time, and leave it to install
all of these automatically:
```shell
nvim --headless +q
```
Once this completes, run neovim. Note that you will still see Mason installing a bunch of tools now, which will cause a
lot of notifications. Don't be alarmed by that, it is normal. Once the notifications stop, the installation process will
be truly complete. You can then close neovim.
### XDG base dirs
My flake exports various environment variables and does a bunch of other things to force applications into following XDG
base directory specification and not cluttering `$HOME`.
However, since we used a bunch of applications already, before moving to my flake. There will be a bunch of files or
directories that already got made. We will need to move these to their appropriate XDG locations, or even delete them
entirely, if we're not using these applications anymore, or if these applications are capable of automatically
recreating these directories trivially:
```shell
rm "$HOME/.nix-defexpr"
rm "$HOME/.bash_history"
```
### GPG keys and commit signing
Another important thing is to finish up setting your git commit signing. As you've probably noticed from the myOptions
config, I have already defined my signing key there, however you will need to import this gpg key manually.
Export your public and private keys with GPG and make them available on this machine. To do so, you can run these
commands from another machine:
```shell
gpg --output ./my-key.pub.gpg --armor --export [key-id]
gpg --output ./my-key.priv.gpg --armor --export-secret-keys [key-id]
# Now get these files to the new machine
# you can use sftp, or just a flash drive or whatever other method you prefer
```
Once the keys are available, run these commands from the new machine:
```shell
gpg --import ./my-key.pub.gpg
gpg --import ./my-key.priv.gpg
```
You might also want to change the trust level for this key, which you can do with:
```shell
gpg --edit-key [key-id]
# In the interactive session, run `trust`, select your trust level and finally run `save`
```
My flake already configured your git to enable commit signing using the key you specified earlier (even though it wasn't
yet available at that point). Any new commits that you make from now on will be signed
### Git credentials
> [!WARNING]
> I don't yet have a proper set up for git credentials handled, for now, you can
> just use the HTTPS based authentication with store credential helper. Like what's
> described below. This category will however be completely rewritten and moved to
> SSH keys once I have support for them ready in the flake.
```shell
git config --local credential.helper "store --file ~/.config/git/git-credentials"
```
Now, once you run `git push`, you will be asked for a password, which will get stored
to `~/.config/git/git-credentials` (in plain-text, though the file is protected by file-system permissions, and only the
owner can read it).
### Push to git
First, let's remove our temporary hack with git local configuration we used to allow us to make commits:
```shell
git config --local --unset user.name
git config --local --unset user.email
```
Now that you have git set up, let's ammend our previous commits, which will recreate it, and this time, git will use our
global configuration with the gpg keys configured to sign the commits.
The following command will rebase all commits until we reach the `main` branch, from which we branched off, which means
it will sign all commits in our `temp` branch:
```shell
git rebase --exec 'git commit --amend --no-edit -n -S' -i main
```
Now that our commits are signed, we're ready to merge and push:
```shell
git checkout main
git rebase temp
git push
```
## Sources / Attribution
- <https://nixos.wiki/wiki/Btrfs>
- <https://www.reddit.com/r/NixOS/comments/qys6xw/btrfs_recommendation/>
- <https://wiki.tnonline.net/w/Btrfs/Mount_Options>
- <https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html>
- <https://git.notashelf.dev/NotAShelf/nyx/src/branch/main/docs/notes/2023-03-14-impermanence.md>
- <https://nixos.wiki/wiki/NixOS_Installation_Guide>
- <https://nixos.wiki/wiki/Impermanence>

View file

@ -4,6 +4,7 @@ My NixOS and home-manager flake
## Structure
- [`docs`](./docs): Directory with various documents explaining the installation process, or other relevant topics.
- [`flake.nix`](./flake.nix): Starting point of the configuration, declaring entrypoints.
- [`system`](./system/): Basic core configurations for the system itself.
- [`home`](./home): Home-Manager configuration.

326
docs/01_INSTALLATION.md Normal file
View file

@ -0,0 +1,326 @@
# 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 in `fdisk`).
> 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:
```shell
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
```
### 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.
```shell
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` and `space_cache=v2`.
> These are all default (with the `ssd` being auto-detected), so specifying them is pointless now.
```shell
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:
```shell
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:
```shell
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:
```nix
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-uuid/08047b54-10af-4579-bb58-6af549b5c13e";
```
Which you will want to change to:
```nix
boot.initrd.luks.devices."cryptfs".device = "/dev/disk/by-label/NIXCRYPTFS";
```
A bunch of entries for our btrfs partition:
```nix
fileSystems."/" =
{ device = "/dev/disk/by-uuid/61b2d710-2508-4849-9613-b52fbc62bcf5";
fsType = "btrfs";
options = [ "subvol=root" ];
};
```
Where you will change the `device` like so:
```nix
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:
```nix
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/6383-E5C1";
fsType = "vfat";
};
```
To:
```nix
fileSystems."/boot" =
{ device = "/dev/disk/by-label/NIXBOOT";
fsType = "vfat";
};
```
And finally the swap partition from:
```nix
swapDevices =
[ { device = "/dev/disk/by-uuid/cb8cd9b7-8824-4a59-9249-89b5b2df0dbc"; }
];
```
To:
```nix
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:
```nix
fileSystems."/" =
{ device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=root" ];
};
```
To the following:
```nix
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:
```nix
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`:
```nix
{ 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.
```shell
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](./02_IMPERMANENCE.md).
## Sources / Attribution
- <https://nixos.wiki/wiki/Btrfs>
- <https://www.reddit.com/r/NixOS/comments/qys6xw/btrfs_recommendation/>
- <https://nixos.wiki/wiki/NixOS_Installation_Guide>
- <https://wiki.tnonline.net/w/Btrfs/Mount_Options>

247
docs/02_IMPERMANENCE.md Normal file
View file

@ -0,0 +1,247 @@
# Impermanence
The word impermanence means temporary or short-lived. When you see this term in NixOS, it refers to the practice
of automatically resetting/wiping your system after each reboot.
This will mean that your root directory will be wiped after every reboot. Such a setup is possible because
NixOS only needs `/boot` and `/nix` in order to boot, all other system files are simply links to files in `/nix`.
This guide assumes you're following from the [INSTALLATION](./01_INSTALLATION.md) guide, which means you have a working
setup with BTRFS file-system. Note that you will need the `/persist` and `/var/log` (and `/root`) subvolumes if you want
to proceed with this guide.
Note that setting up impermanence is completely optional, and if you do not wish to do so, you can simply skip this
guide and move on to the next one. If you're unsure whether impermanence is worth setting up, check out the
[Why section](#why).
## Auto-wipe root partition
To reset the root subvolume on every boot, we can simply delete it and create a new one in its place. We will be doing
this from initrd, which runs in a temporary file-system, before the actual file-system is properly mounted (following
fstab). This makes it a perfect place to run a script, which will wipe the root subvolume before each boot.
I will set this up using a systemd-based initrd, because I will need systemd for TPM unlocking later on. If you don't
care about that, it is also possible to do this without systemd. You can a guide for such setup
[here](https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html#darling-erasure). That said, I find this to be
a cleaner setup than the non-systemd one anyway, so it might be worth it for you to follow this regardless. However, do
note that using systemd in initrd may result in slightly slower boot times.
To achieve this, let's add the following to our `configuration.nix`:
```nix
boot.initrd.systemd = {
enable = true; # This enables systemd support in stage 1 - required for below setup
services.rollback = {
description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ];
# make sure it's done after decryption (i.e. LUKS/TPM process)
after = [ "systemd-cryptsetup@cryptfs.service" ];
# mount the root fs before clearing
before = [ "sysroot.mount" ];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script = ''
# Mount the BTRFS root to /mnt so we can manipulate btrfs subvolumes
mount --mkdir /dev/mapper/cryptfs /mnt
# Simply deleting a subvolume with btrfs subvolume delete will not work,
# if that subvolume contains other btrfs subvolumes. Because of that, we
# instead use this function to delete subvolumes, whihc will first perform
# a recursive deletion of any nested subvolumes.
#
# This is necessary, because the root subvolume will actually usually contain
# other subvolumes, even if the user haven't created those explicitly. It seems
# that NixOS creates these automatically. Namely, I observed these in root subvol:
# - root/srv
# - root/var/lib/portables
# - root/var/lib/machines
# - root/var/tmp
delete_subvolume_recursively() {
IFS=$'\n'
for x in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/mnt/$x"
done
echo "Deleting subvolume $1"
btrfs subvolume delete "$1"
}
# Recreate the root subvolume
delete_subvolume_recursively "/mnt/root"
echo "Re-creating root subvolume"
btrfs subvolume create "/mnt/root"
# we can now unmount /mnt and continue on the boot process.
umount /mnt
'';
};
};
```
## Impermanence
What this implies is that certain files, such as saved networks for network-manager will be deleted on each reboot.
While a little clunky, [Impermanence](https://github.com/nix-community/impermanence) is a great solution to our problem.
Impermanence adds a `environment.persistence."<dirName>"` option, that we can use to make certain directories or files
permanent. A sample configuration module for this can look like so:
```nix
{ config, pkgs, ... }:
let
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in
{
imports = [ "${impermanence}/nixos.nix" ];
# Some people use /nix/persist/system instead, leaving the persistent files in /nix subvolume
# I much prefer using a standalone subvolume for this though.
environment.persistence."/persist/system" = {
hideMounts = true;
directories = [
"/etc/nixos" # nixos configuration source
"/etc/NetworkManager/system-connections" # saved network connections
"/var/db/sudo" # keeps track of who got the sudo lecture already
"/var/lib/systemd/coredump" # recorded coredumps
];
files = [
"/etc/machine-id"
];
};
# For some reason, NetworkManager needs this instead of the impermanence mode to not get screwed up
systemd.tmpfiles.rules = [
"L /var/lib/NetworkManager/secret_key - - - - /persist/system/var/lib/NetworkManager/secret_key"
"L /var/lib/NetworkManager/seen-bssids - - - - /persist/system/var/lib/NetworkManager/seen-bssids"
"L /var/lib/NetworkManager/timestamps - - - - /persist/system/var/lib/NetworkManager/timestamps"
];
# Define host key paths in the persistent mount point instead of using impermanence for these.
# This works better, because these keys also get auto-created if they don't already exist.
services.openssh.hostKeys = mkForce [
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
}
```
You can put this module in `/etc/nixos/impermanence.nix`, and add it to your `imports` in `configuration.nix`.
Additionally, you may also want to move the `boot.initrd.systemd` configuration to this file.
Alternatively, you can of course also extend your `configuration.nix` adding this in directly, and keeping
everything in the same place.
### User configuration
Note that with impermanence, your user passwords will get erased too (with the `/etc/shadow` file). To avoid this,
you can create password files, which will contain the password hashes for each user:
```shell
mkdir -p /persist/passwords
chmod 700 /persist/passwords
mkpasswd -m sha-512 > /persist/passwords/root
mkpasswd -m sha-512 > /persist/passwords/itsdrike
chmod 600 /persist/passwords/*
```
And declare these in our `configuration.nix` or `impermanence.nix`
```nix
users = {
# This option makes it that users are not mutable outside of our configuration.
# If you're using root impermanence, this will actually be the case regardless
# of this setting, however, setting this explicitly is a good idea, because nix
# will warn us if our users don't have passwords set, preventing lock outs.
mutableUsers = false;
# Each existing user needs to have a password file defined here, otherwise
# they will not be available to login. These password files can be generated with:
# mkpasswd -m sha-512 > /persist/passwords/myuser
users = {
root = {
# password file needs to be in a volume marked `neededForBoot = true`
hashedPasswordFile = "/persist/passwords/root";
};
itsdrike = {
hashedPasswordFile = "/persist/passwords/itsdrike";
};
};
};
```
### Copy the configuration
While NixOS will take care of creating the specified symlinks, you will want to move the relevant files and directories
to where the symlinks are pointing at before rebooting.
```shell
mkdir -p /persist/system/etc
cp -r {,/persist/system}/etc/nixos
cp {,/persist/system}/etc/machine-id
mkdir -p /persist/system/var/db
cp -r {,/persist/system}/var/db/sudo
mkdir -p /persist/system/var/lib/systemd
cp -r {,/persist/system}/var/lib/systemd/coredump
mkdir -p /persist/system/etc/NetworkManager
cp -r {,/persist/system}/etc/NetworkManager/system-connections
sudo mkdir -p /persist/system/var/lib/NetworkManager
sudo cp {,/persist/system}/var/lib/NetworkManager/secret_key
sudo cp {,/persist/system}/var/lib/NetworkManager/timestamps
sudo cp {,/persist/system}/var/lib/NetworkManager/seen-bssids # if this fails, read the note below and repeat
... # Copy any other files/dirs you have configured
```
> [!NOTE]
> In case `/var/lib/NetworkManager/seen-bssids` doesn't (yet) exist, you can just create a file
> like this in it's place:
> `echo "[seen-bssids]" > /persist/system/var/lib/NetworkManager/seen-bssids`
## Rebuild
Once you have declared all the files that you wish to persist, you can now rebuild your configuration for the next boot,
and reboot.
> [!TIP]
> If you want to test out whether it worked, you can create a file somewhere on the root subvolume
> and make sure that it will no longer be there after the reboot: `touch /test_flag`
```shell
nixos-rebuild boot
reboot
```
You should now be back in your system, with the root subvolume wiped and auto-reconstructed by NixOS.
You can now move on to the next file: [MY_FLAKE](./99_MY_FLAKE.md).
## Why?
Honestly, why not?
Automatic root partition wiping will force you into declaring all of your files which you actually care about
persisting, which allows you to create incredibly small backups of only those files which actually matter. No more
creating backups of the entire file-system for absolutely no reason.
Additionally, doing this is just a great practice in general, as it will mean recreating your entire system from a clean
slate, from an immutable `/nix/store`, which means even in the unlikely case, that your system got affected by some kind
of malware, it will simply be gone after the next reboot. (Unless it affected the images in `/boot`, at which point all
bets are off.)
## Sources / Attribution
- <https://nixos.wiki/wiki/Impermanence>
- <https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html>
- <https://git.notashelf.dev/NotAShelf/nyx/src/branch/main/docs/notes/2023-03-14-impermanence.md>

291
docs/99_MY_FLAKE.md Normal file
View file

@ -0,0 +1,291 @@
# Integrating my flake
This file will walk you through setting up my flake. You can follow this guide directly from
[INSTALLATION](./01_INSTALLATION.md), or after any of the other optional steps. This will however assume the minimal set
up that you would have after just finishing the installation, so if you're following from a more advanced setup, some
things here may be redundant.
This assumes that you're setting up a new machine, and adding it to my flake (as opposed to attempting to use an
existing machine that's already declared - for example after a reinstall, although after reading through this guide, it
should be fairly easy to understand how to set up an existing machine too).
## Clonning
Now, let's move your config over to my flake, creating a new host machine there. Unless you're me, you will want to fork
my nix flake repository so that you can actually push to it before continuing.
First, you will need to git clone the flake. However, in the base system, there is no git, so let's first add this
to our `configuration.nix`:
```nix
programs.git.enable = true;
```
Let's also enable flake support, so we can use `nix flake` command:
```nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];
```
and run `nixos-rebuild switch`
Once done, log in as your unprivileged user, and clone my flake: `git clone https://github.com/ItsDrike/nixdots ~/dots`.
## Setting up git for new commits
In order to make any extra commits, you will need to set up a git user now, and log in to github. For a quick and
dirty way to achieve this, I'd recommend just setting a local git config for the `~/dots` repository. This is enough
for now, as my flake will introduce proper git setup once cloned anyway:
```shell
cd ~/dots
git config --local user.name ItsDrike
git config --local user.email itsdrike@protonmail.com
```
If you also need commit signing, you can set it up by adding `gnupg` package, importing your keys and setting a signing
key here too, however, I'd recommend against that. Instead, you can just rebase and sign the commits afterwards, once
you have my flake set up, as it already contains support for this. Similarly, setting up authorization to allow you to
push to github with your account is also something you can do after my flake is set up.
For now, let's just work on a temporary branch:
```shell
git checkout -b temp
```
## Moving config over
At this point, we're ready to move our configuration over to my flake, by declaring a new host machine. To do this, first,
let's create a directory in `~/dots/hosts`, with the same name as you're machines hostname (you can call it something else
too if you like, but this is the naming convention I follow) (for some reason, the naming scheme for my machines follow
the names of famous sword from Lord of the Rings).
```shell
mkdir ~/dots/hosts/anduril
```
Now declare this host in `~/dots/hosts/default.nix`:
```nix
anduril = lib.nixosSystem {
system = "x86_64-linux";
modules = [
./anduril
inputs.home-manager.nixosModules.home-manager
] ++ shared;
};
```
And copy the current files in `/etc/nixos/` to `~/dots/hosts/anduril`, renaming `configuration.nix` to `default.nix`
```shell
cp /etc/nixos/* ~/dots/hosts/anduril
mv ~/dots/hosts/anduril/{configuration.nix,default.nix}
```
## Adjusting some things
Once moved, assuming you get rid of most of the settings in `default.nix`, as my flake will
handle setting almost everything up for you. Instead, you can use my custom options to declare almost everything.
The resulting file should then look something like this:
```nix
{ lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
];
boot.supportedFilesystems = [ "btrfs" ];
hardware.enableAllFirmware = true;
# My flake disables password-based SSH authentication.
# either set up a key based auth, or uncomment this
#services.openssh.settings.PasswordAuthentication = lib.mmkForce true;
nix.settings = {
max-jobs = 6;
cores = 6;
};
# NixOS release from which this machine was first installed.
# (for stateful data, like file locations and db versions)
# Leave this alone!
system.stateVersion = lib.mkForce "23.11";
myOptions = {
system = {
hostname = "anduril";
username = "itsdrike";
#impermanence = {
# root = {
# enable = true;
# # Some people use /nix/persist/system for this, leaving persistent files in /nix subvolume
# # I much prefer using a standalone subvolume for this though.
# persistentMountPoint = "/persist";
# };
#
# # Configure automatic root subvolume wiping on boot from initrd
# autoWipeBtrfs = {
# enable = true;
# devices."/dev/disk/by-label/NIXROOT".subvolumes = [ "root" ];
# };
#};
};
device = {
virtual-machine = false;
cpu.type = "intel";
};
home-manager = {
enabled = true;
stateVersion = "23.11";
git = {
userName = "ItsDrike";
userEmail = "itsdrike@protonmail.com";
signing = {
enabled = true;
key = "FA2745890B7048C0";
};
};
};
};
}
```
## Commit and switch
Once you've declared everything, make a commit and run `nix flake check` to make sure you everything checks out,
and you didn't make any typos or other issues.
```shell
git add hosts
git commit -m "Add anduril host"
nix flake check .
```
> [!WARNING]
> If you're logged in through SSH, you will need to set up a key based authentication
> before switching, since password auth for SSH is disabled in my flake.
Finally, you should now be ready to switch:
```shell
sudo nixos-rebuild switch --flake .
```
If everything went well, you should now be left with a system configured to my specification.
## Last steps
Now that you've managed set up my flake, there are a few last steps to take.
First, you will probably now still be in a bash shell, I however use zsh, so you will want to re-login.
### Neovim
Once in zsh, another important step to set up neovim. Since I use a custom configuration, which relies on a lot of
plugins and other utilities, you will want to run neovim in headless mode for the first time, and leave it to install
all of these automatically:
```shell
nvim --headless +q
```
Once this completes, run neovim. Note that you will still see Mason installing a bunch of tools now, which will cause a
lot of notifications. Don't be alarmed by that, it is normal. Once the notifications stop, the installation process will
be truly complete. You can then close this instance neovim.
### XDG base dirs
My flake exports various environment variables and does a bunch of other things to force applications into following XDG
base directory specification and not cluttering `$HOME`.
However, since we used a bunch of applications already, before moving to my flake. There will be a bunch of files or
directories that already got made. We will need to move these to their appropriate XDG locations, or even delete them
entirely, if we're not using these applications anymore, or if these applications are capable of automatically
recreating these directories trivially:
```shell
rm "$HOME/.nix-defexpr"
rm "$HOME/.bash_history"
```
### GPG keys and commit signing
Another important thing is to finish up setting your git commit signing. As you've probably noticed from the myOptions
config, I have already defined my signing key there, however you will need to import this gpg key manually.
Export your public and private keys with GPG and make them available on this machine. To do so, you can run these
commands from another machine:
```shell
gpg --output ./my-key.pub.gpg --armor --export [key-id]
gpg --output ./my-key.priv.gpg --armor --export-secret-keys [key-id]
# Now get these files to the new machine
# you can use sftp, or just a flash drive or whatever other method you prefer
```
Once the keys are available, run these commands from the new machine:
```shell
gpg --import ./my-key.pub.gpg
gpg --import ./my-key.priv.gpg
```
You might also want to change the trust level for this key, which you can do with:
```shell
gpg --edit-key [key-id]
# In the interactive session, run `trust`, select your trust level and finally run `save`
```
My flake already configured your git to enable commit signing using the key you specified earlier (even though it wasn't
yet available at that point). Any new commits that you make from now on will be signed
### Git credentials
> [!WARNING]
> I don't yet have a proper set up for git credentials handled, for now, you can
> just use the HTTPS based authentication with store credential helper. Like what's
> described below. This category will however be completely rewritten and moved to
> SSH keys once I have support for them ready in the flake.
```shell
git config --local credential.helper "store --file ~/.config/git/git-credentials"
```
Now, once you run `git push`, you will be asked for a password, which will get stored
to `~/.config/git/git-credentials` (in plain-text, though the file is protected by file-system permissions, and only the
owner can read it).
### Push to git
First, let's remove our temporary hack with git local configuration we used to allow us to make commits:
```shell
git config --local --unset user.name
git config --local --unset user.email
```
Now that you have git set up, let's ammend our previous commits, which will recreate it, and this time, git will use our
global configuration with the gpg keys configured to sign the commits.
The following command will rebase all commits until we reach the `main` branch, from which we branched off, which means
it will sign all commits in our `temp` branch:
```shell
git rebase --exec 'git commit --amend --no-edit -n -S' -i main
```
Now that our commits are signed, we're ready to merge and push:
```shell
git checkout main
git rebase temp
git push
```
Done! We've just managed to add a new host to my flake and push the config back to git. You can now use this machine to
make any additional changes, whether that's host-specific ones or global ones.