Nix Flakes

A flake is a directory which directly contains a Nix file called flake.nix, that follows a very specific structure. Flakes introduce a URL-like syntax for specifying remote resources. To simplify the URL syntax, flakes use a registry of symbolic identifiers, allowing the direct specification of resources through syntax such as github:NixOS/nixpkgs. Flakes are also pinned (for reproducability) to versions via a flake.lock file.

Structure

Minimally, a flake file contains a description of the flake, a set of input dependencies and an output. You can generate a very basic flake file at any time using nix flake init. This will populate the current directory with a file called flake.nix that will contain something akin to:

# ❄︎ flake.nix
 
{
  description = "A very basic flake";
 
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  };
 
  outputs = { self, nixpkgs }: {
 
    packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
 
    packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
 
  };
}

In the example above, you can see the description, the input specified as a GitHub repository with a specific branch (here nixos/nixpkgs on the nixos-unstable branch), and an output that makes use of the input. The output simply specifies that the flake contains one package for the x86_64 architecture called hello. Even if your flake’s output wouldn’t use its input (however, in practice, that is highly unlikely), the output still needs to be a Nix function.

Schema

The flake.nix file is a Nix file but that has special restrictions (more on that later).

It has 4 top-level attributes:

  • description is a string describing the flake.
  • inputs is an attribute set of all the dependencies of the flake. The schema is described below.
  • outputs is a function of one argument that takes an attribute set of all the realized inputs, and outputs another attribute set whose schema is described below.
  • nixConfig is an attribute set of values which reflect the values given to nix.conf. This can extend the normal behavior of a user’s nix experience by adding flake-specific configuration, such as a binary cache.

Input schema

The inputs attribute defines the dependencies of the flake. For example, nixpkgs has to be defined as a dependency for a system flake in order for the system to build properly.

Nixpkgs can be defined using the following code:

inputs.nixpkgs.url = "github:NixOS/nixpkgs/<branch name>";

Nixpkgs can alternatively also point to an url cached by the NixOS organization:

inputs.nixpkgs.url = "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz";

In this example the input would point to the nixpkgs-unstable channel.

For any repository with its own flake.nix file, the website must also be defined. Nix knows where the nixpkgs repository is, so stating that it’s on GitHub is unnecessary.

For example, adding Hyprland as an input would look something like this:

inputs.hyprland.url = "github:hyprwm/Hyprland";

If you want to make Hyprland follow the nixpkgs input to avoid having multiple versions of nixpkgs, this can be done using the following code:

inputs.hyprland.inputs.nixpkgs.follows = "nixpkgs";

Using curly brackets({}), we can shorten all of this and put it in a table. The code will look something like this:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/<branch name>";
  hyprland = {
    url = "github:hyprwm/Hyprland";
    inputs.nixpkgs.follows = "nixpkgs";
  };
};

Output schema

Once the inputs are resolved, they’re passed to the function outputs along with with self, which is the directory of this flake in the store. outputs returns the outputs of the flake, according to the following schema.

Where:

  • <system> is something like “x86_64-linux”, “aarch64-linux”, “i686-linux”, “x86_64-darwin”
  • <name> is an attribute name like “hello”.
  • <flake> is a flake name like “nixpkgs”.
  • <store-path> is a /nix/store.. path
{ 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 = ""; };
}

You can also define additional arbitrary attributes, but these are the outputs that Nix knows about.

Enabling flakes

Enabling flakes temporarily

When using any nix command, add the following command-line options:

 --experimental-features 'nix-command flakes'

Enabling flakes permanently

Add the following to the NixOS configuration:

  nix.settings.experimental-features = [ "nix-command" "flakes" ];

Add the following to your home manager config:

  nix.settings.experimental-features = [ "nix-command" "flakes" ];

Nix standalone: Add the following to ~/.config/nix/nix.conf or /etc/nix/nix.conf:

experimental-features = nix-command flakes