How to manage nixos and nixpkgs using flakes

While I admit I’m rather new to nix, I have not been able to find any clear information on this.

Using channels I know you just do
… nixpkgs
… nixos

And when you rebuild and/or upgrade it knows where to pull from (although I’ve had this act weird as well)

But I can’t find how to, for example, use the nixos-unstable branch for nixpkgs only, and the 24.11 branch for nixos using flakes.

Vimjoyer does a great job explaining how they work; his content helped me greatly when I started looking at them a year ago:

Many other NixOS users have fantastic videos about how to get started with flakes, too, for instance:

The past LinuxFest event has some great NixOS content too:

Personally, IIRC this was the talk that made me rethink my whole existence as a GNU/Linux user:

All praise to these great folks :raised_hands:

There’s a lot to unpack there so take your time, start small, and iterate → repeat :slight_smile:

I’ve watched a few of these videos, I will watch the rest, thanks!

I think I might have a core misunderstanding in how the OS itself is updated.

I just read a bit deeper into the --upgrade flag and it says it updates the channel of the root user before rebuilding.

I wasn’t having any issues really until I tried to use the nixos-unstable-small branch to get some packages that were updated there but not on the main unstable branch, which led to lots of weird behavior such as nothing being updated at all.

Maybe I should, for simplicity sake, just leave my nixos channel as 24.11 and use flakes for packages, although I’m still curious how I could manage both from flakes

Stop using --upgrade with flakes. You can also delete your channels.

Very simple.

Yes I understand this much, but how can I update the os separate from the packages, and vice versa.

For instance if I was using nixos-unstable channel in flakes, but I want to use the 24.11 channel for the OS itself, how do I accomplish that.

Or maybe easier to understand, what if I install nixos 24.11, make a flake for my packages using nixos-unstable, and then want to change my OS to nixos-23.11.

You don’t, because 23.11 doesn’t get updates.

I don’t understand your question, this is my best guess at what you’re looking for.
I also would not recommend this, though. Just pick either stable or unstable and stick with it, don’t mix.

So the TLDR is if I want to use the unstable branch for packages, I should also use it for the OS?

I guess that makes sense as the packages in each branch are tested against the rest of that branch.

I thought I could get more granular, like using unstable for OS related things (gnome 48 for example) while using stable branch for packages ( or vice versa using 24.11 branch for OS but unstable for newer packages, like code-cursor which is only up to date on unstable currently)

I guess for the latter I could keep the channel as 24.11 and have the flake use the unstable?

Oh yeah, you can if you do it properly.

I’ve a packages.nix module where, alongside declaring the packages I want to pull from stable and nixpkgs-unstable, I define this variable pkgsUnstable:

  pkgsUnstable = import inputs.nixpkgs-unstable {
    inherit system;
    config = {
      allowUnfree = true;
    };
  };

That allows me to define groups of attribute sets for the packages I want to install from either OR both channels, this way:

  packagesBaseline =
    with pkgs;
    [
      # Nix - Security
      vulnix # NixOS vulnerability scanner :: https://github.com/nix-community/vulnix

      # Networking
      httping # Ping with HTTP requests :: https://vanheusden.com/httping

      # Python
      python312 # High-level dynamically-typed programming language :: https://www.python.org
      python312Packages.ipython # IPython: Productive Interactive Computing :: https://ipython.org/

      # Storage
      nfstrace # NFS and CIFS tracing/monitoring/capturing/analyzing tool :: NFS and CIFS tracing/monitoring/capturing/analyzing tool
    ]
    ++ (with pkgsUnstable; [
      # Misc
      at
      bat
      broot # Interactive tree view, a fuzzy search, a balanced BFS descent and customizable commands :: https://dystroy.org/broot/
      chezmoi
...
...
]);

And do:

    environment.systemPackages =
      [ ] # Start with an empty list or your base packages
      ++ lib.optionals cfg.baseline packagesBaseline
      ++ lib.optionals cfg.cli._all (builtins.concatLists (builtins.attrValues packagesCli))
      ++ lib.optionals cfg.cli.ai packagesCli.ai
      ++ lib.optionals cfg.cli.backup packagesCli.backup
      ++ lib.optionals cfg.cli.cloudNativeTools packagesCli.cloudNativeTools
      ++ lib.optionals cfg.cli.comms packagesCli.comms
      ++ lib.optionals cfg.cli.databases packagesCli.databases
      ++ lib.optionals cfg.cli.misc packagesCli.misc # TODO_ properly categorize the packages
      ++ lib.optionals cfg.cli.multimedia packagesCli.multimedia
      ++ lib.optionals cfg.cli.programming packagesCli.programming
      ++ lib.optionals cfg.cli.secrets packagesCli.secrets
      ++ lib.optionals cfg.cli.security packagesCli.security
      ++ lib.optionals cfg.cli.vcs packagesCli.vcs
      ++ lib.optionals cfg.cli.web packagesCli.web
      ++ lib.optionals cfg.gui packagesGui
      ++ lib.optionals cfg.guiShell.kde packagesGuiShell.kde
      ++ lib.optionals cfg.nvidia packagesNvidia
      ++ config.mySystem.myOptions.packages.modulePackages; # Add packages contributed by other modules

To assemble the final list of packages to install from both channels, I created several options that allow me to easily toggle what set of packages I want to install on each different host–each host has a profile where you can choose not only what packages to install, but also configure everything else.

There’s a bunch of community flakes I rely on too, so properly defining both channels in the flake, like this:

 inputs = {
    nixpkgs.url = "nixpkgs/nixos-24.11"; # NixOS release channel
    nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; # The unstable release channel to allow for fresher packages
}

It’s super important to configure them, e.g.:

    # Snapd support for NixOS
    nix-snapd = {
      inputs.nixpkgs.follows = "nixpkgs-unstable";
      url = "github:nix-community/nix-snapd";
    };

    # A Neovim configuration system for Nix
    nixvim = {
      inputs.nixpkgs.follows = "nixpkgs";
      url = "github:nix-community/nixvim/nixos-24.11";
    };

Notice how the nix-snapd flake input follows nixpkgs-unstable and nixvim follows the stable channel.

HTH

I would not mix flakes and channels, you’ll end up confusing yourself even more when you go to update your system.
And again, I would strongly not recommend that you try to mix flake inputs. But if you want to, I linked instructions on how to do so.