NixOS

Reproducible builds and deployments.

Nix

A powerful package manager for Linux and other Unix systems that makes package management reliable and reproducible. Share your development and build environments across different machines.

NixOS

A Linux distribution with a unique approach to package and configuration management. Built on top of the Nix package manager, it is completely declarative, makes upgrading systems reliable, and has many other advantages.

$ # Hi!$ # Wondering how to start using Nix?$ # Here are a few examples:$ node -e "console.log(1+1)"node: command not found$ # Interesting, no node on this machine$ # No problem with Nix!$ nix-shell -p nodejs[nix-shell]$ node -e "console.log(1+1)"2[nix-shell]$ # And now we are able to use node.[nix-shell]$ # Nix Magic! :)$ # Typing "nix-shell -p ..." each time can be tedious. We can do better.$ # We can write everything down in default.nix$ bat default.nix────────┬──────────────────────────────────────────────────────────────────── │ File: default.nix────────┼──────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} # here we import the nixpkgs package set 2 │ }: 3 │ pkgs.mkShell { # mkShell is a helper function 4 │ name="dev-environment"; # that requires a name 5 │ buildInputs = [ # and a list of packages 6 │ pkgs.nodejs 7 │ ]; 8 │ shellHook = '' # bash to run when you enter the shell 9 │ echo "Start developing..." 10 │ ''; 11 │ }────────┴────────────────────────────────────────────────────────────────────$ # Pause the video to understand the default.nix$ # To enter dev-environment simply run:$ nix-shellStart developing...[nix-shell]$ node -e "console.log(1+1)"2[nix-shell]$ # Now go ahead commit default.nix to your repository[nix-shell]$ # and share your development environment with your coworkers$ # For the last example, let us build a minimal docker image with Nix$ bat docker-redis.nix───────┬───────────────────────────────────────────────────────────────────── │ File: docker-redis.nix───────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} # nixpkgs package set 2 │ }: 3 │ pkgs.dockerTools.buildLayeredImage { # helper to build docker image 4 │ name = "nix-redis"; # give docker image a name 5 │ tag = "latest"; # provide a tag 6 │ contents = [ pkgs.redis ]; # packages in docker image 7 │ }───────┴─────────────────────────────────────────────────────────────────────$ # Pause the video and take the time to understand docker-redis.nix file$ # Now let's build the docker image and load it into docker$ nix-build docker-redis.nix -o ./result ... SKIPPING OUTPUT .../nix/store/1crapx24sjgqm2j1wmq17k6f6a9wy66d-docker-image-nix-redis.tar.gz$ docker load -i ./result Loaded image: nix-redis:latest$ docker images | grep redisdebian-redis latest 8366943c77e8 3 days ago 136MBalpine-redis latest aae644cd3417 3 days ago 6.99MBnix-redis latest 30486183a209 50 years ago 45.4MB$ # The size of our docker image is somewhere between a Debian and$ # an Alpine image$ # The redis packaged in nixpkgs is not optimized for small size$ # Let us fix this!$ bat redis-minimal.nix───────┬───────────────────────────────────────────────────────────────────── │ File: redis-minimal.nix───────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} 2 │ }: 3 │ pkgs.redis.overrideAttrs (old: { 4 │ # no need for systemd support in our docker image 5 │ makeFlags = old.makeFlags ++ ["USE_SYSTEMD=no"]; 6 │ # build static binary with musl 7 │ preBuild = '' 8 │ makeFlagsArray=(PREFIX="$out" 9 │ CC="${pkgs.musl.dev}/bin/musl-gcc -static" 10 │ CFLAGS="-I${pkgs.musl.dev}/include" 11 │ LDFLAGS="-L${pkgs.musl.dev}/lib") 12 │ ''; 13 │ # Let's remove some binaries which we don't need 14 │ postInstall = "rm -f $out/bin/redis-{benchmark,check-*,cli}"; 15 │ })───────┴─────────────────────────────────────────────────────────────────────$ # In redis-minimal.nix we override the default redis build with three changes:$ # 1.) Remove the redis systemd support$ # 2.) Build a statically linked binary with musl$ # 3.) Remove all binaries apart from redis-server$ # Now let's build the docker image with our newly created minimal redis$ bat docker-redis-minimal.nix───────┬───────────────────────────────────────────────────────────────────── │ File: redis-minimal.nix───────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} 2 │ }: 3 │ let 4 │ redisMinimal = import ./redis-minimal.nix { inherit pkgs; }; 5 │ in 6 │ pkgs.dockerTools.buildLayeredImage { # helper to build docker image 7 │ name = "nix-redis-minimal"; # give docker image a name 8 │ tag = "latest"; # provide a tag 9 │ contents = [ redisMinimal ]; # use redisMinimal package 10 │ }───────┴─────────────────────────────────────────────────────────────────────$ # Let's build the new docker image now$ nix-build docker-redis-minimal.nix -o ./result ... SKIPPING OUTPUT .../nix/store/83zcgs5xvzrgx09iv8s82wkabl8xkr03-docker-image-nix-redis-minimal.tar.gz$ docker load -i ./resultLoaded image: nix-redis-minimal:latest$ docker images | grep redisdebian-redis latest 8366943c77e8 3 days ago 136MBalpine-redis latest aae644cd3417 3 days ago 6.99MBnix-redis latest 30486183a209 50 years ago 45.4MBnix-redis-minimal latest a21238890680 50 years ago 2.02MB$ # Did we just produce a docker image _smaller_ than Alpine? Interesting!$ # Go tell your friends :)$ # This was a quick taste of what Nix can do.$ # I hope we made you eager to try it for yourself.$ # Happy Nixing!
  • Reproducible

    Nix builds packages in isolation from each other. This ensures that they are reproducible and don't have undeclared dependencies, so if a package works on one machine, it will also work on another.

  • Declarative

    Nix makes it trivial to share development and build environments for your projects, regardless of what programming languages and tools you’re using.

  • Reliable

    Nix ensures that installing or upgrading one package cannot break other packages. It allows you to roll back to previous versions, and ensures that no package is in an inconsistent state during an upgrade.

Examples

  • Try new tools without fear

    $ # Lets see if python is present on the system$ python --versionpython: command not found$ # Use nix-shell to create a shell environment with python$ nix-shell -p python3(nix-shell) $ python --versionPython 3.7.7(nix-shell) $ # YAAAY! Python is available(nix-shell) $ exit$ And this is how you create on demand environments
  • Multiple languages, one tool

    $ # Lets create an environment with multiple packages$ nix-shell -p python3 nodejs go rustc(nix-shell) $ node --versionv10.20.1(nix-shell) $ go versiongo version go1.14.1 linux/amd64(nix-shell) $ rustc --versionrustc 1.42.0(nix-shell) $ # Imagine how easy (nix-shell) $ exit$ # And POOF, just like that your are back to your normal environment after$ # playing around. No system was hurt during this time :)
  • Isolated development environments

    $ # You can also persist your development environment.$ # Here is a short example with python and nodejs:$ bat default.nix────────┬──────────────────────────────────────────────────────────────────── │ File: default.nix────────┼──────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} # here we import the nixpkgs package set 2 │ }: 3 │ pkgs.mkShell { # mkShell is a helper function 4 │ name="dev-environment"; # that requires a name 5 │ buildInputs = [ # and a list of packages 6 │ pkgs.python3 7 │ pkgs.python3Packages.virtualenv 8 │ pkgs.nodejs 9 │ pkgs.yarn 10 │ ]; 11 │ shellHook = '' # bash to run when you enter the shell 12 │ echo "Start developing..." 13 │ ''; 14 │ }────────┴────────────────────────────────────────────────────────────────────$ # Pause the video to read and understand the default.nix$ # To enter dev-environment simply run:$ nix-shellStart developing...(nix-shell) $ python --versionPython 3.7.7(nix-shell) $ virtualenv --version16.7.9(nix-shell) $ # With python and virtualenv you should be ready to start(nix-shell) $ # your python project(nix-shell) $ node --versionv10.20.1(nix-shell) $ yarn --version1.22.4(nix-shell) $ # Having node and yarn in PATH you already know you can(nix-shell) $ # do all the good stuff with nodejs (nix-shell) $ exit$ # How hard it is in your company to share the same version of required$ # tooling across different machines?
  • Minimal docker images

    $ # We all love docker. But it becomes hard with time to write $ # reliable Dockerfile.$ # What if you can use the power of Nix and build Docker images?$ bat docker.nix───────┬───────────────────────────────────────────────────────────────────── │ File: docker.nix───────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} # nixpkgs package set 2 │ }: 3 │ pkgs.dockerTools.buildLayeredImage { # helper to build docker image 4 │ name = "nix-hello"; # give docker image a name 5 │ tag = "latest"; # provide a tag 6 │ contents = [ pkgs.hello ]; # packages in docker image 7 │ }───────┴─────────────────────────────────────────────────────────────────────$ # Pause the video to read and understand the docker.nix$ # Now we build a docker image with nix-build$ nix-build default.nix -o result/nix/store/91ry9y0686xn9dgnn6rawfvknj8582ws-nix-hello.tar.gz$ # We can import the image into docker as usual$ docker load -i ./resulte25615ae850b: Loading layer 1.649MB/1.649MBbde5792b3b71: Loading layer 256kB/256kB1d9c7edd824b: Loading layer 31.63MB/31.63MBab8ee9b997a1: Loading layer 266.2kB/266.2kBf568d8025dd8: Loading layer 71.68kB/71.68kBLoaded image: nix-hello:latest$ # You can see that docker layers were automatically calculated.$ docker images | grep nix-hellonix-hello latest 83667617cdb9 50 years ago 32.9MB$ # And for the final thing lets test that it really works$ docker run -ti nix-hello:latest helloHello World!$ # There is a lot we didn't cover in this little demo, but we hope$ # it shows that declarative docker images are possible.
  • Declarative cloud images

    $ # How hard would it be to build and configure an Amazon EC2 image?$ # Let us configure a Nginx to serve a "Welcome to nginx!" page, with a $ # valid SSL certificate (via LetsEncrypt) and recommended security settings$ bat amazon.nix────────┬───────────────────────────────────────────────────────────────────── │ File: amazon.nix────────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs, ...}: 2 │ { 3 │ security.acme.acceptTerms = true; 4 │ security.acme.email = "nix@example.com"; 5 │ services.nginx = { 6 │ enable = true; 7 │ recommendedGzipSettings = true; 8 │ recommendedOptimisation = true; 9 │ recommendedProxySettings = true; 10 │ recommendedTlsSettings = true; 11 │ virtualHosts."example.com" = { 12 │ enableACME = true; 13 │ forceSSL = true; 14 │ locations."/".root = "${pkgs.nginx}/html"; 15 │ }; 16 │ }; 17 │ }────────┴────────────────────────────────────────────────────────────────────$ # Pause the video to understand the default.nix$ # Now we just need to build it.$ nix-build '<nixpkgs/nixos/release.nix>' \ -A amazonImage.x86_64-linux \ --arg configuration ./amazon.nix \ -o ./result...$ ls ./result/nixos-amazon-image-20.09pre130979.gfedcba-x86_64-linux.vhdnix-support$ # The resulting Virtual Hard Disk image (.vhd suffix) can be then be$ # imported to Amazon EC2 as usual.
  • Test your configurations

    $ # In this example we will look how to test your NixOS configuration$ # We will create a simple configuration with `hello` pacakge installed$ # system wide and check that `hello` world binary works.$ bat test.nix────────┬───────────────────────────────────────────────────────────────────── │ File: test.nix────────┼───────────────────────────────────────────────────────────────────── 1 │ { pkgs ? import <nixpkgs> {} 2 │ }: 3 │ pkgs.nixosTest ({ lib, pkgs, ...}: { 4 │ name = "example-test"; 5 │ # virtual machine with one package installed system wide 6 │ machine = { pkgs, ... }: { 7 │ environment.systemPackages = [ pkgs.hello ]; 8 │ }; 9 │ testScript = '' 10 │ # run hello on machine and check for output 11 │ machine.succeed('hello | grep "Hello, world!"') 12 │ # test is a simple python script 13 │ ''; 14 │ })────────┴────────────────────────────────────────────────────────────────────$ # Pause the video to understand the test.nix$ # To run the test you simply need to run:$ nix-build test.nix...machine1: must succeed: hello | grep "Hello, world!".../nix/store/99cb3pmmyaxp0rs6r596kqq5v8ivp45j-vm-test-run-example-test$ # While this example is simple you can imagine how handy can it be $ # to test interaction between many machines.$ # You can take screenshots, scrape for text and more. Checkout what$ # else you can do in NixOS manual under NixOS Tests section$ # Happy testing!