NixOS modules and configurations offer us a tantilizing way to express and share systems. My friends and I can publish our own flakes containing nixosModules
and/or nixosConfigurations
outputs which can be imported, reused, and remixed. When it comes to secret projects though, that openness and ease of sharing can be a bit of a problem.
Let's pretend my friend wants to share a secret NixOS module or package with me, they've already given me access to the GitHub repository, and now I need to add it to my flake. My flake is public and has downstream users. I can't just up and add it as an input. For one thing, it'd break everything downstream. More importantly, my friend asked me not to.
It's terribly inconvienent to add the project as an input and be careful to never commit that change to the repository. Worse, if I did screw up and commit it, my friend might be disappointed in me. We simply can't have that.
Let's explore how to create a flake which extends some existing flake, a pattern which can be combined with a git+ssh
flake URL to resolve this precarious situation.
This same situation strategy can be applied to other outputs of a flake, and can be combined with Configurable Nix Packages.
Extending a Flake
Let's assume for a moment we have a simple flake called original
with a nixosConfiguration
and a nixosModule
:
# original/flake.nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11";
};
outputs = { self, nixpkgs }: {
nixosModules.default = { config, pkgs, lib, ... }: {
# Create a file `/etc/original-marker`
environment.etc.original-marker.text = "Hello!";
# You can ignore these, just keeping things small
boot.initrd.includeDefaultModules = false;
documentation.man.enable = false;
boot.loader.grub.enable = false;
fileSystems."/".device = "/dev/null";
system.stateVersion = "22.11";
};
nixosConfigurations.teapot = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.default
];
};
};
}
While our little demo configuration, teapot
, might not be a very practical system for hardware (or even a VM) it's a perfectly valid NixOS expression which we can build and inspect the resultant output filesystem of:
Now, let's assume there exists some other flake, called extension
that looks like this:
# extension/flake.nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11";
};
outputs = { self, nixpkgs }: {
nixosModules.default = { config, pkgs, lib, ... }: {
# Create a file `/etc/extension-marker`
environment.etc.extension-marker.text = "Hi!";
};
};
}
Our goal is to create a teapot
with the extension.nixosModules.default
module included.
To do that, we'll create a new flake, called extended
which looks like this:
# extended/flake.nix
{
inputs = {
original.url = "/home/ana/git/extension-demo/original";
nixpkgs.follows = "original/nixpkgs";
extension = {
url = "/home/ana/git/extension-demo/extension";
inputs.nixpkgs.follows = "original/nixpkgs";
};
};
outputs = { self, nixpkgs, original, extension }:
original.outputs // {
nixosConfigurations.teapot =
original.nixosConfigurations.teapot.extendModules {
modules = [
extension.nixosModules.default
];
};
};
}
Because of outputs = { self, nixpkgs, original, extension }: original.outputs // { /* ... */ }
this extended
flake has the same outputs of original
, plus whatever overrides we add inside { /* ... */ }
.
path:/home/ana/git/extension-demo/extended?lastModified=1680125924&narHash=sha256-EV4jQJ5H3mypuOt4H174lII2yhnaUbZ9rbML2mjyRlI=
We call extendModules
on the original nixosConfiguration.teapot
to extend the configuration with new modules, in this case, our extension.nixosModules.default
.
Now we can inspect the resultant output filesystem:
Using private GitHub inputs in flakes
While the github:username/repository
flake paths utilize Github's API and work well for public repositories, you may experience issues trying to use it with a private repository.
In these cases, try using the git+ssh
protocol. For example:
{
inputs = {
original.url = "/home/ana/git/extension-demo/original";
nixpkgs.follows = "original/nixpkgs";
extension = {
url = "git+ssh://git@github.com/hoverbear/top-secret-project.git";
inputs.nixpkgs.follows = "original/nixpkgs";
};
};
outputs = { self, nixpkgs, original, extension }:
original.outputs // {
nixosConfigurations.teapot =
original.nixosConfigurations.teapot.extendModules {
modules = [
extension.nixosModules.default
];
};
};
}
Running nix flake update
should then use your SSH key and work, as long as you have the ability to clone that repository over SSH.
This article was suggested by my employer, @grahamc at Determinate Systems.