NixOS is a Linux distribution based on the Nix package manager
Nix supports Linux, OS X and FreeBSD
Nix uses the purely functional Nix Expression language to define packages
NixOps uses Nix to deploy NixOS based infrastructure to Amazon Web Services, Google Cloud Platform, VirtualBox, Hetzner and NixOS containers
DisNix uses Nix to deploy distributed services
Hydra is a continuous integration and build cache server for Nix
For those that come from Windows, there are no software installers
For those from OS X, there is no App Store
For those from Linux, it is not FHS compliant
no /sbin
/lib
/lib64
/usr/local
/usr/lib
…
/etc
off limits
only /bin/sh
and /usr/bin/env
In general you can’t install arbitrary applications or libraries from the internet
You have to learn the Nix Expression language
You may have to learn a little bit about purely functional programming
If you get stuck you can’t just get the answer on AskUbuntu
You may have to add packages to the package manager
We do these things not because they are easy, but because they are hard
and because it makes the impossibly difficult easily possible
There is an excellent explanation of what the Nix package manager is on Sander van der Burg’s blog http://sandervanderburg.blogspot.co.za/2012/11/an-alternative-explaination-of-nix.html
and what follows is directly based on that
Tools to automate installing, upgrading, configuring, and removing software packages
Was initiated by Eelco Dolstra as part of his PhD research
Functions are found in many programming languages
But they are usually not the same as functions in mathematics
𝑥 = 𝑦 ⇒ ⨍⟮𝑥⟯ = ⨍⟮𝑦⟯
This C
code
does not obey the Leibnz’ principal
because it allows side-effects to be programmed
meaning it lacks referential transparency
so function application cannot be replaced by its result
Side-effects are not allowed to be programmed (they are pure)
No variables, only identifiers to immutable objects
This all means they are referential transparent
So expression only get evaluated when needed
Allows tying the knot, used for cyclic data structures while retaining purity
Results of evaluation can be cached
Evaluation only happens when the result is required
Referential transparency gives determinism allowing parallel evaluation
Treat deployment of packages as a function in a programming language
A “function” is representative of imperative languages like C
Execution can destructively modify other packages
/usr/lib
),Build recipes are run in isolated environments limiting chances of side effects
The result of each build is stored in a unique separate location
The storage locations are immutable
Unique locations give better guarantees against implicit dependencies sneaking through
You get determinism, the same package is the same where or when ever you install it
You can have multiple versions of packages co-existing
You can safely upgrade any package and always rollback an upgrade
You can in-place upgrade packages
NixOS is a GNU/Linux distribution built around the Nix package manager
It builds on Nix using the Nix Expression DSL to declaratively define every component including the kernel and configuration files
It uses systemd for services which are declaratively defined using the Nix Expression DSL.
NixOS is gives you relaible atomic upgrades
NixOS allows you to always rollback any changes
NixOS allows you to safely live upgrade your system
Purity means no side-effects (e.g. no variable assignment)
Laziness means function arguments are only evaluated when needed
Functional means functions are first class and can be passed around and manipulated
The language is not full-featured and general purpose
Its aim is describing packages, compositions of packages and variability within packages
#
character, or/* ... */
null
1234
true
and false
"single line string"
''
First line of a multiline string.
Second line
''
"this is an ${antiquote} in a string"
/bin/sh
or ./builder.sh
.
Allows you to introduce local variables for an expression
When defining sets its often convenient to copy variable from the surrounding scope
or from another set
both evaluates to
introduces the set e1
into the lexical scope of expression e2
Functions take a single argument with multiple arguments supported through currying
pattern: body
f x
set patterns are supported
including variable binding, default values for missing keys and partial matches
Doesn’t really need explaining
e.attrpath # select attribute
e1 e2 # call function
e ? attrpath # test attribute existance
e1 ++ e2 # list concatenation
e1 + e2 # list or path concatenation
!e1 # boolean negation
e1 // e2 # set union
e1 == e2 # equality
e1 != e2 # inequality
e1 && e2 # logical AND
e1 || e2 # logical OR
e1 -> e2 # logical implication (!e1 || e2)
import
Nix expressions that actually get built are derivations
Here is the example derivation for building GNU Hello
stdenv
and fetchurl
./configure; make; make install
to build hello we compose its function with its arguments
hello
attribute is bound to the function defined earlier and invoked with its argumentsWhen you install hello
the following paths will be added to the Nix store
/nix/store/vh0mxra4c7jrdg2fi5yn83k5kwdflsbx-hello-2.6.drv
/nix/store/agjbqxl2n6yhqwcarx9shdbsc13dgwk0-hello-2.6.tar.gz.drv
/nix/store/1gqj6zr1x0n812qxijy066fzrypgh3im-hello-2.6
Everything is cached so they won’t be downloaded again or build again if something else requires it
The answer is Nix profiles and again a very good explanation can be found on Sander’s blog http://sandervanderburg.blogspot.co.za/2013/09/managing-user-environments-with-nix.html
Nix profiles are user environments exposing a generation of installed packages to a user
nix-env
command line utility$ nix-env -i zip unzip
$ zip --version
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
Currently maintained by E. Gordon. Please send bug reports to
the authors using the web page at www.info-zip.org; see README for details.
When you install packages a new profile is created
A profile is a symlink tree synthesizing the contents of the currently installed packages
A user environment / profile is also a Nix package residing in the store
~/.nix-profile
PATH
by e.g. export PATH=~/.nix-profile/bin:$PATH
Deployment actions are actually non destructive
nix-env
new profiles are creatednix-env --rollback
nix-env --delete-generations old
nix-collect-garbage
{ config, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
# Use the GRUB 2 boot loader.
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
networking.hostName = "example-machine";
# List packages installed in system profile
environment.systemPackages = with pkgs; [
wget
termite
nvim
];
# List services that you want to enable:
services.openssh.enable = true;
networking.firewall.enable = false;
# Enable the X11 windowing system.
services.xserver.enable = true;
services.xserver.layout = "us";
# Enable the KDE Desktop Environment.
services.xserver.displayManager.sddm.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
# Define a user account. Don't forget to set a password with ‘passwd’.
users.extraUsers.a_user = {
isNormalUser = true;
uid = 1000;
home = "/home/a_user";
extraGroups = ["wheel" "audio" "video" "networkmanager" "postgres" "vboxusers"];
initialPassword = "a_user";
};
# The NixOS release to be compatible with for stateful data such as databases.
system.stateVersion = "17.09";
# virtual box related
boot.initrd.checkJournalingFS = false;
virtualisation.virtualbox.guest.enable = true;
}
{ config, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
/etc/nixos/configuration.nix
hardware-configuration.nix
# Use the GRUB 2 boot loader.
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
networking.hostName = "example-machine";
# List packages installed in system profile
environment.systemPackages = with pkgs; [
wget
termite
neovim
];
# List services that you want to enable:
services.openssh.enable = true;
networking.firewall.enable = false;
# Enable the X11 windowing system.
services.xserver.enable = true;
services.xserver.layout = "us";
# Enable the KDE Desktop Environment.
services.xserver.displayManager.sddm.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
sudo nixos-rebuild test
sudo nixos-rebuild switch
sudo nixos-rebuild rollback
If you work on different sets of software you don’t want the dependencies to interact
nix-shell
command to enter a development environment with all your dependencies setup.
default.nix
in a folder named after it
This file defines a function though that needs to be invoked with its environment
This is where the shell.nix
file comes in which nix-shell
uses by default
The shell.nix
file sets up the environment and then calls the default.nix
file
# default.nix
{stdenv, fetchurl}:
stdenv.mkDerivation {
name = "hello-2.6";
src = fetchurl {
url = ftp://ftp.gnu.org/gnu/hello/hello-2.6.tar.gz;
sha256 = "1h6fjkkwr7kxv0rl5l61ya0b49imzfaspy7jk9jas1fil31sjykl";
};
meta = {
homepage = http://www.gnu.org/software/hello/manual/;
license = "GPLv3+";
};
}
./default.nix
stdenv
and fetchurl
from the top level nix expressionIn the hello
directory run nix-shell
unpackPhase
./configure
and ./make
nix-shell
to setup your environments
let
pkgs = import <nixpkgs> {};
haskellPackages = pkgs.haskellPackages.override {
overrides = self: super: with pkgs.haskell.lib;{
ghc-syb-utils = dontCheck super.ghc-syb-utils;
};};
ghcWithPackages =
haskellPackages.ghcWithHoogle (g: with g;
[classy-prelude hakyll hakyll-favicon hakyll-filestore
hakyll-ogmarkup hakyll-series base pandoc pandoc-types
]);
in with pkgs;
runCommand "hakyll-env"
(with ghcWithPackages.haskellPackages;
rec
{ ghc = ghcWithPackages;
shellHook = "eval $(egrep ^export ${ghc}/bin/ghc)";
buildInputs =
[ ghcWithPackages zsh ghc-mod hindent cabal-helper
cabal-install codex stylish-haskell hoogle hlint
align netcat-openbsd hdevtools];})
"echo success > $out"
let
pkgs = import <nixpkgs> {};
haskellPackages = pkgs.haskellPackages.override {
overrides = self: super: with pkgs.haskell.lib;{
ghc-syb-utils = dontCheck super.ghc-syb-utils;
};};
...
ghc-syb-utils
package skipping its test suite ghcWithPackages =
haskellPackages.ghcWithHoogle (g: with g;
[classy-prelude hakyll hakyll-favicon hakyll-filestore
hakyll-ogmarkup hakyll-series base pandoc pandoc-types
]);
Then we create a special GHC that has the specified libraries available along with a custom Hoogle and documentation
in with pkgs;
runCommand "hakyll-env"
(with ghcWithPackages.haskellPackages;
rec
{ ghc = ghcWithPackages;
shellHook = "eval $(egrep ^export ${ghc}/bin/ghc)";
buildInputs =
[ ghcWithPackages zsh ghc-mod hindent cabal-helper
cabal-install codex stylish-haskell hoogle hlint
align netcat-openbsd hdevtools];})
"echo success > $out"
shellHook
buildInputs
attributelet
pkgs = import <nixpkgs> {};
in with pkgs;
clangStdenv.mkDerivation rec
{ name = "cpp-play-${version}";
version = "0.0.0";
buildInputs =
[clang clang-tools llvmPackages.libcxx llvmPackages.libcxxabi glibc gdb];
CPLUS_INCLUDE_PATH =
"${llvmPackages.libcxx}/include/c++/v1:${clangStdenv.cc.libc_dev}/include";
C_INCLUDE_PATH = "${clangStdenv.cc.libc_dev}/include";
LD_FLAGS =
"-L${llvmPackages.libcxx}/lib -L${llvmPackages.libcxxabi}/lib";
}
all attributes are defined as environment variables in the shell
antiquoting a package results in its final stored location
Given file hello/pegged-nix.nix
# helper to fetch nix-expression
let fetchFromGitHub = (import <nixpkgs> {}).fetchFromGitHub;
# fetch the nix expression from github at the specified revision
in import
( fetchFromGitHub
{
owner = "NixOS";
repo = "nixpkgs";
rev = "2839b101f927be5daab7948421de00a6f6c084ae";
sha256 =
"0a863cc5462gn1vws87d4qn45zk22m64ri1ip67w0b1a9bmymqdh";
}
) {}
Then hello can be called in hello/shell.nix
using the pegged nix packages
NixOps is a cloud deployment tool extending NixOS’s declerative approach to networking and provisioning.
Create a new deployment, then deploy it to Virtual Box and then display info about the deployment.
nixops create ./valgrind-docs.nix ./single-vbox.nix -d single-vbox
nixops deploy -d single-vbox --force-reboot
nixops info -d single-vbox
Network name: single-vbox
Network UUID: 650dbdee-752c-11e8-9eec-0242c2d31dbe
Network description: Single web server serving valgrind docs
Nix expressions: ./valgrind-docs.nix ./single-vbox.nix
+-----------+-----------------+------------+-------------+----------------+
| Name | Status | Type | Resource Id | IP address |
+-----------+-----------------+------------+-------------+----------------+
| webserver | Up / Up-to-date | virtualbox | ........... | 192.168.56.101 |
+-----------+-----------------+------------+-------------+----------------+
Valgrind’s documentation is served up at http://192.168.56.101
let # load-balanced.nix
backend = (import ./valgrind-docs.nix).webserver;
in
{
network.description = "Load balancing network";
proxy =
{ config, pkgs, nodes, ... }:
{ services.httpd.enable = true;
services.httpd.adminAddr = "bob@example.org";
services.httpd.extraModules = ["proxy_balancer" "lbmethod_byrequests"];
services.httpd.extraConfig =
''
<Proxy balancer://cluster>
Allow from all
BalancerMember http://backend1 retry=0
BalancerMember http://backend2 retry=0
</Proxy>
ProxyPass / balancer://cluster/
ProxyPassReverse / balancer://cluster/
'';
networking.firewall.allowedTCPPorts = [ 80 ];
};
backend1 = backend;
backend2 = backend;
}
Create a new deployment, then deploy it to libvirt and then display info about the deployment.
nixops create ./load-balanced.nix ./load-balanced-libvirt.nix -d load-balanced-libvirt
nixops deploy -d load-balanced-libvirt
nixops info -d load-balanced-libvirt
Network name: load-balanced-libvirt
Network UUID: d4e37f45-7531-11e8-823f-0242c2d31dbe
Network description: Load balancing network
Nix expressions: /home/handre/dev/load-balanced.nix /home/handre/dev/load-balanced-libvirt.nix
+----------+-----------------+----------+-------------+-----------------+
| Name | Status | Type | Resource Id | IP address |
+----------+-----------------+----------+-------------+-----------------+
| backend1 | Up / Up-to-date | libvirtd | | 192.168.122.14 |
| backend2 | Up / Up-to-date | libvirtd | | 192.168.122.25 |
| proxy | Up / Up-to-date | libvirtd | | 192.168.122.227 |
+----------+-----------------+----------+-------------+-----------------+
Valgrind’s documentation is served up at http://192.168.122.227
patchelf
orFHS