Introduction
Warning This is an attempt to create workshops that can be done by complete beginners without having NixOS installed but only Nix.
The main idea is to provide a chain of workshops that do not require any Nix knowledge at the begining. Each step allows to talk about a little part of Nix & NixOS domain without the need of a full understanding. This learn by doing approach should break down the steep learning curve of Nix.
Each workshop should allow the "speaker" to drive the attendees with a live code session. Those workshops are not documented enough to be led by someone that does not have at least a small experience with Nix.
Warning The workshops have only been tested on NixOS and Ubuntu.
Nix installation
Requirements
- Linux, MacOS, Windows (WSL2)
ToDo
- Try installation with nix-installer
Steps
For Linux users with install Nix:
sh <(curl -L https://nixos.org/nix/install) --daemon
Enable expirmental features but not so expiremental:
mkdir -p ~/.config/nix
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
Don't forget to restart nix-daemon.
systemctl restart nix-daemon
Let's play a litte bit with Nix:
- Language
- Channels
- Nixpkgs
- Repl
Minimal image
Requirements
- A minimal setup from this workshop
ToDo
- Try with nixos-generators
Steps
Let's initialize a flake:
nix flake init
Nix create a flake.nix
file containing a minimal setup.
We need to lock our flake to define the main dependency nixpkgs
:
nix flake lock
Repl exploration:
nix repl
:lf . #Load Nix flake
packages # hit tab for autocomple
packages.x86_64-linux # hit tab
packages.x86_64-linux.default # hit enter
:e packages.x86_64-linux.default # To open derivation
:b packages.x86_64-linux.default # To build derivation
:q # To exit
Show package content in store:
tree /nix/store/1pry7pnxqig0n2pkl4mnhl76qlmkk6vi-hello-2.12.1
Build packages:
nix build # to build the default package
nix build .#hello # to build the hello package
nix build nixpkgs#cowsay #to build a package from nixpkgs
Run packages:
nix run
nix run nixpkgs#cowsay hello
Look at the Flakes feature documentation: https://nixos.wiki/wiki/Flakes
Add nixpkgs channel to inputs:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11";
};
}
Update flake lock file:
nix flake lock
To create a system
configuration we need to use nixpkgs.lib.nixosSystem
:
{
outputs = { self, nixpkgs, ... }: {
nixosConfigurations.default = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [];
};
};
}
Let's try to check if our configuration is valid with:
nix flake check
Output:
error:
Failed assertions:
- The ‘fileSystems’ option does not specify your root file system.
- You must set the option ‘boot.loader.grub.devices’ or 'boot.loader.grub.mirroredBoots' to make the system bootable.
(use '--show-trace' to show detailed location information)
We need to define the fileSystems
option and target a "device" (something like /dev/sda
) to be able build a minimal nixosSystem
.
To define "things" such as the fileSystems
option in our configuration we need to put them in the modules
argument.
Each module specified will receive arguments as described here: https://nixos.wiki/wiki/NixOS_modules#Function
We can either directly embded it or specify a file to import:
{
system = "x86_64-linux";
modules = [
({ config, pkgs, ... }: {
# my config
})
./machine.nix
];
}
To ease our workshop and not overload our brain with too much information we will import an installer module provided by the nixpkgs repository:
{
modules = [
({ modulesPath, ... }: {
imports = [
"${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
];
})
]
}
This will help us to produce an image with predefined fileSytems
and other neat configurations.
Don't hesitate to have a look to the file which is imported by installation-cd-minimal.nix
: https://github.com/NixOS/nixpkgs/blob/nixos-22.11/nixos/modules/installer/cd-dvd/iso-image.nix
Do you remember how to build a something from nix repl
? Let's do it again, according to the module we imported:
# This module creates a bootable ISO image containing the given NixOS
# configuration. The derivation for the ISO image will be placed in
# config.system.build.isoImage.
:lf .
nixosConfigurations # hit tab
nixosConfigurations.default # again ...
nixosConfigurations.default.config.system.build.isoImage # it's a derivation so we can build it
:b nixosConfigurations.default.config.system.build.isoImage # build a derivation from repl is silent so you will have to wait until the build has finished
Let's add a package target:
{
packages.x86_64-linux.isoImage = self.nixosConfigurations.default.config.system.build.isoImage;
}
Build it from nix build
command:
nix build .#isoImage
Our image is now build and locate in the following folder:
ls result/iso
We now have a minimal image to use on a USB stick.
Custom image
Requirements
- A minimal image from this workshop
Steps
Maybe one of the first thing you might want to do when you booted your device from an image is to connect to it through SSH instead of relying on the main terminal so you can for example copy/paste commands directly from your computer.
To do this we need to install an OpenSSH server and add our public SSH key to the authorized keys of the main user.
We could install the package and configure it manually but instead we will be using a nixos module that will automatically do this for us.
This module can be searched from several places with services.openssh
:
- https://nixos.org/nixos/options.html
- man configuration.nix
We can see that there is an option called services.openssh.enable
but fortunately with the import "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
, OpenSSH server is already enabled here.
If we explore a little bit the code where is enabled the OpenSSH service, we can see that we need to add a public SSH key to the nixos
user if we want to automatically be able to login.
To do that search for the authorizedKeys
word, you should find some of these options:
services.openssh.authorizedKeysFiles
users.users.<name>.openssh.authorizedKeys.keys
users.users.<name>.openssh.authorizedKeys.keyFiles
The option users.users.<name>.openssh.authorizedKeys.keys
is what we need, just replace <name>
with nixos
which is the default user configured by our import:
{
modules = [
({ modulesPath, ... }: {
imports = [
"${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
];
users.users.nixos.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3N... "
];
})
];
}
Now if we build our image, we will be able to SSH on a device that booted with our image.
Let's try on VM by running:
nix build .#nixosConfigurations.default.config.system.build.vm
export QEMU_NET_OPTS="hostfwd=tcp::2222-:22"
./result/bin/run-nixos-vm
In another terminal:
ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no nixos@localhost -p 2222
cat /etc/ssh/authorized_keys.d/nixos
We now have an image containing our public SSH key.
You can go further and pre-install packages like tmux
, configure services or pre-configure WiFi setup.
Custom package
Requirements
- A minimal setup from this workshop
ToDo
- Create a Docker image from this book with Nginx
Package override
Requirements
- A minimal setup from this workshop
ToDo
- Override a package to apply a patch
Development environment
Requirements
- A minimal setup from this workshop
ToDo
-
Create a developement environment for:
- Python
- Rust
- PHP
- Go
- Node.js