Using nix shells without polluting repositories

Hi! I felt the need to open this topic after getting the following error while quickly making a new test project:

opening library failed ( cannot open shared object file: No such file or directory)

I recently installed NixOS, and my workflow often requires making new GUI-based applications, among other arbitrary things that might require existing system libraries which I already have installed but not in my LD_LIBRARY_PATH as that’s against the Nix principles.

So my question is, what’s the best approach to allow me to easily make new projects without needing to add 2-4 files to my repo each time (flake.nix, direnv, the 2 other temp/lockfiles they both create)? Especially for simple projects that others will be looking at and working with too, I don’t want to pollute the repositories with extra configuration files that only I will ever use while others don’t even know what Nix is.

Like of course, here’s the most “correct” way of fixing my problem:

pkgs.mkShell {
  shellHook = ''
      with pkgs;
      lib.makeLibraryPath [ libGL xorg.libX11 xorg.libXi ]

But is there any way to either:

  • Apply that globally (without interfering with the other LD_LIBRARY_PATH paths already present)
  • Create easily re-usable shells that I can easily reference without adding them to my repository, and somehow making it work with vscode, preferably without needing to add a direnv still.

Overall, I’ve been loving the new workflows that NixOS has allowed me to make, e.g. for complex libraries such as custom LLVM builds, it’s so easy to just describe it in a nix flake and include it in my environment. But for simple more ubiquitous libraries, it’s a bit more of an issue. Would anyone have any suggestions on how to approach this?

1 Like

Create the files and then don’t commit them, ignore via .git/info/exclude, use path:$(pwd) as the flake ref.

Alternatively create it in a sibling folder and refer to that.


Fair enough yeah, I need to read up on flake templates to automate part of this process, the solution I found so far is:

  environment.sessionVariables = {
    LD_LIBRARY_PATH = with pkgs; [

I was under the assumption that environment variables must be strings, but I searched through nixpkgs and seems like some packages were using LD_LIBRARY_PATH as an array (e.g. opengl-driver), so I tried that and it worked.

My ideal goal here is to mix and match which parts of the Nix philosophy I want to use, and which parts of average linux shared environment stuff I want to use. Makes life a lot easier for me and other devs who look at my codebases.

But over time, I’ll try slowly integrating more nix shell stuff into my workflow, it’s just a bit hard to transition everything (especially considering the gitignore stuff, which makes it a bit tedious). Hopefully I’ll find a way to automate that too.

Ironically, going full nix should make things easier for such, definitely for people who don’t contribute back. Even if they contribute, a nix shell (but not flake checks or such) will help them.

Traditional desktop use means missing dependencies because you have no idea what’s already lying around from other projects. Having a shell.nix can’t be worse than that, and any reader remotely willing to just read will get a clear indication of what you’re actually using - package names are parseable even if nix is completely unreadable to you.

You’d have to be deliberately dragging your feet for a nix shell to cause problems - if you encounter such people, you can appease them by copying the package list to your readme, bonus points for writing some script that matches repology against fedora package names or something.

If you insist on using flakes, the alternate flake uri that starts with path: as @NobbZ suggests makes it so you don’t need to commit your flake.nix (comes at a cost of copying everything to the nix store, though, so careful with uncommitted secrets and big VM files and such). You can also fall back to a traditional shell.nix, which does not need to be committed to work.

At that point you can add an ignore for it to your global git config. For projects in which you want whichever file you put your shell in, you can then explicitly git add it.


Right, thank you for the response, though I just want to double-check something because I feel like my understanding isn’t that great with shell.nix vs flake.nix. I jumped straight on flakes for my system config because that seemed to be “the right future-proof thing to do”, and by extension, I assumed the same for dev shells. Also blog posts such as this one led me to believe flakes are the right way to go too.

if you encounter such people, you can appease them by copying the package list to your readme

I in my case, half the time “such people” are co-workers in private company repositories, usually ones that I don’t have ownership over myself, or I might have to hand ownership over to someone else at any time, hence me trying to minimize the “confusing” configuration as people appear to be fairly opposed to adding repository “bloat” that they’re not familiar with (it just works on their machine after all). And in most cases they’re right, for basic apps with little system deps.

But yeah, assuming I do add a nix shell to my repository, do you recommend sticking to shell.nix unless required otherwise, even if everything else I use is flakes?

Yes, that requires more bravery than on your private stuff :wink: Personally I used the path: “hack” with direnv so I didn’t need to keep typing it out in those situations. And then mentally ignored the flake.nix, because adding it to global git ignores annoyed me for some reason.

I don’t have a clear yes or no to this one. Flake shells come with the massive downside of copying everything to the nix store constantly, and 4 years later there has been no visible progress on resolving this.

Detsys are pushing flakes hard now, so maybe that will be resolved in the near future. But I was hoping for that two years ago, and as I see it flake stabilization is at least another two years out…

The main advantage is the input handling - channels are bloody confusing and trip up new users all the time. When you share your flakes, making sure others get exactly the same nixpkgs commits is useful, too.

Personally I use flakes because of that input handling and just accept the various annoyances.

If you don’t care for sharing your flake.nix though, I could see it argued that just learning how to pin your channels to your system flake inputs and using shell.nix is much more ergonomic.

That does require some semi-advanced system config though: Do flakes also set the system channel? - #16 by peterhoeg