Nix is built on a trifecta of three things: functional language, derivations + nixpkgs and nixos.
Language Syntax
https://nix.dev/tutorials/nix-language.html
https://nix.dev/manual/nix/2.28/language/index.html
Use nix repl for REPL and nix-instantiate --eval for files
Attribute Set
In the form { … }, collection of name-value-pairs. Literally like json, except using semicolons instead of commas.
{
string = "hello";
integer = 1;
float = 3.141;
bool = true;
null = null;
list = [ 1 "two" false ];
attribute-set = {
a = "hello";
b = 2;
c = 2.718;
d = false;
}; # comments are supported
}You can add rec { ... } to have it be able to recursively call it’s own attributes. Access using the . operator. Commas are not used.
with …; …
Think of it as “namespaces”. Allows to access attributes without repeating.
let
a = {
x = 1;
y = 2;
z = 3;
};
in
with a; [ x y z ]instead of
a.x, a.y, a.z
inherit
{ inherit x y; }is the same as {x = x; y = y;}
you can also hinherit from a specific attribute set using ()
inherit (a) x y;
is the same as {x = a.x; y = a.y;}
let…in…
Used for assigning names to values, to execute in the in block
let
b = a + 1;
a = 1;
in
a + b
Strings
"" for strings. '' '' for multiline strings. ${} for interpolation
Functions
x: x + 1
{a, b}: a + b
{a, b ? 0}: a + b
{a, b, ...}: a + b
args@{a, b, ...}: a + b + args.c
{ a, b, ... }@args: a + b + args.cIn pure functional pattern, they only accept one parameter. To call, just put it after
f = {a, b}: a + b
f {a = 1; b = 2;}
To have multiple, you need to curry
Operators
https://nix.dev/manual/nix/2.28/language/operators.html
Cool ones:
++list concatenationattrset ? attributehasattrset // attrset“Update”, but its basically just a union, with duplicatings using secondarguments |> funcpiping
Builtins
https://nix.dev/manual/nix/2.28/language/builtins.html
Implemented in C++ for the language interpreter
Most of them you need to use builtins.<func>. Except for:
import
Parses a nix expression and returns
pkgs.lib
From nixpkgs. Not built inl, but useful funcs implemented in nix itself
Derivation
Derivations are a way to create reproducible builds, byte-for-byte. It’s stored in the format /nix/store/<hash>-file-name. Derivations can depend on each other, etc.
Derivations can be stored in cache.nixos.org, to download tarballs in FHS layout that can be unpacked. These allow maximal reproducibility. HGased based on merkle trees (binary trees based on hashes of children)
Fun fact: ultimate root of trust being some kind of stdenv tarball, seed-c, etc
So what can you do with Nix?
derivation {
name: "hello"; # symbolic name
system: "x86_64-linux" # system
builder: "/bin/bash" # command to build
args: [];
outputs: [ "out" ]; # default out. passed as environment variables
}https://www.youtube.com/watch?v=5D3nUU1OVx8
https://nix.dev/manual/nix/2.28/language/derivations.html
Flakes
A way to use nix syntax to create reproducible “flakes” - file system trees
{
description: "string describing flake";
inputs: <>
outputs: function
nixConfig: <> # attribute sets of values
}https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-flake.html#flake-references
{ self, ... }@inputs:
{
# Executed by `nix flake check`
checks."<system>"."<name>" = derivation;
# Executed by `nix build .#<name>`
packages."<system>"."<name>" = derivation;
# Executed by `nix build .`
packages."<system>".default = derivation;
# Executed by `nix run .#<name>`
apps."<system>"."<name>" = {
type = "app";
program = "<store-path>";
};
# Executed by `nix run . -- <args?>`
apps."<system>".default = { type = "app"; program = "..."; };
# Formatter (alejandra, nixfmt or nixpkgs-fmt)
formatter."<system>" = derivation;
# Used for nixpkgs packages, also accessible via `nix build .#<name>`
legacyPackages."<system>"."<name>" = derivation;
# Overlay, consumed by other flakes
overlays."<name>" = final: prev: { };
# Default overlay
overlays.default = final: prev: { };
# Nixos module, consumed by other flakes
nixosModules."<name>" = { config, ... }: { options = {}; config = {}; };
# Default module
nixosModules.default = { config, ... }: { options = {}; config = {}; };
# Used with `nixos-rebuild switch --flake .#<hostname>`
# nixosConfigurations."<hostname>".config.system.build.toplevel must be a derivation
nixosConfigurations."<hostname>" = {};
# Used by `nix develop .#<name>`
devShells."<system>"."<name>" = derivation;
# Used by `nix develop`
devShells."<system>".default = derivation;
# Hydra build jobs
hydraJobs."<attr>"."<system>" = derivation;
# Used by `nix flake init -t <flake>#<name>`
templates."<name>" = {
path = "<store-path>";
description = "template description goes here?";
};
# Used by `nix flake init -t <flake>`
templates.default = { path = "<store-path>"; description = ""; };
}Self in the flake refers tot he ENTIRE FLAKE
Flake-utils
Use nix-systems to have a listof all systems, and then flake-utils to make something for eachDefaultSystem.
Nixpkgs
Nixpkgs is just a flake, you want to use import nixpkgs {…} to get the actual pkgs. Use inherit system if you are using flake-utils
Overlays
https://nixos.wiki/wiki/Overlays
Allow packages to be changed / updated
overlays = [ (import rust-overlay) ];
pkgs = (import nixpkgs) {
inherit system overlays;
};
then call inherit overlays to use it
Tips
you should do someDep.inpuits.nixpkgs.follows="nixpkgs". tells nix to follow their own nixpkgs.