Adding a package and service to NixOS using flakes
One of the advantages of nix flakes is the ability to quite arbitrarily compose flakes in different ways to obtain consistent, reproducible environments comprising disparate applications and tools.
However, using flakes to add new packages and modules to NixOS is not very well documented. Here, I describe how I added a simple package and service to my NixOS configuration with a flake. More specifically, I describe how to add a simple go application which implements a REST API and associated systemd
service to a NixOS configuration - the idea was to focus specifically on a package which is not available within nixpkgs
and address the issue of how to add it via a flake.
It is worth noting that there is a post here by Eelco Dolstra which covers this to some extent, but focuses more on the issue of how to add functionality in a given flake ( hydra in this case) to a NixOS configuration; it does not discuss how to create the flake containing the new package and module in the first place.
The go application is in this repo and the flake git repo which I created is here.
Basic concepts
Before going further it’s worth recapping on some basic NixOS concepts — skip this if this you’re very familiar with these concepts.
A basic concept of NixOS is the notion of a package — this is typically an executable that can be run by the operating system and has some set of build instructions in a derivation. (In general, a nix package can contain multiple executables). In NixOS, you can specify which packages are installed on your system by specifying the packages in environment.systemPackages
and the full set of available packages can be searched here. It's worth noting that you can install a service which uses a package but it might not be available from the command line - such a package would be installed in the nix store but would there would be no link in/run/current-system/sw/bin
to this package meaning that it cannot be found in the PATH
(but it is of course referenced when the service is run).
As well as packages, NixOS has the notion of modules. These are generally used to support services which are managed by the system — modules tend to focus more on configuration than packages. Modules have excellent systemd
support, for example, with many modules providing systemd
services.
In this case, then, it is necessary to create a package from the git repository and create a module which uses the package — it’s also desirable to enable the package to be added the set of available packages in the NixOS configuration such that it can, for example, be added to environment.systemPackages
.
Creating a flake repository
A flake repository should have a flake.nix
file at the top level which will be the entry point for evaluating the flake; typically, it also contains a description of the flake. In this example, the flake contains the following:
- an
overlay
which is the mechanism used in nix to augment an existing set (such as a set of packages) with additional package(s). - a
package
set which is provided by the flake - a
nixosModule
set which contains the modules provided by the flake
Adding the package
Following the example provided in the hydra
flake, I included apackage.nix
file in the top level of the repository which contains the derivation for the package. In this case, there is only a single package in the derivation, called simple-go-server
; the derivation fetches a repository from github and builds it with the buildGoModule
builder.
It’s worth noting that the name of the resulting binary is simple-rest-api
which is specified by the (golang) module system (see here).
If you only need to include a package, you can remove the nixosModule
attribute from flake.nix
and you will have a solution in which the package defined in the flake will be available in your NixOS configuration. You can of course change the contents of package.nix
to include a derivation for any package. Also, you can easily add multiple packages to the flake definition by, for example, specifying multiple derivations and including them in flake.nix
using callPackage
.
Adding the module
To add modules to NixOS, the modules need to be defined as nixosModules
within the flake. In this case, a single nixos module is defined with two options: an option to enable the service and an (unused) option to specify a port for the service.
The implementation section of the module definition specifies a systemd
service which can be enabled. The implementation defines a dedicated group and user for the service as well as the systemd
unit definition which launches the service, calling the simple-rest-api
binary from the simple-go-server
package; it could also refer to any other configuration parameters (eg the port in this case) when running the service.
Adding the flake to the NixOS configuration
Assuming that NixOS is configured using flakes, add the flake to your main flake.nix
for configuring NixOS.
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
simple-go-server.url = "github:seanrmurphy/simple-go-server-flake";
}
To add the simple-go-server package to the set of packages, use the following in your flake.nix
(adapted to your system....):
nixosConfigurations."nixos" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
simple-go-server.nixosModules.simple-go-server
({ pkgs, ... }: {
nixpkgs.overlays = [
simple-go-server.overlays.default
];
})
];
};
Then the package and the module can be used in the NixOS configuration:
environment.systemPackages = with pkgs; [
...
simple-go-server
...
];
services.simple-go-server.enable = true;
Working with the new flake
While working on this, it was necessary to push updates to the flake in the github repo and test; I needed to pull down the latest version but didn’t want to update the nixpkgs
flake each time. It was possible to update just the simple-go-server flake using
$ nix flake lock --input-path simple-go-server
where simple-go-server
is the name of the entity in the inputs section of the configuration.
It was also interesting to use this command to see what is defined within the flake.
$ nix flake show github:seanrmurphy/simple-go-server-flake --no-write-lock-file
<...snip...>
github:seanrmurphy/simple-go-server-flake/4db2749d0d65d271206bea3bc809b7440c8f73dd
├───nixosModules
│ ├───overlayNixpkgsForThisInstance: NixOS module
│ └───simple-go-server: NixOS module
├───overlays
│ └───default: Nixpkgs overlay
└───packages
├───aarch64-linux
│ ├───default omitted (use '--all-systems' to show)
│ └───simple-go-server omitted (use '--all-systems' to show)
└───x86_64-linux
├───default: package 'simple-go-webserver-0.0.0'
└───simple-go-server: package 'simple-go-webserver-0.0.0'
Final comments
Nix flakes provide a quite usable mechanism to add packages and modules to NixOS; however, some aspects are a little non-obvious including the overlay mechanism, the way packages need to be defined and the use of nixosModules
. Further, while this solution works fine for this context (adding a single small package which is not included in nixpkgs
), it's not clear if/how it could work if it was necessary to install many packages from arbitrary flakes. Flakehub is a good initiative to aggregate flakes to enable them to be searchable but it's just one part of the solution if flakes are to be the standard way of managing software in NixOS and there is still more work to be done within the nix ecosystem to realize the full potential of this mechanism.
Originally published at https://www.gopaddy.ch on September 6, 2024.