new article and nonsense nix something something
This commit is contained in:
183
posts/cool-nix-shit.norg
Normal file
183
posts/cool-nix-shit.norg
Normal file
@@ -0,0 +1,183 @@
|
||||
@document.meta
|
||||
title: Cool shit you can do with nix
|
||||
description: Nix is surprisingly versatile
|
||||
authors: [
|
||||
Adumh00man
|
||||
]
|
||||
categories: [
|
||||
nix
|
||||
linux
|
||||
blog
|
||||
]
|
||||
created: 2026-03-28T19:03:27+00:00
|
||||
updated: 2026-03-28T19:59:57+0100
|
||||
draft: false
|
||||
layout: post
|
||||
version: 1.1.1
|
||||
@end
|
||||
|
||||
* Nix is hard
|
||||
Nix is supposed to be a solve-all to the "it runs on my machine" problem. And it is. If it didn't do that, then there would be no point in
|
||||
it existing. But, the syntax is a pain in the ass, and the documentation is half baked at best. For reference, everything I talk about
|
||||
here is coming from my {https://git.voidarc.co.uk/voidarc/nixos}[nixos config repo], so if you need some context, then everything in
|
||||
there should be functional enough, at least to serve as some kind of guide.
|
||||
|
||||
+html.class note
|
||||
> I am not a professional. If you want to get your info from someone that knows what they're talking about, look on youtube, idiot.
|
||||
|
||||
** Starting out
|
||||
This whole guide is coming from the perspective of someone who has never used nix outside of nixos. I decided to switch one day and had
|
||||
to learn as I went ({:/posts/nix:}[/If only there was a post about that/]). Of course, at the beginning, my config was very simple. I
|
||||
basically tried to replicate my arch config, which I already managed the dotfiles for through git. I managed, but my main issue was that
|
||||
my nixos config had to be edited by the root user, because it was in the `/etc/nixos` directory. This is the default location, which makes
|
||||
sense, because most linux systems need to be oriented toward having multiple users, and you don't want every person on the system to be
|
||||
able to change the underlying programs. However, this is irritating, because you have to manage a git repo as the root user, which *sucks*.
|
||||
There are some workarounds, mostly involving symlinks, that, if broken, will brick your system, so they aren't recommended. The real way
|
||||
you're supposed to do it is with flakes.
|
||||
|
||||
*** Flakes
|
||||
Flakes aren't real. They can't hurt you. But, they can let you manage your config outside of the `/etc` directory.
|
||||
|
||||
@code nix
|
||||
{
|
||||
description = "My example flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs }: {
|
||||
packages.x86_64-linux = {
|
||||
default = self.packages.x86_64-linux.hello;
|
||||
hello = nixpkgs.legacyPackages.x86_64-linux.hello;
|
||||
};
|
||||
};
|
||||
}
|
||||
@end
|
||||
|
||||
This is the default flake that you get upon running `nix flake init` in a project. What you'll immediately notice is that there are only
|
||||
2 sections: inputs and outputs (there are more but idk how to use them, except the description). These are not as explanatory as you
|
||||
might first expect, so I tend to think about it like this:
|
||||
|
||||
- Inputs
|
||||
-- External links / projects
|
||||
-- Immutable (for the most part)
|
||||
- Outputs
|
||||
-- Generated / built code
|
||||
-- Changable
|
||||
-- Where you would put anything of interest
|
||||
|
||||
I am aware this clears up nothing, so let me explain slightly further.
|
||||
|
||||
**** Inputs
|
||||
Without inputs, you basically can't build anything outside of a hello world with no dependancies. They are the main reason that flakes
|
||||
were made in the first place. This is because every input is stored in the lock file as a hash of the git repo it is a part of. I went
|
||||
more in depth in the article I linked to at the start, but for now I'll give you some examples.
|
||||
|
||||
The main input that you would need to
|
||||
know about is the `nixpkgs` input, which provides a fixed version of the nixpkgs git repo that you can use to access whatever packages
|
||||
you need. This can be unstable, which, as the name suggests, is newer and less supported, a fixed hash, like
|
||||
`nixpkgs.url = "github:nixos/nixpkgs?ref=f5da6d7f24b8565882487ce7f45c2a7d9d8afdeb";`, or just a branch, like the one in my config:
|
||||
`nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";`. All have different use cases, which I may or may not get to.
|
||||
|
||||
Another common form of input is a repo input. Nixpkgs uses this kind, so it follows the same rules.
|
||||
@code nix
|
||||
omnisearch = {
|
||||
url = "git+https://git.voidarc.co.uk/voidarc/omnisearch";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
@end
|
||||
The url is slightly different because the repo isn't hosted on github, but the `github:` from before is just a shortcut that expands
|
||||
into `git+https://github.com/` when you build the flake. Notice that there is another line in the input. This tells nix to use the
|
||||
version of nixpkgs that the whole system is running on, the one in this repo's flake.lock, instead of the one in the called flake's
|
||||
repo. This saves storage and prevents you from installing needless copies of apps that are basically the same. Using this syntax is
|
||||
best practice unless there is a specific version of a dependancy that the app needs.
|
||||
|
||||
The last kind of input you're probably going to see is the local input. These come in the form of local paths, relative to the current flake.
|
||||
These are used when you have submodules that aren't large enough to deserve a whole repo, or modules that you need to reload on the fly.
|
||||
@code nix
|
||||
chataigne.url = "./modules/chataigne";
|
||||
nvim-wrapped = {
|
||||
url = "git+file:///home/user01/.dotfiles/.config/nvim";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
@end
|
||||
The first input here is just a relative path, with a specific set of dependancies, hence it doesn't follow nixpkgs like the other.
|
||||
The second input is still a local module, but it isn't relative. The flake is located in a path above the system flake, that I want
|
||||
to call it from, so I needed to use a workaround and use the repo input type with a `file://` prefix. This lets me test my nvim config
|
||||
without pushing to git beforehand, which is useful when I've only added one dependancy and don't want to make a whole commit for 3
|
||||
lines of code. (don't worry, we'll get back to why it's called nvim-wrapped later)
|
||||
|
||||
**** Outputs
|
||||
Outputs, as I said, are what's produced when the flake is run. There are different types of these, too, so let's start simple.
|
||||
|
||||
Before you can even think about compiling an app, you have to allow the outputs access to the inputs. This can be seen as the
|
||||
function call at the beginning of the output section:
|
||||
@code nix
|
||||
outputs =
|
||||
{ self, nixpkgs }:
|
||||
{
|
||||
# Whatever app
|
||||
};
|
||||
@end
|
||||
However, this only allows the outputs to access nixpkgs, which is ok for the first type of output: The humble binary.
|
||||
|
||||
Binaries are defined by system. This allows you to have different build instructions for x64 and arm systems in the same flake, but with
|
||||
modern programming languages that isn't much of an issue. As an example, this flake output would provide fastfetch
|
||||
@code nix
|
||||
packages.${system}.default = pkgs.fastfetch;
|
||||
@end
|
||||
Simple, right? the `nixpkgs` input provides `pkgs` as a callable parameter, so that would be all you need. This is on display with
|
||||
the example flake from earlier, providing the `hello` package for x86_64-linux.
|
||||
This can be added to, by providing a devshell with this package, although that requires slightly more boilerplate.
|
||||
@code nix
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
# Packages you want available in your shell
|
||||
buildInputs = [
|
||||
pkgs.fastfetch
|
||||
];
|
||||
|
||||
# Environmental variables or shell hooks that you want to run
|
||||
# when the shell starts
|
||||
shellHook = ''
|
||||
export SOME_VAR=foo
|
||||
echo "Shell started"
|
||||
'';
|
||||
};
|
||||
@end
|
||||
Of course, a whole shell for fastfetch is a solved issue, with `nix shell` and everything, but for explanatory purposes that's all you
|
||||
would need to read flakes, and understand what you would need to call in order to get the right package from a repo. There are obviously
|
||||
building tools, but I have no idea how those work. This is supposed to be from a fully nixos user perspective, and I don't tend to write
|
||||
too much code.
|
||||
|
||||
Finally, there's the main thing that you would need to manage your system, `pkgs.lib.nixosSystem`. Of course, you could use the
|
||||
function directly, but that's boring, so I made a function to make a system instead:
|
||||
@code nix
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
...
|
||||
}@inputs:
|
||||
let
|
||||
mkSystem =
|
||||
extraModules:
|
||||
nixpkgs.lib.nixosSystem {
|
||||
inherit system;
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
common
|
||||
hardwareConfig
|
||||
]
|
||||
++ extraModules;
|
||||
};
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
mobile02 = mkSystem [ ./configs/configuration-laptop.nix ];
|
||||
};
|
||||
@end
|
||||
There are a few things to note here. This is from my system flake, I've just omitted my pc config, because you only really need one to
|
||||
see what's going on. Starting from the top, there is an extra `@inputs` after the output function. This allows for the `inherit inputs`
|
||||
inside the mkSystem function, so that you can dynamically access any input from whatever module you're calling for your system.
|
||||
|
||||
Reference in New Issue
Block a user