# Flakify Yo NixOS So you've been running NixOS for a while. Despite all its warts, it's starting to feel comfortable, and you feel like you can finally find your way around the system. Need to add another service? Just search https://search.nixos.org/options for whatever you need. Developing some software? `nix-shell` is great for setting up a consistent environment, especially when paired with something like [niv](https://search.nixos.org/options?). You've sort-of figured out how to make a `default.nix` to build your project with. But you've heard about this new wunderkind, [Nix Flakes](https://nixos.wiki/wiki/Flakes), so let's throw all that out of the window and dive head first into this brave new, barely functioning, world. ## Wait, what is a flake? For those who don't know, flakes is an experimental new feature in the Nix package manager. It's been "upcoming" since 2019 and according to the [original RFC](https://github.com/NixOS/rfcs/pull/49) they > [...] allow hermetic, reproducible evaluation of multi-repository Nix > projects; impose a discoverable, standard structure on Nix projects; and > replace previous mechanisms such as Nix channels and the Nix search path. It's also still woefully underdocumented (like most of the Nix ecosystem), has many sharp edges and is in general extremely opaque to work with. With some of my complaints out of the way, it actually doesn't take much to turn your NixOS setup into a flake. Doing so will make your setup more reproducible, as it doesn't rely on the current state of your nix channels or anything like that. ## Enabling flakes on NixOS The first thing you'll need to do in order to be able to work with flakes is to add support for it in your `/etc/nixos/configuration.nix`. Modify your base derivation as follows: ```nix {pkgs, ...}: { ... nix = { package = pkgs.nixFlakes; extraOptions = '' experimental-features = nix-command flakes ''; }; ... } ``` After running `nixos-rebuild switch`, you should be able to use `nix flake` in your terminal. ## Version-control your NixOS configuration Next, you should move your NixOS configuration into a separate git-repository such that it can be version-controlled: ```console $ mkdir my-nixos-config $ cd my-nixos-config $ git init $ cp /etc/nixos/configuration.nix . $ cp /etc/nixos/hardware-configuration.nix . $ git add . && git commit -m "Initial commit" ``` ## Setting up our flake First we initialize the `flake.nix` file: ```console $ nix flake init ``` The resulting `flake.nix` should look like this: ```nix { description = "A very basic flake"; outputs = { self, nixpkgs }: { packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello; }; } ``` At its core, a flake consists of a description, a number of inputs and a number of outputs wrapped in a function. The file generated by `nix flake init` has an `outputs` that takes as arguments itself and a nixpkgs, and in this case specifies a new package, `hello` as well as a `defaultPackage` which is used by `nix build`. Now, in order to control which nixpkgs to use, i.e. if you want to use unstable, let's specify what nixpkgs we want to use. Add a new input to your flake like this: ```nix { description = "A very basic flake"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; ... } ``` [^1]: If you don't specify a nixpkgs, HEAD aka. the master branch from GitHub is used. Also, no matter whether you specify nixpkgs or not, the version used will be pinned as soon as you start building your flake. Since we're not using this flake to build a package, let's change the `outputs` to contain the NixOS configuration for our machine: ```nix { ... outputs = { self, nixpkgs }: { nixosConfigurations.myMachine = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ configuration.nix ]; }; }; } ``` Finally, we need to modify our `hardware-configuration.nix`: Because flakes must be pure, we cannot reference the current system nixpkgs in the import. For instance, my `hardware-configuration.nix` looked something like this: ```nix { config, lib, pkgs, ... }: { imports = [ ]; ... } ``` We need to modify the derivation to take a `modulesPath` as argument and use that to find the `not-detected` derivation: ```nix { config, lib, pkgs, modulesPath, ... }: { imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; } ``` ## Deploying the flake to your local system Hopefully, this has all been relatively painless. Of course, we haven't really run or built anything yet, so now comes the crucial moment: Let's try to deploy. Unfortunately, we cannot just use the regular `sudo nixos-rebuild switch` command that we're used to. Instead, we have to run `sudo nixos-rebuild switch --flake .#mySystem`. Here, the dot (`.`) in the final argument refers to the path of the flake you're trying to use, it could be any path that contains a flake.nix, and `mySystem` refers to the name of the system you wish to deploy[^2]. [^2]: If your system name contains dots, as in `nixosConfiguration."mysite.com" = ...`, you can use `sudo nixos-rebuild switch '.#"mysite.com".'`. Hopefully, everything will go smoothly. A `flake.lock` file will be generated, your system built and deployed, and you're now living in a world of reproducibility. Congratulations! ## Follow-ups Next time, I'll describe how to use `deploy-rs` to deploy to and manage remote hosts using your flake. I'll also write about secret management using `agenix` at some point. In the meantime, you can check out my [philnix repository](https://github.com/Munksgaard/philnix) for inspiration. ## Closing words Feel free to contact me if this was in any way helpful, if you think the guide could be improved or if you just want to talk. I'm also curious to hear if you think this format (markdown on gopher) makes sense. ## References: - [`nix flake` command reference](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#flake-references): Good reference for specifying inputs and the general format - [Flakes on the NixOS Wiki](https://nixos.wiki/wiki/Flakes): The output schema is out-dated, prefer the one in the `full` Flake template, see below. - [Nix Flake templates](https://github.com/NixOS/templates): Can be used by running, e.g. `nix flake init --template templates#full`. Also the `full` template is a useful reference. - [Xes](https://xeiaso.net/blog/nix-flakes-1-2022-02-21) [ongoing](https://xeiaso.net/blog/nix-flakes-2-2022-02-27) [https://xeiaso.net/blog/nix-flakes-3-2022-04-07](series) [on](https://xeiaso.net/blog/nix-flakes-4-wsl-2022-05-01) [flakes](https://xeiaso.net/blog/nix-flakes-look-up-package): Lots of useful information here. - [Practical Nix Flakes](https://serokell.io/blog/practical-nix-flakes): Good flake tutorial more focused on building packages. - [Nix Flakes, Part 1: An introduction and tutorial](https://www.tweag.io/blog/2020-05-25-flakes/): Out-dated tutorial by Eelco Dolstra, who originally propsed flakes.