DEV Community

Cover image for Flakify your code as NixOS Packages and Modules
Abhishek Adhikari
Abhishek Adhikari

Posted on

Flakify your code as NixOS Packages and Modules

Introduction

Recently I had to flakify a Django app so that it outputs a few packages and modules which can be installed and configured in the NixOS System. I needed to figure out how to get started. After a week of searches and reviewing other nix packages, I made it work.

Today, I'll try to explain whatever I have learned in this process. I may be wrong somewhere or maybe there's a better way, so feel free to correct me or share your knowledge in the comments section.

Why Flakes?

When using normal Nix packages the derivation is fetched from the current nix-channel set in the system. So, we may sometimes arrive at a situation when we need to change the nix-channel for fetching a particular package. Now, several such packages from different channels quickly start building a mess. And making sure the nix-channels are in sync becomes hard. Flakes resolves this problem as we specify the nixpkgs version to use by providing its URL in the inputs.

In general, flakes make things a little more declarative and easy to understand and maintain. Also, the dependencies get pinned to a lock file, which makes sure that the flake output is consistently reproducible, the main moto of Nix.

What are we going to do today?

Today we will be writing a script that prints a passed argument along with the current time after every 30 seconds.

Setting up our project

First, let's initialize git.

git init
Enter fullscreen mode Exit fullscreen mode

Now we will set up a basic flake by running the command

nix flake init
Enter fullscreen mode Exit fullscreen mode

It will create a basic flake.nix file.

Flake.nix

Now let's create your bash script. First, we will create a folder called "src", the name can be anything but "src" clearly tells that it contains the source files.

Bash script

Now we will create the derivation for the package. I like to create a folder called "pkgs" and put the derivation there.

Derivation

Let us understand the derivation here.

pname = "nix-flake-demo";
Enter fullscreen mode Exit fullscreen mode

We are defining the package name here. The package will be available to the path by this name.

outputs = [ "out" ];
Enter fullscreen mode Exit fullscreen mode

We can have more outputs for different purposes, the out is a default one that links to the directory in the nix store for this derivation.

src = ../../src;
Enter fullscreen mode Exit fullscreen mode

The location of the source files.

buildInputs = [ ];
Enter fullscreen mode Exit fullscreen mode

We don't need anything else to build our derivation as this is a simple bash program. But for example, if we had a flask app, then we would have to pass python along with the flask package to the buildInputs.

installPhase = ''
    mkdir $out
    mkdir $out/bin
    cp -rv $src/* $out/bin
    chmod +x $out/bin/nix-flake-demo
'';
Enter fullscreen mode Exit fullscreen mode

We create our output directory and a bin directory inside it. Then we copy our source files there and make the script executable.

One thing to note here is that there should be an executable in the output bin directory of the name same as that of the derivation. If the script is named something else in the source just rename it in the installPhase.

There are several more Phases in mkDerivation. I am not mentioning the rest as I don't have much experience in them.

You can read more about the different phases here.

Now we can edit the flake to output the derivation as a package.

Adding package to flake

Let's understand the flake code till now.

inputs = {
  nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
  utils.url = "github:numtide/flake-utils";
};
Enter fullscreen mode Exit fullscreen mode

We are providing the URL of the nixpkgs to be used in this flake. Also, we are providing flake-utils.

packages =
      let
        system = "x86_64-linux";
        pkgs = import nixpkgs { inherit system; };
      in
      utils.lib.eachDefaultSystemMap (system: {
        nix-flake-demo = pkgs.callPackage ./pkgs/nix-flake-demo { };
      });
Enter fullscreen mode Exit fullscreen mode

We are outputting nix-flake-demo for all types of systems.

defaultPackage.x86_64-linux = self.packages.x86_64-linux.nix-flake-demo;
Enter fullscreen mode Exit fullscreen mode

Then we specify the defaultPackage of this flake. So when we run the command nix run, it will run this package.

Let's test our flake till now. Don't forget to commit the changes first. Then run the command

nix run
Enter fullscreen mode Exit fullscreen mode

Nix run test

The script prints only the time as no argument was passed to it.

Nix run with arguments

Now let's create the module which provides a systemd service to NixOS. I like to keep the code for that in the "modules" directory.

NixOS Module

We provide a few options that can be configured in NixOS configuration. And if the enable option is set to true, we add the service to the system.
If you don't know how to add a flake to your NixOS configuration, let me know, and I will try to create a post on that.
I have a boilerplate to set up nix flake. Please visit this repo:

nix-pkg-module-boilerplate

A fork and star will be highly appreciated.

Thanks for reading till the end. Hope I was able to teach something new. Please provide your feedback or request topics in the comment section.

With ❀️ by Abhishek Adhikari.

Top comments (3)

Collapse
 
siph profile image
Chris Dawkins

defaultPackage is deprecated. It has moved to packages.<system>.default.
github.com/NixOS/nix/issues/5532

Collapse
 
xpertr2 profile image
Abhishek Adhikari

Thanks for reminding me. Will update the post soon.

Collapse
 
kushagar profile image
Kushagar sharma

Really Helpful, keep up the good work brother