Franz Pletz / @fpletz
Mayflower GmbH
FrOSCon 2016
Build results depend on inherited state
Package installs modify state
Typical WTF Moments
Useful properties:
The purely functional package manager
/nix/store/s3fikpaws0z0wdkgs53nym05wj4wjy5w-openssh-7.3p1
A package is an output of a pure function. It depends only on the function inputs, without any side effects.
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
For impure derivations where the output is known before the build.
Guarded by cryptographic hash of output.
src = fetchFromGitHub {
owner = "ponylang";
repo = "ponyc";
rev = "4eec8a9b0d9936b2a0249bd17fd7a2caac6aaa9c";
sha256 = "184x2jivp7826i60rf0dpx0a9dg5rsj56dv0cll28as4nyqfmna2";
};
$ du -sh $(nix-store -qR `nix-build -A openssh`)
19M /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23
562K /nix/store/1zcm7nlw89y9hqq5ihhbkkmncw6s0vm8-bash-4.3-p42
8.4M /nix/store/6bscdl1q74d16d1c7hp84j77mpy9q5jg-ncurses-6.0
126K /nix/store/lx142x7lrirv833ckk74niiy3fp5p2wa-ncurses-6.0-man
534K /nix/store/02fpx56jrmbs8pgkdv0ihql2sdzbaxdq-ncurses-6.0-dev
1.8M /nix/store/381mr3hs51sbw4r53hc7adixlmard2cv-libressl-2.4.2
66K /nix/store/lcazrvc9390cm4bi95n458jabrs6y8z4-zlib-1.2.8
815K /nix/store/67ggrjiphzll3sy60zcv642r7jiv94nj-cracklib-2.9.6
1.8M /nix/store/g2q9kgmjsr6sbpnjmkbnvq298p89ps3f-linux-pam-1.2.1
209K /nix/store/zbc4iykrf79hj0gr7s77l56743yf5wml-libedit-20150325-3.1
3.1M /nix/store/s3fikpaws0z0wdkgs53nym05wj4wjy5w-openssh-7.3p1
$ tree `nix-build -A openssh`
/nix/store/s3fikpaws0z0wdkgs53nym05wj4wjy5w-openssh-7.3p1
├── bin
│ ├── scp
│ ├── sftp
│ ├── ssh
│ ├── ssh-add
│ ├── ssh-agent
│ ├── ssh-copy-id
│ ├── sshd
│ ├── ssh-keygen
│ └── ssh-keyscan
├── etc
│ └── ssh
│ ├── moduli
│ ├── ssh_config
│ └── sshd_config
├── libexec
│ ├── sftp-server
│ ├── ssh-keysign
│ └── ssh-pkcs11-helper
└── share
└── man
├── man1
│ ├── scp.1.gz
│ ├── sftp.1.gz
│ ├── ssh.1.gz
│ ├── ssh-add.1.gz
│ ├── ssh-agent.1.gz
│ ├── ssh-copy-id.1.gz
│ ├── ssh-keygen.1.gz
│ └── ssh-keyscan.1.gz
├── man5
│ ├── moduli.5.gz
│ ├── ssh_config.5.gz
│ └── sshd_config.5.gz
└── man8
├── sftp-server.8.gz
├── sshd.8.gz
├── ssh-keysign.8.gz
└── ssh-pkcs11-helper.8.gz
9 directories, 30 files
$ ldd `which ssh`
linux-vdso.so.1 (0x00007ffccb981000)
libcrypto.so.38 => /nix/store/381mr3hs51sbw4r53hc7adixlmard2cv-libressl-2.4.2/lib/libcrypto.so.38
libdl.so.2 => /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/libdl.so.2
libutil.so.1 => /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/libutil.so.1
libz.so.1 => /nix/store/lcazrvc9390cm4bi95n458jabrs6y8z4-zlib-1.2.8/lib/libz.so.1
libcrypt.so.1 => /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/libcrypt.so.1
libc.so.6 => /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/libc.so.6
libresolv.so.2 => /nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/libresolv.so.2
/nix/store/10zyyd2m90vqzj8gyqqdikzmg6a4dzqp-glibc-2.23/lib/ld-linux-x86-64.so.2
$ cat `which iotop`
#! /nix/store/1zcm7nlw89y9hqq5ihhbkkmncw6s0vm8-bash-4.3-p42/bin/bash -e
export PYTHONPATH=/nix/store/hb9pj22cm3xjxjkblqlay3i5f3fdz48r-iotop-0.6/lib/python2.7/site-packages:/nix/store/pfv9m665mg2368hdkldwscxy927851n8-python-2.7.12/lib/python2.7/site-packages:/nix/store/wkhan6yfzljykzlwyd3wli4y990lf2gr-python2.7-setuptools-19.4/lib/python2.7/site-packages:/nix/store/c291g9drj1a6nvs0ils0jfx6rqppc8x9-python-curses-2.7.12/lib/python2.7/site-packages${PYTHONPATH:+:}$PYTHONPATH
export PATH=/nix/store/hb9pj22cm3xjxjkblqlay3i5f3fdz48r-iotop-0.6/bin:/nix/store/pfv9m665mg2368hdkldwscxy927851n8-python-2.7.12/bin:/nix/store/ya9d15pgg63bx6m7m72czvh9fcj5vfr7-less-483/bin:/nix/store/wkhan6yfzljykzlwyd3wli4y990lf2gr-python2.7-setuptools-19.4/bin:/nix/store/hb9pj22cm3xjxjkblqlay3i5f3fdz48r-iotop-0.6/bin${PATH:+:}$PATH
exec -a "$0" /nix/store/hb9pj22cm3xjxjkblqlay3i5f3fdz48r-iotop-0.6/bin/.iotop-wrapped "${extraFlagsArray[@]}" "$@"
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Dependencies of this "derivation"
Parameters of a new function
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Create a derivation using a wrapper
from the "standard environment"
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Define package name with version
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Fetch the source code from an
OpenBSD mirror & check hash
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
List of packages available
in the build environment
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
List of flags to pass to the
configure script (GNU autotools)
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Custom make target to install
openssh (don't generate host keys)
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
More meta information
Independent of the package build
{ stdenv, fetchurl, fetchpatch, zlib, openssl, libedit, pkgconfig, pam }:
stdenv.mkDerivation rec {
name = "openssh-${version}";
version = "7.3p1";
src = fetchurl {
url = "mirror://openbsd/OpenSSH/portable/${name}.tar.gz";
sha256 = "1k5y1wi29d47cgizbryxrhc1fbjsba2x8l5mqfa9b9nadnd9iyrz";
};
buildInputs = [ zlib openssl libedit pkgconfig pam ];
configureFlags = [
"--with-mantype=man"
"--with-libedit=yes"
(if pam != null then "--with-pam" else "--without-pam")
];
enableParallelBuilding = true;
installTargets = [ "install-nokeys" ];
meta = with stdenv.lib; {
homepage = "http://www.openssh.com/";
description = "An implementation of the SSH protocol";
license = licenses.bsd2;
platforms = platforms.unix;
maintainers = with maintainers; [ eelco ];
};
}
Just like
but for all packages!
And for all programming language specific package managers like
[fpletz@yolovo:~]$ python
python: Command not found
[fpletz@yolovo:~]$ nix-shell -p python
[nix-shell:~/src/nixpkgs]$ python
Python 2.7.12 (default, Jun 25 2016, 21:49:32)
[GCC 5.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Imperative Environments
[nix-shell:~]$ python -c 'import requests'
ImportError: No module named requests
[nix-shell:~]$ exit
[fpletz@yolovo:~]$ nix-shell -p python pythonPackages.requests
[nix-shell:~]$ python -c 'import requests'
Imperative Environments
$ python -c 'import requests'
ImportError: No module named requests
$ nix-shell --pure
$ python -c 'import requests'
$
Declarative environments for your project
with import <nixpkgs> {}; {
env = stdenv.mkDerivation {
name = "pyrequests-env";
buildInputs = [
python
pythonPackages.requests
];
};
}
default.nix
like for instance Puppet
package { "openssh":
ensure => installed,
}
user { "eris":
ensure => present,
uid => 1023,
home => "/home/eris",
}
apt-get install openssh
useradd -u 1023 eris
Undeclared aspects of the target system are unknown and could have unexpected values!
eventual consistency
defined consistency
So why not take Nix and build…
…to produce a complete operating system?
The declarative
Linux Distribution
{
boot.loader.grub.device = "/dev/sda";
fileSystems."/".device = "/dev/sda1";
networking.hostname = "webserver";
networking.firewall.allowedTCPPorts = [ 80 443 ];
environment.systemPackages = with pkgs; [ htop vim mtr ];
services =
{ openssh.enable = true;
nginx =
{ enable = true;
virtualHosts."service.example.com" =
{ forceSSL = true;
enableACME = true;
location."/".proxyPass = "https://backend:3000/";
};
};
};
}
$ cat deploy.nix
{
webserver = { pkgs, ... }:
{ services.openssh.enable = true;
services.nginx.enable = true;
users.extraUsers.root.openssh.authorizedKeys.keys =
[ "ssh-ed25519 AAAA…BUV fpletz@yolovo" ];
};
}
$ cat deploy-vbox.nix
{
webserver = { ... }:
{ deployment.targetEnv = "virtualbox";
deployment.virtualbox.memorySize = 1024;
};
}
$ nixops create ./deploy.nix ./deploy-vbox.nix -d my-web-deployment
$ nixops deploy -d my-web-deployment
creating VirtualBox VM ‘webserver’...
Quotes by Rob Vermas - http://nixer.ghost.io/why/
Find these slides online:
You can also ask me on Twitter after the talk!
$ cat default.nix
derivation {
name = "my-package";
builder = ./builder.sh;
system = "x86-64-linux";
src = /home/user/source.tar.gz;
}
$ cat builder.sh
tar xvfz $src
cd program-1.0/
mkdir -p $out/bin
cp program.sh $out/bin/program
$ nix-build
/nix/store/cpcx3df4s2d51ddm01qm0w9x1nv8mpfq-my-package
$ ./result/bin/program