Skip to content

JustAlternate/nix-intro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

theme paginate marp
default
true
true

bg left:40% 60%

Introduction to Nix

Nix, the declarative approach

https://nixos.org/


The Problem

  • You upgrade a package in your system and find that others packages are broken because a shared dependency got upgraded aswell...
  • Your system is broken and there is no undo button...
  • You want to migrate a system installation and configuration to another machine but don't want to redo all the steps you did for installing it..
  • You installed a lot of dependencies during an installation but don't want to bother cleaning it all manually.
  • You spend a lot of time setting up environment for development.
  • You want to embrace real reproducibility.

<style scoped> section { font-size: 25px; } .gray { color: lightgray; } </style>

What can Nix offer

  • Reproducible development environments.
  • Easy installation of software over URLs.
  • Easy transfer of development environments between machines.
  • Declarative and reproducible specification of Linux machines.
  • Reproducible integration testing using virtual machines.
  • Avoidance of version conflicts with already installed software.
  • Installing software from source code.
  • Transparent build caching using binary caches.
  • Strong support for software auditability.
  • First-class cross compilation support.
  • Remote builds.
  • Remote deployments.
  • Atomic upgrades and rollbacks.

bg 60%


Nix is a package manager

  • A purely functional package manager.
  • Can be installed on any Linux systemd based system (Ubuntu, macOS, WSL2..)
  • Has 100 000+ packages.
  • Declarative.
  • Atomic.
  • Reproducible.

Nix easy installation

sh <(curl -L https://nixos.org/nix/install) --daemon

Once installed, one can temporarly install a package using:

nix-shell -p fastfetch

bg 75%


Nix store

/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1/

Contains all the build products including binaries, libraries, configurations files... Permit the installation of multiple packages with different versions and configurations. Are immutable, isolated and atomic


Declarative approach for development

my-python-app/app.py

#!/usr/bin/env python

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return {
        "message": "Hello, Nix!"
    }

def run():
    app.run(host="0.0.0.0", port=5000)

if __name__ == "__main__":
    run()

Declarative approach for development

my-python-app/shell.nix

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "python-environment";

  buildInputs = [
    pkgs.python311
    pkgs.python311Packages.flask
  ];

  shellHook = ''
    export FLASK_APP="app.py"
    echo "===================================="
    echo "Welcome to my-python-app environment"
    echo "usage: python3 app.py"
    echo "===================================="
  '';
}

Flakes

Experimental feature of Nix (but is vastly used by the majority of the community)

Allow for a standardized project structure.

Make it easier to write, share and deploy reproducible Nix expression.

Cache the produced evaluation for faster runtime

Pin versions of dependencies in a lock file.

bg right:40% 60%


Flake example for making a reproducible dev environment

flake.nix

{
  description = "A Python project with Nix Flakes";
  inputs = {
    nixpkgs.url = "nixpkgs/nixpkgs-24.05-darwin";
  };
  outputs = { self, nixpkgs }:
    let
      pkgs = nixpkgs.legacyPackages.aarch64-darwin;
    in
    {
      # Define a development shell for the project (callable with nix develop)
      devShells.${system}.default = pkgs.mkShell {
        buildInputs = with pkgs;
          [
            # Packages needed to dev in the project
            python311
            ...
            docker-compose
            # tools
            jq
            # Linter / formatters...
            black
            ruff
            # git shenanigans
            git
            pre-commit
          ];

        shellHook = ''
          echo "Start the postgres local db"
          docker compose up -d
          trap 'docker compose down' EXIT

          echo "Configure pre-commit to reduce circle-ci costs"
          pre-commit install --hook-type pre-commit
        '';
      };
    };
}

flake.lock

{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1724615852,
        "narHash": "sha256-CB8YqljFSCXwW51LKAZYIQNsKypppHfraotRSYXDU7Q=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "bb8bdb47b718645b2f198a6cf9dff98d967d0fd4",
        "type": "github"
      },
      "original": {
        "id": "nixpkgs",
        "ref": "nixpkgs-24.05-darwin",
        "type": "indirect"
      }
    },
    "root": {
      "inputs": {
        "nixpkgs": "nixpkgs"
      }
    }
  },
  "root": "root",
  "version": 7
}

Want unstable packages ? Yes sir !

{
  description = "A flake with two channels";

  inputs = {
    nixpkgs.url = "nixpkgs/nixpkgs-24.05-darwin";
    nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs, nixpkgs-unstable }:
  ...
    pkgs.firefox
    pkgs.unstable.chromium
  ...
}

Docker or Nix ?

Docker is repeatable, but not reproducible because it relies on sources that change over time (such as apt repositories)

One docker build can have different output depending when it is done.

Because of:

FROM ubuntu:latest
RUN apt update
RUN apt install nginx

Nix on the other hand is reproducible because it can pins down dependencies versions using our flake.lock

Or by overriding a package version:

{ pkgs ? import <nixpkgs> { } }:
let
  version = "1.20.2";
in
pkgs.mkShell {
  buildInputs = [
    (pkgs.nginx.overrideAttrs (oldAttrs: {
      inherit version;
      src = pkgs.fetchurl {
        url = "https://nginx.org/download/nginx-${version}.tar.gz";
        sha256 = "sha256-lYh2dXeCGQoWU+FNwm38e6Jj3jEOBMET4R6X0b70WkI=";
      };
    }))
  ];
}

Using Docker within Nix

FROM python:3.12-slim

WORKDIR /app

COPY . .

RUN pip install flask

EXPOSE 5000

ENV FLASK_APP=app.py

CMD ["python", "app.py"]

Using Docker within Nix

pkgs.dockerTools is a set of functions for creating and manipulating Docker images (note that Docker is not used behind the hood to perform these functions)

{ pkgs ? import <nixpkgs> { } }:
pkgs.dockerTools.buildLayeredImage {
  name = "flask-app";
  config = {
    Cmd = [
      (pkgs.lib.getExe (pkgs.python3.withPackages (ps: with ps; [ flask ])))
      ./app.py
    ];
    ExposedPorts = { "5000/tcp" = { }; };
  };
}
nix-build DockerInNix.nix
docker run < result

Using Nix within Docker

TODO


NixOS

NixOS is a immutable declarative linux distribution that focus on reproducibility using the Nix package manager


After installation, one can find the nixos configuration file at /etc/nixos/configuration.nix

{ pkgs, ... }:
{
  imports = [ ./hardware-configuration.nix ];

  boot.loader.systemd-boot.enable = true;

  networking.hostName = "JustAlternate-nixos-computer";
  networking.networkmanager.enable = true;

  time.timeZone = "Europe/Paris";

  console.keyMap = "fr";

  users.users.justalternate = {
    isNormalUser = true;
    extraGroups = [ "networkmanager" "wheel" ];
  };

  environment.systemPackages = with pkgs; [
    vim
    wget
  ];

  services.openssh.enable = true;
  system.stateVersion = "24.11";
}

We also have a hardware-configuration.nix file containing our hardware auto generated config.

{ config, lib, pkgs, modulesPath, ... }:
{
  boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ "kvm-amd" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/eba54cc4-e684-474c-a38b-d3033e5e657b";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/E329-BF6B";
      fsType = "vfat";
      options = [ "fmask=0077" "dmask=0077" ];
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/5f0732ca-9d43-432f-8bf9-bad19a61a596"; }
    ];

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

Now lets install application and services on our NixOS machine

...


Nix tools

<style scoped> section { font-size: 25px; } .blue { color: blue; } </style>
  • yarn2nix: Generate Nix expressions from a yarn.lock file
  • node2nix: Generate Nix expression from a package.json.
  • poetry2nix: Build Python packages directly from Poetry's poetry.lock. No conversion step needed.
  • compose2nix: Generate a NixOS config from a Docker Compose project (only for NixOS)
  • composer2nix: Generate Nix expressions to build composer packages.
  • sbtderivation: mkDerivation for sbt, similar to buildGoModule.
  • nixos-infect: Replace a running non-NixOS Linux host with NixOS.

Nix and Ansible ?

bg right:50% 50%

TODO


Nix and Kube ?

TODO


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published