6.6 KiB
Flakes
Flakes are still an experimental feature in Nix. However, they are so widely used by the community that they almost became standard. Furthermore, synix uses Flakes.
Nix flakes are a reproducible way to define, build, and deploy Nix projects, making them reliable and portable.
Flakes accomplish that by:
Standardized Input
They define a fixed, declarative input (the flake.nix file) that specifies all project dependencies, sources, and outputs. This eliminates implicit dependencies or environment variables that could cause builds to differ.
Example in flake.nix:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; # Declare we need nixpkgs, specifically this branch
};
Reproducible "Lock File"
When you build or develop with a flake, Nix generates a flake.lock file. This file records the exact content-addressable hashes of all transitive inputs used for that specific build. This lock file can be committed to version control, ensuring that anyone else cloning the repository (or a CI system) will use precisely the same set of inputs and thus achieve the identical result.
Example flake.lock entry for nixpkgs:
"nixpkgs": {
"locked": {
"lastModified": 1709259160,
"narHash": "sha256-...",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b2f67f0b5d1a8e1b3c9f2d1e0f0e0c0b0a090807", // The exact commit!
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github",
"url": "github:NixOS/nixpkgs/nixos-23.11"
}
}
Flake Schema
The flake.nix has a well-defined structure for inputs (sources like Git repos, other flakes) and outputs (packages, applications, modules, etc.). This consistent schema makes flakes composable and predictable.
A flake.nix file typically looks like this:
# flake.nix
{
description = "A simple example flake";
inputs = {
# Inputs are other flakes or external resources
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; # Locked to a specific branch/version
# This is how you would add synix to your flake:
# synix.url = "git+https://git.sid.ovh/sid/synix"
};
outputs = { self, nixpkgs, ... }@inputs: # 'self' refers to this flake, inputs are available
let
# Define common arguments for packages from nixpkgs
# This ensures all packages use the same version of Nixpkgs on this system
pkgs = import nixpkgs {
system = "x86_64-linux"; # The target system architecture
};
in
{
# Outputs include packages, devShells, modules, etc.
# Packages that can be built by `nix build .#<package-name>`
packages.x86_64-linux.my-app = pkgs.callPackage ./pkgs/my-app { };
packages.x86_64-linux.my-other-app = pkgs.hello; # From nixpkgs directly
# Development shells that can be entered using `nix develop`
devShells.x86_64-linux.default = pkgs.mkShell {
name = "my-dev-env";
buildInputs = [ pkgs.nodejs pkgs.python3 ];
shellHook = "echo 'Welcome to my dev environment!'";
};
# NixOS modules (for system config)
# nixosConfigurations.<hostname>.modules = [ ./nixos-modules/webserver.nix ];
# (This is more advanced and will be covered in NixOS section)
};
}
Key parts of a flake.nix:
description: A human-readable description of your flake.inputs: Defines all dependencies of your flake. Each input has aurlpointing to another flake (e.g., a GitHub repository, a local path, or a Git URL) and an optionalfollowsattribute to link inputs.outputs: A function that takesself(this flake) and allinputsas arguments. It returns an attribute set defining what this flake provides. Common outputs arepackages,devShells,nixosConfigurations, etc., usually segregated by system architecture. You can read more about flake outputs in the NixOS & Flakes Book.
nix flake Commands
The nix flake subcommand is your primary interface for interacting with flakes. Let's create a new flake to demonstrate them:
Initialize the flake:
mkdir my-flake && cd my-flake
nix flake init
This creates a minimal flake.nix.
Lock your flake:
nix flake lock
This creates flake.lock, a file that locks the exact versions of your inputs.
Update flake inputs:
nix flake update
This updates all inputs to their latest versions allowed by their url (e.g., the latest commit on nixos-unstable for nixpkgs) and then updates the flake.lock file. Since we just locked the flake for the first time, there probably won't be any updates available.
Print flake inputs:
nix flake metadata
Print flake outputs:
nix flake show
Build packages from a flake:
nix build .#hello # The '.' refers to the current directory's flake
./result/bin/hello
Run a package from a flake:
nix run .#hello
Since the packages.<system>.default output exists, you can just do nix run.
nix develop
This command spins up a temporary shell environment with all the tools and dependencies specified in your flake's devShells output.
Let's expand your flake.nix:
# flake.nix
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs =
{ self, nixpkgs }:
let
# Define `pkgs` for the current system
pkgs = import nixpkgs {
system = "x86_64-linux";
};
in
{
packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
# With `pkgs` defined, we could also do this:
# packages.x86_64-linux.hello = pkgs.hello;
packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
devShells.x86_64-linux.default = pkgs.mkShell {
# Packages available in the shell
packages = [
pkgs.git
pkgs.go
pkgs.neovim
];
# Environment variables for the shell
GIT_COMMITTER_EMAIL = "your-email@example.com";
# Commands to run when entering the shell
shellHook = ''
echo "Entering development shell for my project."
echo "You have Git, Go, and Neovim available."
'';
};
};
}
Now, from your project directory:
nix develop
You'll instantly find yourself in a shell where git, go, and nvim are available, and your GIT_COMMITTER_EMAIL is set. When you exit, your regular shell environment is restored – no lingering installations or modified global state. This makes it incredibly easy to switch between projects, each with its specific toolchain and dependencies, without conflicts.