Using current system's pkgs for an ad-hoc dev environment

I have a following shell.nix environment, pinned to a specific version of nixpkgs:

{ pkgs ? import (builtins.fetchTarball "") { } }:

with pkgs; mkShell {
  ROCKSDB_LIB_DIR = "${rocksdb}/lib";

I use it with nix-shell.

Although I use flakes for the overall system, I intentionally don’t use nix shell or nix develop, as that requires adding flake.nix to a git repo or ugly work-arounds. I also don’t have any nix-channels, as I want my flake.lock in my system’s config to be the source of truth.

Now, while I like that shell.nix is pinned to a specific version, I want to experiment with passing the system’s pkgs to it. Given the pkgs ? stanza, and the fact that nix-shell supports --arg arg it seems that

$ nix-shell --arg pkgs $CURRENT_SYSTEM_FLAKE_PKGS

should be in theory possible.

However, I am completely mystified as to what syntax I actually need here. Does anyone know? :slight_smile:

Or maybe it’s an XY problem?

I know you said that youre against channels but you can still let nix manage a nixpath. You can use the option nix.nixPath to do this and add something like

nix.nixPath = [ "nixpkgs=${pkgs.path}" ]; # or inputs.nixpkgs.outPath

Then just import it as usual with import <nixpkgs> {}. Also this way you can easily set up multiple paths. For example one for nixpkgs-unstable, flake-utils-plus. Remember that its just a path so anything really can be there


Delightful, this is exactly what I was looking for, thanks!

I disagree with this approach, as it precludes pure composition. I think @matklad got it exactly right.

@matklad You have to rephrase what you’re doing slightly if you want to pass the URL instead of the entire expression.

{ nixpkgs ?  "<hash>.tar.gz" }:
let pkgs = import (builtins.fetchTarball nixpkgs) { }; in
with pkgs; mkShell {
  # ...

and use the more convenient

nix-shell --argstr nixpkgs<hash>.tar.gz

to pass in the argument.

I would strongly discourage using channels or the <.> notation except for temporary hacks, or where having impurity is decidedly needed for some artistry.

As you said, it depends on your use case.

When I initially answered, I didnt realize that you wanted keep it pinned while having the ability to test with they system version.

To add to what fricklerhandwerk said, for convenience, you can use environment.sessionVariables to easily access the nixpkgs source as follows

 = "${pkgs.path}"; # or inputs.nixpkgs.outPath

And then do something like

# horribly overengineered
{ nixpkgs-src ? "" # your pinned version
, nixpkgs ? builtins.fetchTarball nixpkgs-src
, pkgs ? import nixpkgs { }

and then call it with

nix-shell --args nixpkgs $CURRENT_SYSTEM_FLAKE_PKGS

Edit: Also, since according to the manual for nix-shell, it passes arg to nix-instantiate. So, you can just use nix-shell --args nixpkgs ‘’ instead of using a session variable. It should be the same I think

Just to clarify, @hqurve’s suggestion is exactly what I was looking for. It might be bad for my health, but I does make my life more convenient!

I don’t need to “pin or use system’s nixpkgs”, I want to pick one solution and stick to it. Pinning works, but I don’t like the fact that I have some very old packages as a result, and that I have to upgrade things in multiple places. So I am now trying using a single version of nixpkgs which is pinned by the overall system’s flake.nix.

1 Like

Yet another (dirty) version: I’m using flakes for pinning, but I also have a git-ignored shell.nix like below, where I can replace pkgs with project flake’s inputs if needed:

  pkgs = (builtins.getFlake "/etc/nixos").outputs.nixosConfigurations.cs-338._module.args.pkgs;
  outputs = import ./. { inherit pkgs; };

Really curious what workflows other people arrive at

(but aside, I think it’s not unreasonable to pin both nix_path and flake-registry in configuration.nix)