Nix is fundamentally a source-based package manager, so it'll try to build packages from source. However, since Nix packages are reproducible, it'll check the official binary cache first to see if it has already been built on Nix's infrastructure and download from there it's there. That's why Nix acts like a binary-based package manager most of the time. However, if a package fails to build or does build but isn't available on the binary cache yet, it would inevitably fall back to source builds. You'd occasionally run into this if you use the unstable channel of Nixpkgs. You can check https://status.nixos.org/ to get a sense of how high the chances are of missing the binary cache. Basically the chances are high if the channel build is failing.
While source builds aren't completely avoidable, you can alleviate much of the pain with a combination of declarative package management and dependency locking. It's what long term Nix users most likely end up using:
* nix-darwin for managing system-wide configuration
* Home Manager for managing user-level configuration
* Nix Flakes for locking dependencies
Once you have a working config, the same config would run anywhere, anytime. You can then use GitHub Actions to automatically update dependencies and check if they all build before merging. Also as a last resort, you can always revert to a previous config if something goes wrong.
While source builds aren't completely avoidable, you can alleviate much of the pain with a combination of declarative package management and dependency locking. It's what long term Nix users most likely end up using:
* nix-darwin for managing system-wide configuration
* Home Manager for managing user-level configuration
* Nix Flakes for locking dependencies
Once you have a working config, the same config would run anywhere, anytime. You can then use GitHub Actions to automatically update dependencies and check if they all build before merging. Also as a last resort, you can always revert to a previous config if something goes wrong.