Blog
Steve Swoyer | 26 Jun 2025

Articles about DevOps tend to focus on an outsized abstraction: the DevOps "pipeline." The usual move is to frame this pipeline as something consistent and uniform, rather than a loose collection of steps, environments, and processes.
Something similar happens with software packaging. The premise of packaging is that it gives you portability and reproducibility; however, without the right safeguards in place, a software package is arguably just a wrapper that embodies the local conditions of wherever it was built.
In both cases, we mistake an abstraction for something airtight—something that doesn’t leak.
So pipelines and packages are abstractions, but they're also distractions. They overshadow the core challenge involved in producing software: reproducibility. What builds and runs in one context (like CI), doesn't always behave the same in staging, let alone production. What works when engineers build locally doesn't always work when pushed to CI. And what works on each software engineer's machine doesn't always work when they go to share it with their team. So "works on my machine" becomes both a metaphor and a mantra for software builds at scale.
Many teams already use Flox to address some of these issues. They rely on Flox to create, share, and ship reproducible dev environments that Just Work anywhere. They depend on Flox for access to the world's largest collection of open source software.
As of today, they can also use Flox to build, package, and install their own software, too. Flox gives teams one tool they can use across the whole of the SDLC.
Flox Build & Publish Explained
Flox is no longer "just" a package and environment manager.
Starting with version 1.5.0, you can use it as an integrated build system. With the new flox build command, you can be certain your software will always build in a reproducible way–inside a completely sandboxed environment. You can even build and package artifacts across multiple platforms (macOS, Linux, and Windows with WSL2) and architectures (x86-64, ARM). When you build in a sandbox, not only are your builds always reproducible, but your packages encapsulate the environment in which they were built, so they, too, always run reproducibly across platforms.
Even better, you can use flox publish to package and publish not just your build artifacts but all required runtime dependencies to your private Flox Catalog. In addition to binaries and libraries, you can package up shell scripts, config files, example data, and virtually any other artifact. This gives you a complete, self-contained runtime. You can either flox install your packages imperatively or define them declaratively in the Flox manifest. Either way, your software and all required dependencies are available for you to use—anywhere.
To sum up:
-
flox build – The build subcommand builds and packages software in a reproducible, optionally sandboxed, environment, with support for building and packaging across operating system platforms and CPU architectures.
-
flox publish – The publish sub-command performs a complete build before packaging and uploading build artifacts, along with all runtime dependencies, to your private Flox Catalog. Once published, you can flox search, flox show and flox install your packages.
Building Software with Flox: A Canonical Example
You define your build instructions in a new section in the Flox manifest called [build].
Each entry in the [build] section specifies a build target—typically an artifact such as one or more binaries, libraries, scripts, config file(s)—and defines how to build it from source.
Each build definition includes:
- A name, like [build.myproject], which identifies the artifact;
- A short build script containing the same commands you would normally run manually;
- (Optional) Metadata fields, like description, version,
- (Optional) A sandbox, defined by sandbox = pure to isolate the build from the host;
- (Optional) A list of runtime-packages, to keep down the size of the resulting artifact's dependency tree.
For example, the following build definition runs cowsay in a sandboxed environment, writes its output to a text file, and stores that file in the build output directory.
Let's briefly explore this manifest's most distinctive features:
- We define version and description metadata keys for the build;
- The runtime-packages key is empty; the "package" itself is a textfile; there's no runtime.
- The sandbox = "pure" line isolates the build from the host system. This promotes build determinism and reproducibility, and also surfaces undeclared dependencies at build time.
All output goes to $out, which is one of the most interesting features of flox build.
Let's spend a few minutes exploring that. Spoiler: You can put a lot more than just build artifacts in $out.
The magic of $out
Think of $out as the complete, self-contained output of your build, organized according to the Filesystem Hierarchy Standard (FHS). It can include binaries ($out/bin/), libraries ($out/lib/), configs ($out/etc/), documentation or examples ($out/share/), and any other runtime artifacts your software needs.
For example, after building cow-greeting and before publishing, the resultant contents of $out look like:
Even though our cow-greeting build is intentionally minimal, it's easy to imagine extending it to follow standard Unix packaging conventions. For example, if we were to add typical FHS directories to our build command with simple cp operations, the resulting $out structure would mirror what you'd expect from a well-behaved Unix package:
In other words, $out isn't just for binaries or libraries—it's your package's own isolated, /usr/-like prefix, containing everything it brings to an activated runtime environment. So if your package has runtime requirements like example data, language files, schema definitions, or templates, you can put them in share/. For a package called continuum-transfunctioner, its post-build $out might look like:
- $out/share/continuum-transfunctioner: package-specific resources;
- $out/share/locale: translation files;
- $out/share/examples: sample input, usage demos, etc.;
- $out/share/templates: reusable templates used at runtime;
- $out/share/schema: for format descriptions like JSON Schema, OpenAPI, or Protobuf.
If you publish this package to the Flox Catalog, install it to a Flox environment, and activate that environment, Flox materializes the package as part of a unified runtime closure that includes both package- and system-level dependencies. Under the hood, so to speak, this runtime closure is implemented as a symlink forest, such that the contents of bin/, lib/, share/, etc., are symlinked to corresponding Nix store paths like /nix/store/<hash>-package-version.
For example, the materialized runtime for a popular real-world package, htop (see the C build example, below), looks like this:
The store paths themselves vary based on the package, its version, and the hash derived from its build inputs, but the overall pattern is foundational to how Flox and Nix deliver runtime portability and reproducibility.
Publishing Software with Flox
Once you've built your project, publishing it to your private catalog is simple. You just run:
Before you publish, however, you'll need to take care of a few basics:
-
The package must be defined in the [build] section of manifest.toml;
-
The Flox environment must be inside a Git repo and have at least one package defined in its manifest;
-
The Git working tree must be clean, with no uncommitted changes to tracked files;
-
The current commit must be pushed to a Git remote. If you have just one remote, Flox uses it. With multiple remotes, Flox looks only for upstream first, then origin, and disregards any others.
-
All files referenced during the build must be tracked by Git.
The Git constraints exist in order to lock the published package to a controlled input state. This keeps it from inheriting ambient state from the local build environment. It's a way of creating the conditions for reproducible builds–as well as for traceability and auditability over time.
The built package's payload includes essential metadata, like its name, version, dependencies, and its source Git URL and revision. During the build, Flox locks and tracks every input to the build——the contents of $out plus every package listed under runtime-packages (and what those packages themselves depend on) in your [build] definition. This is its "runtime closure." When you run flox publish, the entire closure gets uploaded to your catalog. And when you flox install your package, this pulls in the closure–not just your software, but any store paths (i.e., dependencies) that aren't already in your Nix store.
Once you've successfully built and tested your package, you're ready to publish it, like so:
This triggers a validation build, at the end of which you'll see a message indicating that the derivation you've built is being uploaded to your catalog. Once this is complete, you'll see a status message like this:
Just running flox publish is sufficient when your manifest defines a single package and you're publishing to your default private catalog.
If you’ve defined multiple packages or want to publish to a different catalog, Flox Publish gives you several options:
- flox publish -o <username>: publish a single package (when only one is defined) to a specific destination
- flox publish -o <username> <package>: publish a specific package to a specific destination
- flox publish <package>: publish a specific package to your default destination
For example, if your [build] section includes targets like [build.cow-greeting], [build.cow-greeting-tests], and [build.cow-greeting-lint], you’ll want to publish just the main package—not the test or lint targets:
You can install your package imperatively, using the flox install` command, or define it in your manifest:
Your packages and their constraints include your FloxHub handle and package name, and are also enclosed in quotes (" and "); otherwise, they're defined just like other packages you'd get from the Flox Catalog.
In fact, you can search for your package using its name:
Or show information about available package versions:
There's one other difference to take note of, however. When you flox install a package from the Flox Catalog, it's almost always available for both Linux and macOS, running on either Intel or ARM.
But with Flox Build, you must build and publish your package for each platform you want it to run on. So building and publishing your cow-greeting package on an x86-64 Linux system shows the following message:
Building and publishing your cow-greeting package on an ARM-based MacBook afterwards would show this:
Plus, the flox show barstoolbluz/cow-greeting command now lists both platforms:
And running flox edit shows your cow-greeting package constrained to two target platforms in your manifest:
That's it. That's Flox Publish in a nutshell.
Build-time and Runtime Reproducibility You Can Take with You
Building and shipping software isn't as straightforward as it could be. As for knowing exactly what you built, how you built it, where it came from, what it needs to run … while being able to track all of this as it evolves?
That sometimes feels impossible. Flox's new Build and Publish feature is the missing piece. With Flox, your local dev environment is always the same as your CI environment. And both are always the same as your software's build and runtime environments. You use the same environment across the entirety of the SDLC.
You can now build your own software, publish it to the private software repo your organization uses for internal packages, and then—to close the loop—install that software back into your local dev, CI, runtime, and build environments. Flox gives you one tool for all of your language ecosystems. Best of all, you can keep the workflows you already use: you run your existing build commands, push the same repo, publish to your private catalog, and then flox install your packages straight back into dev, CI, and production.
Best of all, the Flox environment is itself a declarative definition: it's a TOML manifest describing everything your software needs to build and run anywhere. The packages you flox publish to your private catalog take that manifest with them, so every installable artifact doubles as an immutable record of what was built, how, when, and with which inputs. You get portable proof of provenance you can install and run anywhere.
Want to learn more? Why not start by reading all about Flox Build and Publish, or discover how Flox is different from, and a complement to, containers. Or just take the plunge: Download Flox, create your first package, build it, publish it–and install it anywhere!
To help you get started, let's dig into build examples using real-world software, spanning several popular languages and toolchains.
Build Examples: C
You'll detect a theme with this and the walk-throughs that follow: We'll be building interactive terminal UI (TUI) system monitors. First up is [htop](https://github.com/htop-dev/htop)**, a mature tool written in C. Note: In this and the sections that follow, the first mention of a language links to its Flox Build cookbook entry.
To build htop from source using flox build, you'd clone its repo, initialize a new Flox environment, then create this manifest:
There are a couple of useful things to note about the [build] definition in this manifest::
- We define platform-specific dependencies (compilers and libraries) in the [install] section;
- We build with autoconf on both Linux and MacOS, using each platform's default compiler;
- We use conditional logic (if [[ "$(uname)" == "Darwin" ]]; then) to patch the project source code on macOS. This addresses a common type of platform-specific or version-specific issue;
- We're again doing a sandbox = "pure" build, so we need to define more build-time dependencies than with a basic build. Sandbox builds are reproducible; basic builds are repeatable: inside the sandbox, only the dependencies defined in the manifest are available.
Once you flox activate and run flox build, you'll see output like this:
You can run flox publish to publish to your private FloxHub catalog.
Build Examples: C++
The C++ build example is btop, which might be the coolest htop alternative going. This build example uses cmake, but you can build for Linux and macOS using make, too.
To build btop from source, clone its repo and initialize a new Flox environment. Then paste the TOML below into your manifest:
Once you activate the environment and run flox build, you'll see a message like this:
You can run flox publish to publish to your private FloxHub catalog.
Build Examples: Rust
Rust offers several htop-like alternatives, the most popular of which is probably bottom, or btm.
The TOML below defines not just a build command ([build.bottom]), but lint ([build.lint]) and unit tests ([build.unit-tests]) too. If you prefer, you could incorporate these into a single build command.
Paste the TOML above into your Flox manifest, run flox build, and you're off to the races. Just running flox build will queue up all three commands, showing final output like this:
(Note: The ellipses, …, indicate redacted output messages between each build command.)
Because your build also includes definitions for [build.lint] and [build.unit-tests], you must name the bottom package explicitly when you go to publish it:
Build Examples: Go
There's an htop alternative called gotop for Go, but ctop seems like an even better alternative. It's a terminal ui (TUI) tool for monitoring OCI containers: basically, htop for Docker. The TOML below defines a build plan for ctop, complete with coverage tests:
A successful build shows output like this:
Next just run flox publish to push your package to your private catalog.
Build Examples: Python
Although there's an htop-like tool called pytop, the obvious choice for Python is glances, which by most measures is a more informative tool.
The manifest below defines everything needed to build both glances and its web server, which provides a TUI-style browser interface. The glances web server requires a few extra packages to work on MacOS:
Once your build finishes you'll see a message like this:
Then just run flox publish to push your package to your private catalog.
Build Examples: Java
There's nothing quite like htop in the Java world. But there is visualvm, which provides comparable visibility into the inner workings of Java and the JVM.
The manifest below defines everything you need to build virtualvm for Linux, MacOS, or even Windows:
Once your build finishes, you'll see a message like this:
Again, just run flox publish to push your package to your private catalog.
Build Examples: JavaScript / Node.js
The final build example is vtop, a quite pretty htop alternative written mostly in JavaScript.
The build should finish quickly. Then you'll see a message like:
One last time, run flox publish to push your package to your private catalog.
.png)

