NixOS
The canonical source of truth is the Nix code.
There is a search engine for options.
testing configuration
I couldn't get
nix-instantiate --eval
to play well with flakes, so I use the following setup.
Although I don't usually use a REPL for most languages (python, julia, e.g.), preferring file-based development, unfortunately I couldn't find a more ergonomic setup than using the REPL.
I wrap
nix repl
with a shell script like
#!/run/current-system/sw/bin/sh
nix repl \
--file "$(dirname "$0")/nixos-repl.nix" \
--argstr username "$(whoami)" \
--argstr hostname "$(hostname)" \
--argstr path "/keep$HOME/.config/home-manager"
that loads nixos-repl.nix
placed in the same
directory.
{ username, hostname, path }:
let
self = builtins.getFlake path;
nixos = self.nixosConfigurations.${hostname};
in
{
inherit self;
${hostname} = nixos;
${username} = nixos.config.home-manager.users.${username};
inherit (nixos) config pkgs options;
inherit (nixos.pkgs) lib;
}
This can be then used like
nix-repl> <hostname>.config.
and press <tab>
to see completions.
:p
can be used to force evaluation.
If I have something I want to inspect over and over again, I use a wrapper of nix eval
nix eval ".#nixosConfigurations.$(hostname)" --apply "$(hostname): $1"
like so.
nixos-eval "<hostname>.config.system.activationScripts.usrbinenv"
updating
Simple script to make sure /boot
is mounted before
updating.
#!/run/current-system/sw/bin/sh
if [ ! "$(findmnt /boot)" ]; then
sudo mkdir --parents /boot
sudo mount --onlyonce /dev/nvme0n1p1 /boot
fi
sudo nixos-rebuild switch
nixos-rebuild
has a few possible commands.
switch
: activate and make boot defaultboot
: make boot default but don't activatetest
: activate but don't make boot default-
build
: neither activate nor make boot default-
the result is a symlink placed in
./result
-
the result is a symlink placed in
removing channels and flake registries
Channels and flake registries are unnecessary and a source of impurity as they are unpinned.
In NixOS the following configuration disables channels.
{
nix.channel.enable = false;
}
In Home Manager the following configuration disables flake registries.
{
nix.settings = {
experimental-features = [ "nix-command" "flakes" "repl-flake" ];
flake-registry = "";
use-registries = false;
};
}
Note flake-registry
controls the
global registry
while use-registries
controls user registries.
It is convenient to replace the nixpkgs
reference
with the shell script nixpkgs
.
#!/run/current-system/sw/bin/sh
flake="/keep$HOME/.config/home-manager"
# `--impure` as the flake may be dirty and considered unlocked
# error: cannot call 'getFlake' on unlocked flake reference
nix eval --impure --raw \
--expr "(builtins.getFlake \"$flake\").inputs.nixpkgs.outPath"
Then commands like
nix-shell -I nixpkgs=flake:nixpkgs -p python3
nix shell nixpkgs#python3
can be replaced by
nix-shell -I nixpkgs=$(nixpkgs) -p python3
nix shell $(nixpkgs)#python3
which has the advantage of not requiring internet as it uses the NixOS configuration's nixpkgs.
In addition, a dependency lookup like
nix why-depends /nix/var/nix/profiles/system $(nixpkgs)#nss
is more accurate as it uses the same version of the package as the system.
miscellaneous
installing from arch
One can switch from Arch Linux completely
"in-place", i.e. without re-partitioning any drives. This can be
done by
prototyping
with
kexec
to get a working configuration and then using
NIXOS_LUSTRATE
through the
lustrate
mechanism (which will move the old root partition to
/old-root
).
After this, it's still possible to get into arch with
chroot
, e.g.
sudo chroot /old-root /bin/bash
/bin/pacman -Q
Note that commands need to be fully qualified as
$PATH
is still from NixOS.
live boot
Live boot can be done from an Arch live boot (see NixOS Wiki - Change root).
cryptsetup open /dev/nvme0n1p3 cryptlvm
mount /dev/VolumeGroup/root /mnt
mount -o bind /dev /mnt/dev
mount -o bind /proc /mnt/proc
mount -o bind /sys /mnt/sys
chroot /mnt /nix/var/nix/profiles/system/activate
chroot /mnt /run/current-system/sw/bin/bash
/bin/sh
Having /bin/sh
is technically an impurity as
applications can reference it without knowing the exact version.
However, it's required to do system()
calls in
libc, so it can't be easily disabled entirely. This can cause
issues
with
reproducibility
but progress in fixing this seems to have
stalled.
(see also: NixOS Discourse - Add /bin/bash to avoid unnecessary pain, NixOS Wiki - Command Shell)
I think the cleanest thing to do is to use the default sandbox
shell provided in the default
stdenv,
currently
a statically linked ash shell from
busybox. The reasoning
being that if /bin/sh
matches the one used at
build-time, there's less chance of a runtime error due to
possible incompatibility.
environment.binsh = "${pkgs.busybox-sandbox-shell}/bin/busybox";
It does warn about changing from bash, but considering it's over 10 years old, it's probably fine now.
/usr/bin/env
Like /bin/sh
, /usr/bin/env
is an
impurity
that does not exist at
build time.
Unlike sh
, it can be disabled relatively
easily.
environment.usrbinenv = null;
This can cause issues for unpatched software that rely on
env
, e.g. prettier
installed with
npm
.
There are (currently) also a few minor spurious errors that should be fixed, see this issue.
system.activationScripts.usrbinenv =
lib.mkIf (config.environment.usrbinenv == null) (
lib.mkForce ''
rm -f /usr/bin/env
mkdir -p /usr/bin
rmdir --ignore-fail-on-non-empty /usr/bin /usr
''
);
systemd.services.systemd-update-done.serviceConfig.ExecStart = [
"" # clear
(
pkgs.writeShellScript "systemd-update-done-wrapper" ''
mkdir -p /usr
${pkgs.systemd}/lib/systemd/systemd-update-done
rmdir --ignore-fail-on-non-empty /usr
''
)
];
vulnix
vulnix scans the dependencies of the entire system for CVEs.
vulnix --system