Documentation Index
Fetch the complete documentation index at: https://flox.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
What Are Package Groups?
Package groups (pkg-group) are one of the mechanisms Flox provides to manage dependency conflicts. Each package in a group resolves against the same nixpkgs Git commit; different package groups may resolve against different nixpkgs commits. This safeguards against runtime ABI incompatibilities and version conflicts.
NoteIn Flox terminology, a
nixpkgs Git commit is equivalent to a catalog revision.
Flox maintains a non-destructive fork of nixpkgs,
sampling the upstream unstable branch nixos-unstable
at the same time daily.
The Flox base Catalog evaluates a subset of packages from that fork
and records metadata such as version, license, and source provenance.
A catalog revision is a snapshot of that subset of packages
at a specific nixpkgs commit.nixpkgs.
A Canonical Example
openssl and curl packages. Flox resolves both against the same nixpkgs commit, selecting v3.6.1 of openssl and v8.18.0 of curl.
If you instead require a specific version of one of these packages—say, v1.1.1q of openssl—but do not specify a version of curl, Flox resolves both packages to an older, compatible catalog revision. In this case, the environment pulls in a historical version of curl: v7.84.0, from June 2022, the same catalog revision that includes v1.1.1q of openssl, which was released the following month.
A problem arises if you require specific historical versions of both openssl and curl. In such cases, the Flox resolver cannot always compute a coherent dependency graph. For instance, defining openssl v1.1.1q (June 2022) and curl v8.18.0 (January 2026) in the same environment triggers this error:
openssl package in its own package group:
Mental Model: The Same Package Group = The Same nixpkgs Commit
Every dependency in a package group gets pinned to the same historicalnixpkgs commit. This means they were built and tested against the same set of packages. As a result, shared libraries (such as glibc, libstdc++, etc.) are the same across each group. This minimizes the risk of ABI incompatibilities.
How Package Groups Work
Default Group: toplevel
When you install a package with flox install, it goes into toplevel, the default package group. toplevel is an implicit package group: any package installed without defining a pkg-group field gets placed into it.
toplevel group and resolve against the same pinned nixpkgs commit. To place a package in a named package group, define the pkg-group key.
The three packages below are in the ml group and resolve against a historical nixpkgs commit:
bash, git, curl, and jq share the same nixpkgs commit (as members of toplevel), while python3, numpy, and scipy share another (as members of ml). These commits may be the same or different: i.e., if the ml packages are in fact satisfiable at the same nixpkgs commit as toplevel, then the resolver selects that commit for both groups. The Flox resolver selects different nixpkgs commits only when one or more packages in a group actually require this. For this reason, you can use package groups to organize a Flox environment’s manifest, e.g., grouping related packages together for legibility.
Resolution
When you runflox install, flox edit, flox push, flox pull, or flox activate, Flox attempts to resolve the environment and materialize its closure at $FLOX_ENV if it has not already done so.
During resolution, the Flox resolver performs the following actions for each package group:
- Collects constraints. Gathers every package descriptor in the group (
pkg-path, version constraints, system requirements). - Searches catalog revisions. Iterates through available
nixpkgsrevisions (newest first) to find a single revision where all packages in the group can be satisfied. - Applies per-package filters. Checks
pkg-pathmatch,versionconstraint,broken/unfreeflags, andsystemsconstraint. - Locks the result. Writes every package entry to
manifest.lockwith the samerevandlocked_url.
nixpkgs commit is satisfying for a group if every package in the group has a matching version at that commit, across all target systems.
Lock File Anatomy
The lock file (manifest.lock) records each resolved package with its group and nixpkgs commit. Below is an annotated excerpt showing two packages in different groups:
- Every package in the same group shares the same
revandlocked_url. - Each package has a separate entry per system (
x86_64-linux,aarch64-darwin, etc.), but all entries for the same group share the samerev. - The
priorityfield defaults to5. This is used to resolve file conflicts (lower value = higher priority).
pkg-group or priority constraints, Flox attempts to resolve all defined packages into a dependency graph whose members can coexist within a single closure. Given a large number of packages, the set of nixpkgs commits capable of satisfying all dependency constraints becomes correspondingly narrow. In such cases, Flox typically resolves historical versions of packages rather than current-stable ones. We demonstrated this with openssl and curl in A Canonical Example.
This sometimes happens when only a few packages are defined in an environment. For example, if one package updates slowly—with months or years elapsing between releases—the resolver may need to pull in older versions of more frequently updated dependencies to produce a viable graph.
When to Use Package Groups
Isolating Packages with Tight Version Constraints
When a package requires a specific version that conflicts with versions needed by other packages in your environment:Separating Optional Tooling from Core Dependencies
You can isolate dev tools in a separate package group so they don’t constrain your core stack:Cross-Platform Split
CUDA packages are only available on Linux. Placing them in their own group with asystems filter prevents resolution failures on macOS:
Resolving “Constraints Too Tight” Failures
When resolution fails with the error:Practical Examples
1. Splitting Dev Tools from Runtime
A web application where runtime packages must be version-coherent, but linters and formatters can float independently:2. ML Framework Isolation
A PyTorch inference serving project with several plugin groups, each pinned to its ownnixpkgs commit:
torch doesn’t force numpy or transformers to change.
3. Resolving File Conflicts with Priority and Groups
When two packages install files to the same path, use thepriority field to control which package wins; use package groups so that they resolve from different revisions:
priority field defaults to 5. Lower numbers win file conflicts. Here, gcc (priority 5) takes precedence over gcc-unwrapped (priority 6) for any overlapping files.
Package Groups and Builds
Only toplevel Packages Are Available During Flox Manifest Builds
When you use flox build with manifest builds, only packages in the toplevel group are available as build dependencies. Packages in named groups are not accessible in build commands:
toplevel group’s locked revision, ensuring the built package’s dependencies are compatible with the environment’s core packages.
If a package is needed at build time, it must be in toplevel.
Trimming Runtime Closures
By default, everytoplevel package becomes a runtime dependency of your build. Use runtime-packages to exclude build-only dependencies from the final closure:
runtime-packages list accepts install-id values (i.e., the key before .pkg-path in the [install] section’s TOML definition) from the toplevel group only.
Upgrade Semantics
Atomic Group Advancement
flox upgrade advances groups to newer catalog revisions. The key property: all packages in a group move together to the same new revision.
cuda to a newer revision doesn’t change toplevel or any other group. Individual packages can be targeted by install ID only if they are the sole member of their group. If a group has multiple packages, you must upgrade the entire group.
What Triggers a Change
An upgrade occurs when the resolver finds a newer catalog revision where the group’s packages have changed in version, build configuration, or dependency graph. If nothing has changed, the group stays at its current revision.Troubleshooting
”Constraints too tight”
Symptom: Resolution fails with:-
Loosen version constraints. Change
version = "2.133.0"toversion = "^2.133.0"to accept semver-compatible versions. -
Split into package groups. Isolate the conflicting package:
-
Verify the package exists. Use
flox search <pkg>andflox show <pkg>to confirm the version is in the catalog.
Non-toplevel Packages Missing During Builds
Symptom: A package you installed is not found when running flox build.
Cause: The package is in an explicit group, not toplevel.
Fix: Move it to toplevel by removing the pkg-group field, or add a duplicate entry in toplevel for the build:
Broken Packages Causing Silent Resolution Failures
Packages flagged as broken in nixpkgs are filtered out during resolution. If a package you expect to find isn’t resolving, it may be broken at thenixpkgs commit the resolver is considering. Bypass with this filter:
Appendix A: Concept Mapping Between Flox and Nix
If you’re familiar with Nix flakes, Flox package groups map directly to patterns you already know.Comparison Table
| Flox Concept | Nix Equivalent |
|---|---|
| Package group (single) | Pinned nixpkgs flake input at a specific rev |
| Multiple pkg-groups | Multiple nixpkgs inputs (nixpkgs, nixpkgs-stable) |
toplevel default group | Primary nixpkgs input in a flake |
Group upgrade (flox upgrade) | nix flake update nixpkgs (per-input) |
priority | meta.priority in nixpkgs |
| Lock file group entries | flake.lock input nodes |
| Resolution failure (“constraints too tight”) | No single nixpkgs rev satisfies all version pins |
outputs / outputs = "all" | Derivation multi-output (out, dev, lib) |
| Catalog (Flox-specific) | Nixpkgs revision history (no direct Nix equivalent) |
Side-by-Side: Flox Manifest vs. Nix Flake
Flox manifest: two package groups, resolved automatically:inputs, manually discovered and pinned:
What’s Different
Package groups in Flox automate what is otherwise a manual process in Nix:- Searching for compatible
nixpkgsrevisions. Flox’s resolver iterates through catalog revisions to find one that satisfies all constraints in a package group. In Nix, you can pin specificnixpkgsrevisions yourself, but you must discover, choose, and maintain those pins explicitly. - Resolution across target platforms. Flox resolves a package group against all declared target systems. In Nix, support is typically modeled per-system in flake outputs and checked via builds or tests.
- Atomic upgrades: The
flox upgradecommand advances a package group while keeping all packages in that group pinned to the same nixpkgs revision. In Nix,nix flake updateadvances pinned inputs inflake.lock; ultimately, however, compatibility is determined by the flake’s builds and checks.
Appendix B: How Otherwise Incompatible Packages Coexist in a Single Flox Environment
If packages in different groups are pinned to dissimilarnixpkgs revisions—and potentially link against different versions of core libraries—how can they coexist in the same environment without conflicts?
The answer has to do with two properties of Nix and one property of Flox.
1. The Nix Store: Hash-Based Isolation
Every package built by Nix lives at its own SHA-256-hashed path in the Nix store:<hash> is derived from all of the package’s declared build inputs: source code, compiler, flags, and every dependency. This means that two versions of the same library—say, openssl-1.1.1 and openssl-3.1.7—can coexist as two independent directories in /nix/store. They don’t overwrite each other; they don’t even know about one another. There is no global /usr/lib that forces a single version.
2. RUNPATH: No Global Library Search
On most Linux systems, binaries find shared libraries via global paths like/usr/lib or LD_LIBRARY_PATH. Nix avoids this entirely.
During linking, Nix’s ld-wrapper injects RUNPATH entries into each binary’s ELF header. These entries are absolute store paths that point to the specific versions of libraries the binary was built against:
LD_LIBRARY_PATH or /usr/lib fallback because every Nix-built binary knows precisely which version of which library to load at runtime. This means Package A can use openssl-3.0.15 while Package B can use openssl-1.1.1, in the same environment, at the same time, on the same host.
Note: Modern Nix uses RUNPATH (DT_RUNPATH), not RPATH (DT_RPATH). The practical difference: RUNPATH can be overridden by LD_LIBRARY_PATH, while RPATH cannot. In a Flox environment this likely won’t matter, but it is worth knowing if you’re debugging with ldd or LD_DEBUG.
3. The Flox Environment: A Merged Symlink Forest
When Flox builds an environment, it materializes a single derivation that produces a directory of symlinks pointing into the Nix store. All packages from all groups get merged into this forest:nixpkgs revision each package is built from. In the materialized environment, package groups don’t exist. Each package is simply one of many symlinks into the Nix store, irrespective of which group it belongs to.
File conflicts occur only when two packages expect to install a file to the same relative path (e.g., both provide bin/python). These are resolved by Flox’s priority key: lower numbers win. If two packages have equal priority and install different content to the same path, Flox reports a collision error.
Why Package Groups Matter
Nix makes coexistence mechanically possible; package groups make it simple, declarative, and correct. Within a group, all packages are pinned to the samenixpkgs Git revision, so their shared dependencies are guaranteed to compatible. In other words, If two packages depend on openssl, they both get the same openssl package: the same version, the same store path, the same ABI.
Across groups, dependencies can differ, and Nix handles this gracefully: each binary finds its own libraries via RUNPATH. But packages that interact at runtime should share dependencies. For example:
- Python extensions must link against the same
libpython. Ifnumpyandscipylink against differentlibpythonbuilds, importing both in the same interpreter will crash. Putting them in the same group guarantees they share the samelibpython. - C/C++ libraries that pass data structures between each other (e.g.,
libcurlcalling intoopenssl) must agree on struct layouts and ABI. Same group guarantees this.
nixpkgs revision that satisfies all packages in a constrained set—i.e., the package group. Fewer packages per group means a smaller constraint-solving search space.
This makes it faster and less costly to resolve a coherent dependency graph.